如何将mcrypt_encrypt转换为openssl_encrypt?解决加密结果不一致问题
问题:替换mcrypt为OpenSSL后加密结果不一致的解决方法
我要优化一个用了废弃mcrypt库的WordPress插件,改用OpenSSL加密提交的数据,但遇到了问题:openssl_encrypt的返回结果和mcrypt_encrypt不一致,导致对接的系统无法返回正确数据,而且对方没法提供上传日志。我全网搜过没找到解决方案,怀疑是填充问题,求帮忙。
原代码示例(已修正语法错误)
class EncryptDebug{ private $algo = 'sha1'; private $password = 'ab4232goodcf423484422c90c3e4aa7c'; private $salt = 'ascastas54490a31'; private $iv = '8947da32awl55kwj'; private $length = 16; private function generate_key(){ return hash_pbkdf2( $this->algo , $this->password , $this->salt, 100, $this->length, true ); } public function encryptSSL($plaintext){ $key = $this->generate_key(); $ciphertext = base64_encode(openssl_encrypt($plaintext, 'AES-128-CBC', $key, OPENSSL_ZERO_PADDING, $this->iv)); return str_replace('+', '%2B', $ciphertext); } public function encryptMCRYPT($plaintext){ $key = $this->generate_key(); $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $this->iv)); return str_replace('+', '%2B', $ciphertext); } }
补充说明
用OPENSSL_ZERO_PADDING会报错,用OPENSSL_RAW_DATA得到的结果和mcrypt_encrypt相似,但结尾不同,示例如下:
- OpenSSL加密结果:
rPzVvF7gaPMA4ADAjHUW8Wy1ThTJG%2BVPdcz5iKAkAwrDTTFTcOpWgWOCh9l9JFZ8WcNzMJ868026TkUxcYJMrQ== - mcrypt加密结果:
rPzVvF7gaPMA4ADAjHUW8Wy1ThTJG%2BVPdcz5iKAkAwrDTTFTcOpWgWOCh9l9JFZ8UGVfF091Q9bY61mTRg%2BBSg==
问题根源
mcrypt_encrypt在CBC模式下默认自动用零填充把明文补到块大小的整数倍;而OpenSSL的OPENSSL_ZERO_PADDING选项不会自动填充,必须手动处理,这就是结果不一致的核心原因。
修复方案
1. 修正代码语法错误
- 把变量名
$lenght改为$length,避免拼写错误 - 在
$iv = '8947da32awl55kwj'行末尾添加分号
2. 手动实现零填充
添加一个方法,把明文填充到AES-128的块大小(16字节)的整数倍,和mcrypt的自动填充行为对齐:
private function zero_pad($data, $block_size){ $pad_length = $block_size - (strlen($data) % $block_size); return $data . str_repeat("\0", $pad_length); }
3. 调整OpenSSL加密参数
使用OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING组合参数,确保输出原始二进制数据且不自动添加PKCS7填充。
修正后的完整代码
class EncryptDebug{ private $algo = 'sha1'; private $password = 'ab4232goodcf423484422c90c3e4aa7c'; private $salt = 'ascastas54490a31'; private $iv = '8947da32awl55kwj'; private $length = 16; private function generate_key(){ return hash_pbkdf2($this->algo, $this->password, $this->salt, 100, $this->length, true); } // 手动零填充,对齐mcrypt行为 private function zero_pad($data, $block_size){ $pad_length = $block_size - (strlen($data) % $block_size); return $data . str_repeat("\0", $pad_length); } public function encryptSSL($plaintext){ $key = $this->generate_key(); $block_size = openssl_cipher_iv_length('AES-128-CBC'); $padded_text = $this->zero_pad($plaintext, $block_size); // 组合参数确保原始输出+不自动填充 $ciphertext_raw = openssl_encrypt($padded_text, 'AES-128-CBC', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv); $ciphertext = base64_encode($ciphertext_raw); return str_replace('+', '%2B', $ciphertext); } public function encryptMCRYPT($plaintext){ $key = $this->generate_key(); $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $this->iv)); return str_replace('+', '%2B', $ciphertext); } }
验证说明
- 零填充逻辑和mcrypt完全一致:计算明文长度与16字节块的差值,补对应数量的空字符
\0 - 参数组合
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING让OpenSSL输出和mcrypt相同格式的二进制加密数据,再经过base64编码和字符替换后,结果就能完全匹配。
内容的提问来源于stack exchange,提问作者Astronneu
相关产品推荐
相关产品推荐

