AES Java 实践
AES 算法是一种迭代的对称密钥分组密码,支持 128、192 和 256 位的加密密钥以加密和解密 128 位块中的数据。如果要加密的数据不满足 128 位的块大小要求,则必须对其进行填充。填充是将最后一个块填充到 128 位的过程。
JCE
Java Cryptography Extension(JCE)是一组包,它们提供用于加密、密钥生成和协商以及 Message Authentication Code(MAC)算法的框架和实现。它提供对对称、不对称、块和流密码的加密支持,它还支持安全流和密封的对象。它不对外出口,用它开发完成封装后将无法调用。
JCE 密钥长度限制
加密强度的含义。它由发现密钥的难度定义,这取决于使用的密码和密钥的长度。通常,较长的密钥提供更强的加密。
- 有限的加密强度使用最大 128 位密钥。
- 无限制使用最大长度为 2147483647 位的密钥。
众所周知,JRE 本身包含加密功能。JCE 使用管辖策略文件来控制加密强度。策略文件由两个 jar 组成:local_policy.jar
和 US_export_policy.jar
。多亏了这一点,Java 平台内置了对加密强度的控制。原因很简单,一些国家需要限制加密强度。 如果某个国家/地区的法律允许无限加密强度,则可以根据 Java 版本捆绑或启用它。
检查秘钥长度限制
|
|
- Java 版本 8u151 及更早版本包含
JAVA_HOME/jre/lib/security
目录中的策略文件。 - 从版本 8u151 开始,JRE 提供了不同的策略文件集。 因此,在 JRE 目录
JAVA_HOME/jre/lib/security/policy
中有 2 个子目录:limited 和unlimited。第一个包含有限强度策略文件。第二个包含无限的。
必须用 Oracle 网站上的无限版本替换它
在 Java 版本 8u151 及更高版本中,JCE 框架默认使用无限强度策略文件。此外,还有一个系统属性控制强度。
|
|
必须在 JCE 框架初始化之前设置属性。
Java AESUtil
|
|
Java 和 CryptoJS 互相加解密问题
java 和 js 互相加解密时,如果不指定工作模式和填充方式,会失败。在 js 一般使用crypto-js库来加解密。
- 默认的工作模式是
CryptoJS.mode.CBC
,初始向量 IV 是通过 key 计算(推荐SecureRandom
随机生成并拼接到结果开始的128bit) - 默认填充方式
CryptoJS.pad.Pkcs7
JCE 中 AES 支持五中模式:CBC,CFB,ECB,OFB,PCBC;支持三种填充:NoPadding
,PKCS5Padding
,ISO10126Padding
。
CryptoJS 的 CryptoJS.pad.Pkcs7
与 Java 的 PKCS5Padding
兼容, 另外 BouncyCastleProvider 支持 PKCS7Padding
|
|
padding
某些加密算法要求明文需要按一定长度对齐,叫做块大小(BlockSize),比如16字节,那么对于一段任意的数据,加密前需要对最后一个块填充到16 字节,解密后需要删除掉填充的数据。
- ZeroPadding,数据长度不对齐时使用0填充,否则不填充。
- PKCS7Padding,假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。
- PKCS5Padding,PKCS7Padding的子集,块大小固定为8字节。
由于使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据,而使用ZeroPadding填充时,没办法区分真实数据与填充数据,所以只适合以\0结尾的字符串加解密。
js 采用AES/CBC/ZeroPadding
方式加密。由于java没有提供ZeroPadding
填充方式,可以使用AES/CBC/NoPadding
方式解密,解密后从后往前依次剔除0x00
的字节,遇到非0x00
的字节时停止。剩下的字节就是解密后的结果。不过,这种方式需要注意,原始文本转byte数组后,最后一个字节不能是0x00
。如果是java加密,js解密,将过程反过来即可。
key 与 iv 计算
源码中 cfg.kdf
默认是 CryptoJS.kdf.OpenSSL
(即 EvpKDF
)
在密码学中,密钥派生函数(英语:Key derivation function,简称:KDF)使用伪随机函数从诸如主密钥或密码的秘密值中派生出一个或多个密钥。KDF可用于将密钥扩展为更长的密钥或获取所需格式的密钥,例如将作为迪菲-赫尔曼密钥交换结果的组元素转换为用于高级加密标准(AES)的对称密钥。用于密钥派生的伪随机函数最常见的示例是密码散列函数。
推荐前后端约定好算法模式补码 指定 iv🤝
不显示指定工作模式和填充方式,会造成前后端很难调通。强烈建议前后端约定加解密算法,指定 iv。
算法模式补码推荐 AES/CBC/PKCS5Padding
测试通过,如果不行试试 BouncyCastleProvider 的 PKCS7Padding
指定 iv 可以不用 Java 写 key 生成 iv 的逻辑。
下面代码中的 key 与 iv 是 byte[] 的十六进制表示,可以用 Base64 比较短。其中 iv 必须是 32 个字符 2个十六进制字符1个byte,一共 128个bit。
|
|
|
|
js AES 默认参数示例
一般上面比较通用,如果前段已经固定了默认方式。这里提供下 Java 实现的关键。
CryptoJS
|
|
|
|
vs code 运行选 node.js, 通过调试可以查看 key 与 iv 的产生算法过程。
Java
|
|
|
|
CryptoJS DES
|
|
|
|
附录
|
|