PHP AES-256-CBC与Java AES/CBC/PKCS5Padding加密结果不一致
问题背景
现有可正常运行的Java AES加密逻辑:对传入密钥做SHA-256哈希,取哈希结果前24字节作为AES密钥,取哈希结果前16字节作为初始化向量IV,采用AES/CBC/PKCS5Padding模式对明文加密后返回Base64编码密文,Java实现代码如下:
public static String encrypt(String message, String sercretKey) { String base64EncryptedString = ""; MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digestOfPassword = md.digest(sercretKey.getBytes("utf-8")); byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24); byte[] iv = Arrays.copyOf(digestOfPassword, 16); SecretKey key = new SecretKeySpec(keyBytes, "AES"); javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key, ivParameterSpec); byte[] plainTextBytes = message.getBytes("utf-8"); byte[] buf = cipher.doFinal(plainTextBytes); byte[] base64Bytes = Base64.getEncoder().encode(buf); base64EncryptedString = new String(base64Bytes); return base64EncryptedString; }
使用PHP复现该逻辑时,相同明文、密钥下加密结果和Java端完全不一致,当前PHP代码如下:
function encryptTest($sSecretKey,$sValue) { $key = hash('sha256', $sSecretKey,false); $key = utf8_encode($key); $key = substr($key, 0, 24); $iv = substr($key, 0, 16); $data = $sValue; $outEnc = openssl_encrypt($data, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv); return base64_encode($outEnc); }
错误原因
PHP代码一共有3处核心逻辑和Java端不匹配:
- SHA-256返回结果格式错误:PHP的
hash()函数默认返回十六进制小写字符串,长度为64字符(对应32字节哈希值的十六进制文本表示),但Java端md.digest()返回的是长度32字节的原始二进制字节数组。额外添加的utf8_encode()是把十六进制字符串转成UTF-8编码,和Java端拿到的原始哈希二进制内容完全不符。 - AES加密模式配置错误:Java端取SHA-256哈希前24字节作为密钥,24字节对应AES-192算法,不是AES-256。PHP中写的
AES-256-CBC要求密钥长度为32字节,和Java端密钥长度不匹配,底层会自动做截断/补零处理导致结果偏差。 - 填充规则隐式匹配验证:Java端使用的
PKCS5Padding是PHP openssl扩展CBC模式的默认填充规则,不需要额外配置,但前提是密钥、IV、算法位长完全匹配才能对齐结果。
修正后的PHP代码
function encryptTest($sSecretKey, $sValue) { // 第三个参数传true,让hash返回原始二进制结果,和Java端digest()返回值完全一致 $digestOfPassword = hash('sha256', $sSecretKey, true); // 从原始二进制哈希中截取前24字节作为密钥,对应AES-192算法 $keyBytes = substr($digestOfPassword, 0, 24); // 从原始二进制哈希中截取前16字节作为IV $iv = substr($digestOfPassword, 0, 16); // 算法改为AES-192-CBC,明文默认按UTF-8读取和Java端对齐 $outEnc = openssl_encrypt($sValue, "AES-192-CBC", $keyBytes, OPENSSL_RAW_DATA, $iv); return base64_encode($outEnc); }
内容的提问来源于stack exchange,提问作者Unni K S
相关产品推荐
相关产品推荐

