如何实现PHP与Java的AES-256-CBC加密结果一致?
实现与PHP AES-256-CBC加解密兼容的Java代码
问题背景
已有PHP实现的AES-256-CBC加解密逻辑,需要编写对应的Java代码,保证双方加密结果一致、互相可解密。此前尝试的两种Java实现因逻辑不匹配,无法兼容。
PHP代码的核心兼容要点
要实现兼容,必须完全复刻PHP的密钥、IV生成及加解密流程:
- 密钥生成:对原始密钥字符串执行SHA256哈希,得到64字符的十六进制字符串,取前32个字符的UTF-8字节作为AES-256密钥(PHP的
openssl_encrypt会自动截断过长的密钥字符串至算法要求的32字节) - IV生成:对原始IV字符串执行SHA256哈希,得到64字符的十六进制字符串,取前16个字符的UTF-8字节作为AES-CBC的IV(长度符合16字节要求)
- 加密流程:明文 → AES-256-CBC加密(PKCS#7填充)→ 加密结果转base64 → 再次对该base64字符串执行base64编码
- 解密流程:输入值base64解码 → 得到加密结果的base64字符串 → 解码后执行AES-256-CBC解密 → 得到明文
现有Java实现的问题
- 第一种实现使用PBKDF2密钥派生,与PHP直接SHA256哈希的逻辑完全不符,导致密钥不一致
- 第一种实现使用全0数组作为IV,未复刻PHP的IV生成逻辑
- 第二种实现直接使用原始密钥/IV的字节数组,未做SHA256哈希处理;且使用
NoPadding,与PHP默认的PKCS#7填充不兼容 - 两种实现均未处理PHP的双重base64编码逻辑
正确的Java实现
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; public class AESCompatible { private static final String RAW_KEY = "myKey"; // 与PHP的$key保持一致 private static final String RAW_IV = "myIv"; // 与PHP的$iv保持一致 private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding"; // 计算字符串的SHA256哈希,返回十六进制字符串 private static String sha256Hex(String input) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hashBytes = digest.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder hexBuilder = new StringBuilder(); for (byte b : hashBytes) { String hex = Integer.toHexString(0xFF & b); if (hex.length() == 1) { hexBuilder.append('0'); } hexBuilder.append(hex); } return hexBuilder.toString(); } catch (Exception e) { throw new RuntimeException("SHA256哈希计算失败", e); } } public static String encrypt(String plainText) { if (plainText == null) { return null; } try { // 生成兼容PHP的密钥 String keyHex = sha256Hex(RAW_KEY); byte[] keyBytes = keyHex.substring(0, 32).getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); // 生成兼容PHP的IV String ivHex = sha256Hex(RAW_IV); byte[] ivBytes = ivHex.substring(0, 16).getBytes(StandardCharsets.UTF_8); IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); // 初始化加密器 Cipher cipher = Cipher.getInstance(AES_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); // 执行加密,对应PHP openssl_encrypt的base64输出 byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); String firstBase64 = Base64.getEncoder().encodeToString(encryptedBytes); // 二次base64编码,对应PHP的base64_encode return Base64.getEncoder().encodeToString(firstBase64.getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { e.printStackTrace(); return null; } } public static String decrypt(String encryptedText) { if (encryptedText == null) { return null; } try { // 解码外层base64,得到PHP openssl_encrypt返回的base64字符串 byte[] firstDecode = Base64.getDecoder().decode(encryptedText); String encryptedBase64 = new String(firstDecode, StandardCharsets.UTF_8); // 生成兼容PHP的密钥和IV String keyHex = sha256Hex(RAW_KEY); byte[] keyBytes = keyHex.substring(0, 32).getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); String ivHex = sha256Hex(RAW_IV); byte[] ivBytes = ivHex.substring(0, 16).getBytes(StandardCharsets.UTF_8); IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); // 初始化解密器 Cipher cipher = Cipher.getInstance(AES_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec); // 解码内层base64并解密 byte[] encryptedBytes = Base64.getDecoder().decode(encryptedBase64); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); } catch (Exception e) { e.printStackTrace(); // 对应PHP中无法base64解码的情况,返回原输入 return encryptedText; } } // 测试验证 public static void main(String[] args) { String testStr = "Hello AES Compatible!"; String encrypted = encrypt(testStr); System.out.println("加密结果:" + encrypted); String decrypted = decrypt(encrypted); System.out.println("解密结果:" + decrypted); } }
关键说明
- 字符编码全程使用UTF-8,与PHP默认字符串编码保持一致
- Java的
PKCS5Padding与PHP的默认填充PKCS7Padding等价,可互相兼容 - 严格复刻PHP的双重base64编码逻辑,确保加密结果完全一致
- 密钥和IV的生成逻辑与PHP完全对齐,保证加解密的一致性
内容的提问来源于stack exchange,提问作者Emily
相关产品推荐
相关产品推荐

