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

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的处理方式不匹配,以及部分参数的逻辑差异:

  1. 盐值处理:Java中通过hex(salt)将十六进制字符串的盐转换为字节数组,而PHP直接使用字符串盐,未做十六进制解码。
  2. IV处理:Java中通过hex(IV)将十六进制字符串的IV转换为字节数组,PHP直接使用字符串IV,未解码。
  3. 参数冗余: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 05:55:46