Java无开箱即用文本加解密工具,String不参与加解密;必须用Cipher等组件,严格处理编码、IV、填充及Base64编解码,显式指定算法模式(禁用ECB),密钥须通过getEncoded()导出。

Java 中没有开箱即用的“文本加密解密工具类”,String 本身不参与加解密,真正起作用的是 Cipher、SecretKeySpec、SecureRandom 等密码学组件——直接对 String 调用方法不会加密,必须先转成字节数组,并严格处理字符编码和填充模式。
为什么 new String(cipher.doFinal(...)) 常解密失败
常见错误是忽略字节到字符串的编解码一致性。加密输出的是任意字节序列,可能包含无法映射为有效 UTF-8 字符的 byte 组合;若用 new String(bytes)(默认平台编码)或未指定 charset 解码,会损坏数据。
- 加密前:用
str.getBytes(StandardCharsets.UTF_8)明确编码 - 解密后:必须用相同 charset 构造字符串,例如
new String(decryptedBytes, StandardCharsets.UTF_8) - 更稳妥做法:对加密后的
byte[]进行 Base64 编码(得到合法字符串),传输/存储后再 Base64 解码回字节数组
AES/CBC/PKCS5Padding 和 AES/GCM/NoPadding 的关键区别
选择哪种算法模式直接影响是否需要手动管理 IV、是否校验完整性、能否安全并行处理。
-
AES/CBC/PKCS5Padding:需显式传入 16 字节 IV(每次加密应不同),解密时必须用**同一 IV**;不校验密文是否被篡改 -
AES/GCM/NoPadding:IV(称 nonce)通常 12 字节,自动提供认证标签(authentication tag),解密时必须传入完整 tag,否则Cipher.doFinal()抛AEADBadTagException - GCM 模式下,
Cipher初始化后需调用cipher.updateAAD()(如有附加认证数据),且doFinal()输入必须包含 tag(GCM 输出 = 密文 + tag)
KeyGenerator 生成密钥后,为什么不能直接 toString() 存储
SecretKey 对象的 toString() 只返回类名和哈希码,**不包含密钥材料**;序列化它也不安全(可能含敏感字段)。
立即学习“Java免费学习笔记(深入)”;
- 正确导出方式:调用
secretKey.getEncoded()得到原始 key 字节数组,再用 Base64 编码为字符串保存 - 恢复密钥时:Base64 解码字符串 → 得到
byte[]→ 用new SecretKeySpec(encodedKey, "AES")重建 - 切勿用
KeyPair.getPublic().toString()当公钥字符串——应调用getEncoded()后 Base64 编码,格式才是标准 X.509
使用 Cipher.getInstance() 时,"AES" 和 "AES/ECB/PKCS5Padding" 的隐式风险
只写 "AES" 依赖 JVM 默认实现,不同厂商(Oracle JDK / OpenJDK / Android)可能默认不同模式(如 ECB 或 GCM),导致跨环境加解密失败。
- 必须显式指定完整转换字符串,例如
"AES/CBC/PKCS5Padding"或"AES/GCM/NoPadding" - ECB 模式绝对禁用——相同明文块永远生成相同密文块,完全不满足语义安全
- Android API 28+ 已废弃
PKCS5Padding别名,应统一用PKCS7Padding(二者在 AES 中等价,但命名更准确)
真正麻烦的不是写几行 Cipher 调用,而是 IV 生成与传递、字符编码绑定、密钥生命周期管理、以及 GCM 模式下 tag 的截取与拼接位置——这些细节错一个,解密就静默失败或抛异常,很难定位。










