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

Java加密的数据无法用PHP解密,请求排查实现差异

AES/GCM Java与PHP加密不互通的差异排查

以下是导致二者无法互相解密的常见核心差异及对应解决方法:

1. 密钥与编码一致性

  • 确保两端密钥的字节数组完全一致:Java中必须明确指定编码(如StandardCharsets.UTF_8)转换密钥字符串为字节,PHP中对应使用utf8_encode($keyStr),禁止依赖系统默认编码。
  • 密钥长度要匹配加密算法:16字节密钥对应aes-128-gcm,32字节对应aes-256-gcm,两端必须统一算法标识。

2. IV(初始化向量)的生成与传递

AES/GCM强制推荐使用12字节(96位)IV,这是保证互通的关键:

  • Java端必须生成12字节IV,加密后需将IV与密文+标签拼接后传输;
  • PHP端不能依赖openssl_encrypt自动生成IV(默认长度可能不符),需手动用random_bytes(12)生成12字节IV,且解密时必须使用加密时的同一IV。

3. 附加认证数据(AAD)的处理

如果Java代码中调用了cipher.updateAAD()添加AAD,PHP端必须对应调用openssl_set_aad()设置相同的AAD数据,且AAD的字节编码要和Java端一致(如统一UTF-8),否则会导致认证失败。

4. 密文与标签的拼接逻辑

这是最容易出错的点:

  • Java中Cipher.doFinal()返回的是密文 + 16字节标签的拼接字节数组;
  • PHP中使用OPENSSL_RAW_DATA选项时,openssl_encrypt仅返回密文,标签通过引用参数$tag单独返回,必须手动将密文与标签拼接后再和IV组合传输,否则解密时会因数据结构不匹配失败。

5. 输出格式的统一

  • 若Java端将IV+密文+标签拼接后做Base64编码,PHP端必须对应做相同的拼接后再Base64编码,解密时也要按相同顺序拆分数据。
  • 禁止一端用原始字节传输,另一端用Base64编码传输,必须统一格式。

示例修正对比

正确的Java加密逻辑片段

import org.apache.commons.lang3.ArrayUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

public String encrypt(String plaintext, String key, String aad) throws Exception {
    byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
    SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
    
    // 生成12字节IV
    byte[] iv = new byte[12];
    new SecureRandom().nextBytes(iv);
    
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); // 128位标签长度
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmSpec);
    
    // 设置AAD(如果有)
    if (aad != null) {
        cipher.updateAAD(aad.getBytes(StandardCharsets.UTF_8));
    }
    
    // 加密得到密文+标签
    byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
    
    // 拼接IV + 密文+标签,返回Base64编码
    return Base64.getEncoder().encodeToString(ArrayUtils.addAll(iv, encrypted));
}

对应的PHP加密逻辑片段

function encrypt(string $plaintext, string $key, ?string $aad): string {
    $cipher = 'aes-128-gcm'; // 对应16字节密钥,若密钥是32字节则用aes-256-gcm
    $keyBytes = utf8_encode($key);
    $iv = random_bytes(12); // 强制12字节IV
    
    $tag = '';
    if ($aad !== null) {
        openssl_set_aad(utf8_encode($aad));
    }
    
    // 加密得到密文,标签通过$tag返回
    $ciphertext = openssl_encrypt(
        utf8_encode($plaintext),
        $cipher,
        $keyBytes,
        OPENSSL_RAW_DATA,
        $iv,
        $tag
    );
    
    // 拼接IV + 密文 + 标签,再Base64编码
    return base64_encode($iv . $ciphertext . $tag);
}

内容的提问来源于stack exchange,提问作者Shijin TR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:30:53