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
相关产品推荐
相关产品推荐

