欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【Java基础】用于加密序列化的泛型

程序员文章站 2022-06-28 17:13:27
设计了一个泛型类用于管理并加密序列化敏感类对象, 对于待加密对象无需单独手动实现readObject和writeObject方法. 加密算法上直接套用了AES算法, 通过截取对象输出流明文并加密后重新输出的方式实现了安全序列化, 通过解密输入流并重新反序列化的方式实现了顺利读取对象....

0.需求描述

之前的文章中, 我们在需要加密储存(传输)的类内部实现加密算法, 采用AES直接对数据进行加密, 但对于一般的可序列化类型而言, 总是手动重写readObject(ObjectInputStream in)writeObject(ObjectOutputStream out)是繁琐且不切实际的, 这里我们将序列化的编码过程和对编码进行加密的过程解耦, 通过编写一个泛型类来管理需要加密序列化的类.

1.设计思路

需要定义一个泛型SafeSerializer<T extends Serializable>来管理可序列化的类, 该类的对象应当至少具有一个’密码’属性, 方法方面至少应该提供’添加可序列化对象’, ‘读对象’, '写对象’三种方法, 最终我们设计了如下的类型

类定义: SafeSerializer<T extends Serializable>

属性 描述
private final String account 账户名, 本例中无实际意义
private final String password 密码, 用于生成AES秘钥
private T obj 需要序列化的对象, 其中T是实现了Serializable接口的类
方法 描述
public SafeSerializer(String account, String password) 构造器, 需在构造时指定账户密码
public void setObject(T obj) 用于设置待序列化对象
public void safeWrite(final OutputStream os) 将加密序列化后的对象写入输出流
public T safeRead(final InputStream is) 从输入流的密文中反序列化出对象

在编写这个工具类之前, 我们需要对该类的使用进行预期, 即构思好如何使用该类, 在上述的设计中, 写入(序列化)和读取(反序列化)对象的过程应该大致如下:

/*
'TestClass' is a pre-defined class
'test' is an instance of TestClass
'os' is an instance of a class which extends OutputStream
'is' is an instance of a class which extends InputStream.
*/

//serialize
SafeSerializer<TestClass> sso = new SafeSerializer<>("account","P4S5VV0Rd");
sso.setObeject(test);
sso.safeWrite(os);
//deserialize
SafeSerializer<TestClass> ssi = new SafeSerializer<>("account","P4S5VV0Rd");
ssi.setObeject(test);
TestClass test=ssi.safeRead(os);

2.代码实现

import sun.misc.BASE64Encoder;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;

public class SafeSerializer<T extends Serializable> {
    private final String account;
    private final String password;
    private T obj;
    public SafeSerializer(String account, String password){
        this.account=account; this.password=password;
    }

    public void setObject(T obj){this.obj=obj;}

    public void safeWrite(final OutputStream os) throws IOException, SecurityException, GeneralSecurityException{
        /*将对象写入字节流 并获取明文字节数组plain*/
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(obj);
        String str=bos.toString(String.valueOf(StandardCharsets.ISO_8859_1));
        byte[] plaintxt=str.getBytes(StandardCharsets.ISO_8859_1);
        /*制作秘钥 进行AES加密器初始化 得到cipher*/
        KeyGenerator keygen= KeyGenerator.getInstance("AES");
        keygen.init(128, new SecureRandom((password).getBytes()));
        SecretKey original_key=keygen.generateKey();
        byte[] raw=original_key.getEncoded();
        SecretKey key=new SecretKeySpec(raw, "AES");
        Cipher cipher=Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        /*计算密文字节 使用编码器重编码 并安全输出*/
        byte[] ciphertxt=cipher.doFinal(plaintxt);
        BASE64Encoder encoder=new BASE64Encoder();
        String str_cipher=encoder.encodeBuffer(ciphertxt);
        os.write(ciphertxt);
        os.flush();
    }
    public T safeRead(final InputStream is) throws IOException, SecurityException, GeneralSecurityException, ClassNotFoundException {
        /*读入密文字节数组*/
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        try{
            byte[] buf = new byte[1024];
            int length = 0;
            while ((length = is.read(buf)) != -1) {
                bos.write(buf, 0, length);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e1) { }
            }
        }
        byte[] ciphertxt=bos.toByteArray();
        /*初始化秘钥*/
        KeyGenerator keygen=KeyGenerator.getInstance("AES");
        keygen.init(128, new SecureRandom((password).getBytes()));
        SecretKey original_key=keygen.generateKey();
        byte[] raw=original_key.getEncoded();
        SecretKey key=new SecretKeySpec(raw, "AES");
        Cipher cipher=Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, key);
        /*反序列化*/
        byte[] plaintxt=cipher.doFinal(ciphertxt);
        String str_plain=new String(plaintxt,StandardCharsets.ISO_8859_1);
        ByteArrayInputStream bis=new ByteArrayInputStream(str_plain.getBytes(StandardCharsets.ISO_8859_1));
        ObjectInputStream safeOis=new ObjectInputStream(bis);
        obj=null;
        try {
            obj = (T) safeOis.readObject();
            return obj;
        }catch(ClassCastException e){
            System.out.println("读取失败");
            return null;
        }
    }
}

注意在上述实现中, 我们使用了sun下的BASE64Encoder类, 这是为了解决部分语言(包括中文)下的乱码问题.

本文地址:https://blog.csdn.net/weisuowangshuai/article/details/109258096