AES加盐加密从Java转PHP结果不一致问题求助
AES加密逻辑Java转PHP结果不一致的解决方法
问题概述
将AES加密逻辑从Java转换到PHP时,两端生成的加密结果不一致,需要实现两端输出完全相同的加密结果。以下是两端的原始实现代码:
Java实现代码
public class AesUtil { private final Cipher cipher; public final static String passPhrase = ""; public final static String IV = ""; //32bit IV Length public final static String SALT = ""; public final static int KEY_SIZE = 128; public final static int ITERATION_COUNT = 10000; public AesUtil() { try { cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { throw fail(e); } } // Call this function to encrypt some String public String encrypt(String passPhrase, String plaintext) { try { SecretKey key = generateKey(SALT, passPhrase); byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, IV, plaintext.getBytes("UTF-8")); return base64(encrypted); } catch (UnsupportedEncodingException e) { throw fail(e); } } private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) { try { cipher.init(encryptMode, key, new IvParameterSpec(hex(IV))); return cipher.doFinal(bytes); } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { throw fail(e); } } private SecretKey generateKey(String salt, String passphrase) { try { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), ITERATION_COUNT, KEY_SIZE); SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES"); return key; } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw fail(e); } } public static String random(int length) { byte[] salt = new byte[length]; new SecureRandom().nextBytes(salt); return hex(salt); } public static String base64(byte[] bytes) { return new String(Base64.encodeBase64(bytes)); } public static byte[] base64(String str) { return Base64.decodeBase64(str.getBytes()); } public static String hex(byte[] bytes) { return Hex.encodeHexString(bytes); } public static byte[] hex(String str) { try { return Hex.decodeHex(str.toCharArray()); } catch (DecoderException e) { throw new IllegalStateException(e); } } private IllegalStateException fail(Exception e) { return new IllegalStateException(e); } }
原始PHP实现代码
<?php $password = ""; $salt = ""; $iv = ""; //32bit IV Lenght $length = 16; $salt = ""; $interation = 10000; $key1 = mb_convert_encoding($password, "UTF-8"); $bytes = openssl_pbkdf2($key1, $salt, $length, $interation, "sha1"); $bytes2 = hash_pbkdf2("sha1", $password, $salt, $interation, $length, true); $ciphertext_b64 = base64_encode(openssl_encrypt($plaintext,"aes-128-cbc", $bytes2,OPENSSL_RAW_DATA, $iv)); ?>
问题分析
两端结果不一致的核心原因是盐值、IV的处理方式不匹配,以及部分参数的逻辑差异:
- 盐值处理:Java中通过
hex(salt)将十六进制字符串的盐转换为字节数组,而PHP直接使用字符串盐,未做十六进制解码。 - IV处理:Java中通过
hex(IV)将十六进制字符串的IV转换为字节数组,PHP直接使用字符串IV,未解码。 - 参数冗余:PHP代码中重复定义了
$salt,且部分参数命名有误(如$interation应为$iteration)。
修复后的PHP代码
以下代码完全对齐Java的加密逻辑:
<?php $password = ""; // 对应Java的passPhrase $saltHex = ""; // 对应Java的SALT(十六进制字符串) $ivHex = ""; // 对应Java的IV(十六进制字符串,32位即16字节) $iterationCount = 10000; $keySizeBytes = 16; // 128位密钥对应16字节 $plaintext = ""; // 需要加密的明文 // 1. 将十六进制的盐和IV转为字节数组,对齐Java的hex()解码逻辑 $salt = hex2bin($saltHex); $iv = hex2bin($ivHex); // 2. 生成PBKDF2密钥,对齐Java的generateKey方法 $key = hash_pbkdf2("sha1", $password, $salt, $iterationCount, $keySizeBytes, true); // 3. AES-CBC加密,对齐Java的doFinal方法(自动处理PKCS5填充) $encryptedBytes = openssl_encrypt($plaintext, "aes-128-cbc", $key, OPENSSL_RAW_DATA, $iv); $ciphertextB64 = base64_encode($encryptedBytes); // 输出加密结果 echo $ciphertextB64; ?>
其他编程语言实现建议
Python实现(使用PyCryptodome库)
import base64 import hashlib from Crypto.Cipher import AES from Crypto.Protocol.KDF import PBKDF2 # 配置参数 password = b"" # 转为字节类型,对齐Java的字符编码逻辑 salt_hex = "" iv_hex = "" iteration_count = 10000 key_size_bits = 128 # 解码盐和IV salt = bytes.fromhex(salt_hex) iv = bytes.fromhex(iv_hex) # 生成PBKDF2密钥 key = PBKDF2(password, salt, dkLen=key_size_bits//8, count=iteration_count, prf=lambda p,s: hashlib.sha1(p+s).digest()) # 明文填充(PKCS5),PyCryptodome需手动处理 plaintext = "" plaintext_bytes = plaintext.encode("utf-8") padding_length = AES.block_size - len(plaintext_bytes) % AES.block_size plaintext_padded = plaintext_bytes + bytes([padding_length]) * padding_length # AES-CBC加密 cipher = AES.new(key, AES.MODE_CBC, iv) encrypted_bytes = cipher.encrypt(plaintext_padded) ciphertext_b64 = base64.b64encode(encrypted_bytes).decode("utf-8") print(ciphertext_b64)
内容的提问来源于stack exchange,提问作者abar yadi
相关产品推荐
相关产品推荐

