Java序列化敏感数据应优先在writeObject/readObject中字段级加解密,而非整体加密;需随机IV、AES/GCM或CBC加密、密钥动态获取,避免硬编码。

Java 中对序列化后的对象数据进行加密存储,核心思路不是“先序列化再整体加密”,而是**在序列化过程中控制敏感字段的写入方式**,即利用 writeObject 和 readObject 的自定义机制,在字段级别完成加解密。这种方式更安全、更可控,也避免了对整个字节流加密带来的反序列化兼容性问题。
用 private writeObject 控制敏感字段加密
Java 序列化机制会自动查找并调用类中声明的 private void writeObject(ObjectOutputStream) 方法(签名必须严格匹配)。一旦存在,JVM 就跳过默认反射写入逻辑,转而执行你写的代码。
- 把非敏感字段交给
out.defaultWriteObject()处理 - 对 password、token 等敏感字段,先调用加密方法(如 AES/CBC + 随机 IV),再把密文(含 IV)写入流
- 推荐将 IV 与密文一起写入,比如先
out.write(iv),再out.write(encryptedBytes),或统一用out.writeInt(iv.length)+out.write(iv)+out.writeInt(cipher.length)+out.write(cipher) - 若敏感字段已声明为
transient,则它不会被defaultWriteObject写入,必须手动加密后写入——这是常见且推荐的配合方式
配套实现 private readObject 完成解密还原
加密和解密必须对称。只要重写了 writeObject,就必须提供对应签名的 private void readObject(ObjectInputStream),否则反序列化时会抛出 InvalidClassException 或字段为空。
- 先调用
in.defaultReadObject()恢复非敏感字段 - 按与
writeObject相同顺序读取 IV 和密文字节数组(例如先in.read(iv),再in.read(cipher)) - 用相同密钥和 IV 调用解密方法,将结果赋值给对应字段(如
this.password = decrypt(cipher, iv)) - 注意:密钥不可硬编码,建议从配置中心或密钥管理服务(KMS)动态获取
选对加密方式和参数很关键
序列化场景下不适用 RSA 等非对称算法,应优先选用轻量、快速、标准的对称加密方案。
立即学习“Java免费学习笔记(深入)”;
- 算法模式推荐 AES/GCM:兼顾加密与完整性校验,避免单独加 HMAC;次选 AES/CBC + PKCS5Padding
- IV 必须每次加密随机生成(用
SecureRandom),且随密文一同序列化,不可复用 - 密文可选择 Base64 编码后写入(便于日志调试),但会增大约 33% 体积;生产环境建议直接写原始字节
- 密钥长度至少 128 位,推荐 256 位;密钥本身需安全保管,严禁出现在源码或 properties 文件中
补充:如果真要加密整个序列化流
极少数场景(如统一网关层加密所有传输对象)需要对 ObjectOutputStream 底层流加密,可行但需额外封装:
- 用
CipherOutputStream包裹原始输出流,再传给ObjectOutputStream - 对应地,用
CipherInputStream包裹输入流,再传给ObjectInputStream - 该方式无法控制字段粒度,且要求两端严格一致的 Cipher 初始化(包括 IV、padding、mode),调试成本高,一般不推荐用于业务对象


















