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

Java8实现Rijndael-128/CBC加密144位密钥报错解决方案

问题结论

PHP 老旧的mcrypt扩展确实支持144位(18字节)密钥执行Rijndael-128/CBC加密,但这个行为不符合Rijndael/AES官方标准,属于mcrypt自身的宽松兼容逻辑,这也是Java侧无论用JCA还是BouncyCastle都报密钥长度错误的核心原因。

原因说明
  • 你调用的rijndael-128是指块大小固定为128位的Rijndael算法,也就是标准AES的算法原型,所有规范实现都仅支持16字节(128位)、24字节(192位)、32字节(256位)三种合法密钥长度。
  • mcrypt作为已经废弃多年的旧扩展,对密钥长度的校验极其宽松:当传入非标准长度密钥时,不会抛出错误,而是自动在密钥末尾填充0x00空字节,将密钥补齐到大于传入长度的最近合法密钥长度后再执行加密。你传入的18字节密钥,会被自动补6个空字节,凑成24字节(192位)的合法密钥使用。
  • JCA、BouncyCastle都是严格遵循算法规范实现的,会直接拒绝非标准长度密钥,因此抛出长度不合法的报错。
  • 你提供的Java代码还有一处逻辑错误:PHP代码是手动实现PKCS5填充后再传入mcrypt,mcrypt本身不会自动添加填充,但Java代码错误使用了ZeroBytePadding,会导致填充规则不匹配,即使密钥长度正确也无法得到和PHP一致的结果。
Java侧兼容实现步骤
  • 对原始18字节密钥做补零处理,末尾补6个0x00凑成24字节(192位),作为实际加密使用的密钥
  • 移除Cipher配置中的ZeroBytePadding,不需要引擎额外做填充,对齐PHP手动填充的逻辑
  • 所有字符串转字节数组时明确指定字符编码(推荐UTF-8),填充长度计算按字节数而非字符数,对齐PHPstrlen按字节计数的逻辑
修正后的Java代码
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.RijndaelEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.encoders.Hex;
import java.nio.charset.StandardCharsets;

public class PhpRijndaelCompat {
    public static void main(String[] args) throws Exception {
        String plainText = "Hello World";
        String key = "***"; // 原始18字符密钥
        String iv = "***"; // 16字符IV
        System.out.println(encrypt(plainText, key, iv));
    }

    private static String encrypt(String plainText, String key, String iv) throws Exception {
        // 对齐PHP mcrypt的密钥补零逻辑:18字节补到24字节(192位)
        byte[] originalKeyBytes = key.getBytes(StandardCharsets.UTF_8);
        byte[] realKeyBytes = new byte[24];
        System.arraycopy(originalKeyBytes, 0, realKeyBytes, 0, originalKeyBytes.length);

        byte[] ivBytes = iv.getBytes(StandardCharsets.UTF_8);
        byte[] paddedPlainBytes = pkcs5Pad(plainText).getBytes(StandardCharsets.UTF_8);

        // 不添加额外填充,对齐PHP手动填充逻辑
        BufferedBlockCipher cipher = new BufferedBlockCipher(new CBCBlockCipher(new RijndaelEngine(128)));
        CipherParameters params = new ParametersWithIV(new KeyParameter(realKeyBytes), ivBytes);
        cipher.init(true, params);

        byte[] outBuf = new byte[cipher.getOutputSize(paddedPlainBytes.length)];
        int processLen = cipher.processBytes(paddedPlainBytes, 0, paddedPlainBytes.length, outBuf, 0);
        int finalLen = cipher.doFinal(outBuf, processLen);

        byte[] encrypted = new byte[processLen + finalLen];
        System.arraycopy(outBuf, 0, encrypted, 0, encrypted.length);
        return Hex.toHexString(encrypted);
    }

    // 完全对齐PHP的pkcs5_pad逻辑,按字节计算填充长度
    private static String pkcs5Pad(String text) {
        int blockSize = 16;
        int byteLen = text.getBytes(StandardCharsets.UTF_8).length;
        int padLen = blockSize - (byteLen % blockSize);
        return text.concat(String.valueOf((char) padLen).repeat(padLen));
    }
}

通用补零规则参考:如果后续遇到其他非标准长度密钥,按如下规则补零即可和mcrypt行为对齐:

  • 密钥长度≤16字节:补零到16字节(128位)
  • 16字节<密钥长度≤24字节:补零到24字节(192位)
  • 24字节<密钥长度≤32字节:补零到32字节(256位)

内容的提问来源于stack exchange,提问作者Prakhar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 18:54:22