You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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\":\"\"\",}}"

问题原因及修正方案

核心问题

  1. JSON编码冗余:Java直接加密原始字符串字节,PHP却对数据做了json_encode,导致加密内容是JSON格式化后的字符串,解密后自然带引号和转义字符。
  2. IV处理逻辑完全相反:
    • Java:先将全0IV拼在明文前,再整体加密,最终Base64编码加密后的字节数组。
    • PHP:先加密明文,再将随机IV拼在密文前,再Base64编码,和Java的字节拼接、加密顺序完全不符。
  3. IV值不匹配:Java用的是全0的16字节IV,PHP却生成随机IV,导致解密时IV不一致。
  4. 密钥混淆: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 20:48:14