Java加密字符串无法被PHP解密,求匹配AES-128-CTR的解决方案
问题分析与解决方案
你的Java代码和PHP代码的加密参数存在多处不匹配,导致无法解密,以下是具体问题和修正后的代码:
不匹配的核心点
- 加密模式不一致:PHP使用
AES-128-CTR,Java默认用AES/ECB/PKCS5Padding,两者完全不兼容 - IV未使用:PHP指定了固定IV,Java代码未传入IV(CTR模式必须使用IV)
- 密钥填充规则不同:PHP自动用NULL字节补全16字节密钥,Java用空格补全,导致密钥不一致
- Base64编码与URL处理:未确保Java生成的编码字符串符合PHP的解码预期
修正后的Java代码
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; // 配置与PHP完全一致的参数 String encryptionKey = "MyPassword"; String ivString = "1234567891011121"; // 和PHP的encryption_iv保持一致 JsonObject joParms = new JsonObject(); joParms.addProperty("id", invoiceId); joParms.addProperty("db", db); joParms.addProperty("archived", isArchive); // 处理密钥:补NULL字节到16字节(和PHP行为对齐) byte[] keyBytes = new byte[16]; byte[] originalKeyBytes = encryptionKey.getBytes(); System.arraycopy(originalKeyBytes, 0, keyBytes, 0, Math.min(originalKeyBytes.length, 16)); // 剩余位置默认填充0(等价于PHP的NULL字节) SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); // 处理IV:转换为字节数组 byte[] ivBytes = ivString.getBytes(); IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); // 初始化CTR模式的Cipher(Java 1.6需确保JCE支持该模式) Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); // 加密JSON字符串 byte[] jsonBytes = joParms.toString().getBytes(); byte[] encryptedBytes = cipher.doFinal(jsonBytes); // 生成URL安全的Base64编码,避免URL转义问题 String encodedString = Base64.encodeBase64URLSafeString(encryptedBytes); // 拼接最终消息 String message = messageTemplate.replace("{INVNO}", invNo) .replace("{AMOUNT}", amount) .concat(" ") .concat(siteUrl) .concat("?p=") .concat(encodedString);
关键细节说明
- 加密模式:明确指定
AES/CTR/NoPadding,与PHP的AES-128-CTR完全匹配(CTR模式无需填充) - IV处理:直接使用PHP中相同的IV字符串转字节数组,确保两边IV完全一致
- 密钥对齐:PHP中密钥长度不足16字节时,会自动用NULL字节(\0)填充,Java通过创建16字节数组并拷贝原密钥,剩余位置默认填充0,保证密钥与PHP端一致
- Base64编码:使用URL安全的Base64编码,避免普通Base64中的
+、/在URL中被转义,PHP端的urldecode可正确处理该编码;若使用普通Base64,需额外对结果执行URLEncoder.encode
PHP端兼容性验证
无需修改现有PHP代码,只要Java端参数匹配,openssl_decrypt即可正常解密:
$encryption_options = 0会自动检测并解码Base64输入,URL安全的Base64也能被正确识别
内容的提问来源于stack exchange,提问作者Hussain Akbar
相关产品推荐
相关产品推荐

