不同语言产生加密差异的原因
不同语言产生不同加密结果差异的主要原因基本上产生在算法选择、填充方式、$IV量这几方面的差异
具体可以看这篇文章:https://segmentfault.com/a/1190000018059273 文章里面有个错误 我已经在评论里面指出。
但是其他方面总结的非常全面:
MCRYPT_RIJNDAEL_256 并不是 AES-256
1 2 3 |
MCRYPT_RIJNDAEL_128 & MCRYPT_MODE_CBC + 16位Key = openssl_encrypt(AES-128-CBC, 16位Key) = AES-128 MCRYPT_RIJNDAEL_128 & MCRYPT_MODE_CBC + 24位Key = openssl_encrypt(AES-192-CBC, 24位Key) = AES-192 MCRYPT_RIJNDAEL_128 & MCRYPT_MODE_CBC + 32位Key = openssl_encrypt(AES-256-CBC, 32位Key) = AES-256 |
openssl_* 的 AES cipher 的 iv 长度 固定 为 16 位
这一点 很多网上抄写的代码基本上都用mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128 , MCRYPT_MODE_CBC) 来获取iv长度,这个函数返回的就是16,与OPENSSL兼容
MCRYPT_*默认的填充方式不是PKCS7,而是填充null('\x00')
常见的对方语言比如java/.net则默认使用PKCS7,所以存在实现差异,也会导致加密的结果不一致
所以,如果你的MCRYPT_*实现的AES加密符合$iv=16,使用PKCS7填充,算法和$key 长度也符合要求,那么就能实现100%兼容的转换。
转换过程
转换填充方式
常见的网上流行的代码 基本上都有用纯PHP实现了类似这样的PKCS7填充方式
1 2 3 4 5 6 7 |
function pkcs7($content) { $bit = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $pad = $bit - (mb_strlen($content, '8bit') % $bit); $content .= str_repeat(chr($pad), $pad); return $content; } |
在OPENSSL下请忽略他,因为默认就是PKCS7填充
转换$iv生成方法
OPENSSL本身实现了一个openssl_random_pseudo_bytes来作为推荐的$iv生成方法,记得生成16位的就行,如果你不生成$iv,那么解密也不需要使用$iv
确定加密方式
参考上面那个表里面根据$key长度确定到底使用的是AES-128、256等加密方法
注意加密的编码方式
OPENSSL有OPENSSL_RAW_DATA等选项控制输入输出原始字符串还是编码后base64文本,你需要自行确认加密解密实际输入输出的参数应该是哪种形式,尤其是需要和$iv一起传递密文的时候
传输加密后文本
如果你没有使用$iv,那么加密后的内容就是全部,按需要进行传输就行,如果你使用$iv,那么需要把这个$iv一起传递给对方,有些实现是,把$iv拼在加密后的原始字符串前后,解密的时候按照固定长度切出密文和$iv,这个可以依据双方的系统设计,自行确定适配方法。