一、

最近对接的项目需要访问接口来传递数据,对方是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

只好手动实现填充了,不过我这里没有按照标准来进行了,毕竟现在这种模式已经可以正常运行了:)