AES 加密算法的填充
一
最近对接的项目需要访问接口来传递数据,对方是java后台,我这边使用PHP的curl函数进行访问,结果出了大大小小的各种问题,果然语言不通无法沟通(。
总而言之,问题的关键在于对方对传输的数据做了一次加密,使用的是AES-128-CBC
,这是一种对称加密算法,可以使用PHP内置的openssl_encrypt()
(php_v7.1以上)函数进行加密。然而无论怎么修改代码也无法通过对面的验证,无奈之下只好拿了对面的加密算法来细细研究。
AES的实现都是内置的函数,而因为AES-128是加密时会先将字符串以16字节分割为一组,在不足16字节的时候进行填充,而填充的方法是自定义的,所以密文的不同也就是因为填充方式的不同。
对面的填充的代码:
byte[] dataBytes = originalWords.getBytes();
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
凭借我贫瘠的java知识,我大概猜测到byte数组的默认数据是0,于是尝试填充0,结果完全不行,之后又尝试了空字符串,结果也挂了。但问题肯定出在填充模式上,于是就触及到这种算法默认的填充字串是什么。
之后在这里找到了一个神奇的数字——\\x00
在Unicode中这代表空字符,在C的衍生语言中都是这样定义的(?
尝试使用\\x00
进行填充之后成功了,java方面的知识早就还给老师了,于是只好继续看看关于AES-128-CBC
的知识了。
二
对称加密算法的填充操作也有标准定义,在PKCS(Public Key Cryptography Standards)的几个不同版本中对填充有不同的定义,一般来说常用PKCS7。
PKCS #7 填充字符串由一个字节序列组成,每个字节填充该填充字节序列的长度。
假定块长度为 8,数据长度为 9,
数据: FF FF FF FF FF FF FF FF FF
PKCS7 填充: FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07
也就是填充的数为缺少的字节数。
在使用openssl_encrypt()
函数的时候是没有自动填充的,网上找到的资料说这个函数会按照PKCS7标准进行自动填充,可实际不手动进行填充的话,运行时会产生一个错误:
error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length
只好手动实现填充了,不过我这里没有按照标准来进行了,毕竟现在这种模式已经可以正常运行了:)