Java AES加密逻辑迁移至PHP后解密结果异常求助
AES/CBC/PKCS5Padding Java转PHP加密后Java解密出现转义序列问题
我正在将Java中AES/CBC/PKCS5Padding的加解密逻辑迁移到PHP,目前遇到问题:PHP加密后的内容用Java解密会出现多余的转义序列,但Java自身加解密该字符串结果正常。
现有代码
Java代码
private static final String ALGORITHM = "AES/CBC/PKCS5Padding"; public static String encrypt(String message, String key) throws GeneralSecurityException, UnsupportedEncodingException { if (message == null || key == null) { throw new IllegalArgumentException("text to be encrypted and key should not be null"); } Cipher cipher = Cipher.getInstance(ALGORITHM); byte[] messageArr = message.getBytes(); byte[] keyparam = key.getBytes(); SecretKeySpec keySpec = new SecretKeySpec(keyparam, "AES"); byte[] ivParams = new byte[16]; byte[] encoded = new byte[messageArr.length + 16]; System.arraycopy(ivParams, 0, encoded, 0, 16); System.arraycopy(messageArr, 0, encoded, 16, messageArr.length); cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivParams)); byte[] encryptedBytes = cipher.doFinal(encoded); encryptedBytes = Base64.getEncoder().encode(encryptedBytes); return new String(encryptedBytes); } public static String decrypt(String encryptedStr, String key) throws GeneralSecurityException, UnsupportedEncodingException { if (encryptedStr == null || key == null) { throw new IllegalArgumentException("text to be decrypted and key should not be null"); } Cipher cipher = Cipher.getInstance(ALGORITHM); byte[] keyparam = key.getBytes(); SecretKeySpec keySpec = new SecretKeySpec(keyparam, "AES"); byte[] encoded = encryptedStr.getBytes(); encoded = Base64.getDecoder().decode(encoded); byte[] decodedEncrypted = new byte[encoded.length - 16]; System.arraycopy(encoded, 16, decodedEncrypted, 0, encoded.length - 16); byte[] ivParams = new byte[16]; System.arraycopy(encoded, 0, ivParams, 0, ivParams.length); cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivParams)); byte[] decryptedBytes = cipher.doFinal(decodedEncrypted); return new String(decryptedBytes); }
PHP代码
$encKey = 'encryptionKey'; $cipher = "aes-256-cbc"; $data = 'some data'; $encryption_key = openssl_random_pseudo_bytes(32); $iv_size = openssl_cipher_iv_length($cipher); $iv = openssl_random_pseudo_bytes($iv_size); $ciphertext_raw = openssl_encrypt(json_encode($data), 'aes-256-cbc', $encKey, $options = OPENSSL_RAW_DATA, $iv); //$ciphertext_raw = openssl_encrypt(json_encode($data), $cipher, $encKey, $options=OPENSSL_RAW_DATA, $iv); $encrypted_data = base64_encode($iv . $ciphertext_raw); print_r($encrypted_data);
测试数据
测试字符串
String str = "vendor_id=INT_GTW&format=json&msg_code=KBEX99&data={\"header\":{\"msg_code\":\"KBEX99\",\"source\":\"INSTANTPAY\",\"channel\":\"CONBNK\",\"txn_ref_number\":\"INSTANTPAY_CONBNK_00001\",\"txn_datetime\":\"1498118309808\",\"ip\":\"1\",\"device_id\":\"XYWZPQR123\",\"api_version\":\"1.0.0\"},\"detail\":{\"entity\":\"INSTANTPAY\",\"intent\":\"REG\",\"user_identifier\":\"INSTANTPAY28423928\",\"crn\":\"105683710\",\"p1\":\"105683710\",\"p2\":\"\",\"p3\":\"INSTANTPAY\",\"p4\":\"\",\"p5\":\"\",\"p6\":\"\",\"p7\":\"\",\"p8\":\"\",\"p9\":\"\",\"p10\":\"\",\"p11\":\"\",\"p12\":\"\",\"p13\":\"\",\"p14\":\"\",\"p15\":\"\",\"p16\":\"\",\"p17\":\"\",\"p18\":\"\",\"p19\":\"\",\"p20\":\"\"}}";
Java加解密正常结果
vendor_id=INT_GTW&format=json&msg_code=KBEX99&data={"header":{"msg_code":"KBEX99","source":"INSTANTPAY","channel":"CONBNK","txn_ref_number":"INSTANTPAY_CONBNK_00001","txn_datetime":"1498118309808","ip":"1","device_id":"XYWZPQR123","api_version":"1.0.0"},"detail":{"entity":"INSTANTPAY","intent":"REG","user_identifier":"INSTANTPAY28423928","crn":"105683710","p1":"105683710","p2":"","p3":"INSTANTPAY","p4":"","p5":"","p6":"","p7":"","p8":"","p9":"","p10":"","p11":"","p12":"","p13":"","p14":"","p15":"","p16":"","p17":"","p18":"","p19":"","p20":""}}
PHP加密后Java解密异常结果
"vendor_id=INT_GTW&format=json&msg_code=KBEX99&data={\"header\":{\"msg_code\":\"KBEX99\",\"source\":\"INSTANTPAY\",\"channel\":\"CONBNK\",\"txn_ref_number\":\"INSTANTPAY_CONBNK_00001\",\"txn_datetime\":\"1498118309808\",\"ip\":\"1\",\"device_id\":\"XYWZPQR123\",\"api_version\":\"1.0.0\"},\"detail\":{\"entity\":\"INSTANTPAY\",\"intent\":\"REG\",\"user_identifier\":\"INSTANTPAY28423928\",\"crn\":\"105683710\",\"p1\":\"105683710\",\"p2\":\"\"\",\"p3\":\"INSTANTPAY\",\"p4\":\"\"\",\"p5\":\"\"\",\"p6\":\"\"\",\"p7\":\"\"\",\"p8\":\"\"\",\"p9\":\"\"\",\"p10\":\"\"\",\"p11\":\"\"\",\"p12\":\"\"\",\"p13\":\"\"\",\"p14\":\"\"\",\"p15\":\"\"\",\"p16\":\"\"\",\"p17\":\"\"\",\"p18\":\"\"\",\"p19\":\"\"\",\"p20\":\"\"\",}}"
问题原因及修正方案
核心问题
- JSON编码冗余:Java直接加密原始字符串字节,PHP却对数据做了
json_encode,导致加密内容是JSON格式化后的字符串,解密后自然带引号和转义字符。 - IV处理逻辑完全相反:
- Java:先将全0IV拼在明文前,再整体加密,最终Base64编码加密后的字节数组。
- PHP:先加密明文,再将随机IV拼在密文前,再Base64编码,和Java的字节拼接、加密顺序完全不符。
- IV值不匹配:Java用的是全0的16字节IV,PHP却生成随机IV,导致解密时IV不一致。
- 密钥混淆:PHP代码中定义了随机生成的
$encryption_key但未使用,实际用的是$encKey,需确保和Java的密钥字节一致(注意字符编码统一)。
修正后的PHP代码
$encKey = 'encryptionKey'; // 根据密钥长度调整:16字节密钥用aes-128-cbc,32字节用aes-256-cbc $cipher = "aes-128-cbc"; $data = 'vendor_id=INT_GTW&format=json&msg_code=KBEX99&data={"header":{"msg_code":"KBEX99","source":"INSTANTPAY","channel":"CONBNK","txn_ref_number":"INSTANTPAY_CONBNK_00001","txn_datetime":"1498118309808","ip":"1","device_id":"XYWZPQR123","api_version":"1.0.0"},"detail":{"entity":"INSTANTPAY","intent":"REG","user_identifier":"INSTANTPAY28423928","crn":"105683710","p1":"105683710","p2":"","p3":"INSTANTPAY","p4":"","p5":"","p6":"","p7":"","p8":"","p9":"","p10":"","p11":"","p12":"","p13":"","p14":"","p15":"","p16":"","p17":"","p18":"","p19":"","p20":""}}'; // 使用和Java一致的全0IV $iv = str_repeat("\x00", 16); // 不做JSON编码,直接拼接IV和明文,再加密(和Java逻辑对齐) $plaintext_with_iv = $iv . $data; $ciphertext_raw = openssl_encrypt($plaintext_with_iv, $cipher, $encKey, OPENSSL_RAW_DATA, $iv); // 直接Base64编码加密后的结果(无需再拼接IV) $encrypted_data = base64_encode($ciphertext_raw); print_r($encrypted_data);
额外注意事项
- 密钥长度匹配:Java的
SecretKeySpec会根据key.getBytes()的长度自动选择AES版本(16字节=AES-128,24字节=AES-192,32字节=AES-256),PHP需对应设置$cipher参数。 - 字符编码统一:Java的
String.getBytes()默认用平台编码,建议在Java和PHP中都明确指定UTF-8,避免编码不一致导致密钥/明文字节不同(比如Java用message.getBytes("UTF-8"),PHP确保字符串为UTF-8编码)。 - IV安全性优化:Java中使用全0IV存在安全风险,生产环境建议改为使用随机IV,并调整Java和PHP的逻辑为:加密明文后将IV拼在密文前传输,解密时先分离IV再解密,但当前为兼容现有Java逻辑暂时保留全0IV。
内容的提问来源于stack exchange,提问作者Rohannn Singh
相关产品推荐
相关产品推荐

