PHP与Shell通过OpenSSL加密字符串的结果差异及问题排查
PHP与Shell AES-256-CBC加密结果不一致的排查与修复
核心问题分析
两段代码的核心差异在于密钥/IV的格式处理逻辑不匹配,以及哈希值提取方式错误:
- 参数格式差异:
- PHP的
openssl_encrypt接收$key和$iv为原始二进制字节串,但原代码错误地使用了sha256哈希的十六进制字符串片段,而非原始二进制哈希。 - Shell的
openssl enc -K/-iv参数要求传入原始二进制数据的十六进制表示:AES-256需要32字节密钥(对应64位十六进制),CBC模式IV需要16字节(对应32位十六进制)。原Shell代码只取了32位/16位十六进制片段,openssl自动补0到要求长度,导致实际密钥/IV与PHP完全不符。
- PHP的
- 哈希提取逻辑错误:
原代码试图生成32字节密钥、16字节IV,但实际取的是十六进制字符串的片段,而非对应长度的原始二进制哈希。
修复方案
修复PHP代码
修改哈希提取逻辑,获取原始二进制哈希值:
<?php $encrypt_method = "AES-256-CBC"; $secret_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; $secret_iv = 'YYYYYYYYY'; // 获取32字节原始二进制密钥(完整sha256哈希) $key = hash('sha256', $secret_key, true); // 获取16字节原始二进制IV(sha256哈希的前16字节) $iv = substr(hash('sha256', $secret_iv, true), 0, 16); $string = file_get_contents('plaintext.txt'); $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv); file_put_contents('ciphertext.base64', $output); ?>
说明:
hash函数第三个参数设为true时,返回原始二进制哈希,而非十六进制字符串,符合openssl_encrypt的参数要求。
修复Shell代码
修改哈希提取逻辑,取符合长度要求的十六进制字符串:
key='XXXXXXXXXXXXXXXXXXXXXXXXXXXXX' secret_iv='YYYYYYYYY' # 取完整64位十六进制sha256哈希(对应32字节密钥) keyhash=$(echo -n "$key" | sha256sum | cut -c 1-64) # 取前32位十六进制sha256哈希(对应16字节IV) ivhash=$(echo -n "$secret_iv" | sha256sum | cut -c 1-32) openssl enc -aes-256-cbc -e -K "$keyhash" -iv "$ivhash" -in plaintext.txt -out ciphertext.base64 -base64
说明:此时密钥和IV的十六进制长度完全符合openssl要求,不会触发“hex string is too short”的补0警告。
验证一致性
修复后,两段代码使用的密钥和IV完全匹配:
- 密钥:
$secret_key的完整sha256原始哈希(32字节),PHP直接用二进制,Shell用对应的64位十六进制表示 - IV:
$secret_iv的sha256哈希前16字节原始数据,PHP直接用二进制,Shell用对应的32位十六进制表示
执行后生成的ciphertext.base64内容完全一致。
内容的提问来源于stack exchange,提问作者Thibault Richard
相关产品推荐
相关产品推荐

