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

Java解密AES遇IllegalBlockSizeException,PHP无Padding却正常问题

解决Java解密PHP AES-256-CBC无填充密文的块大小异常问题

问题根源

PHP的OpenSSL扩展对AES-CBC无填充模式的实现较为宽松:当密文长度不是AES块大小(16字节)的整数倍时,它会直接处理最后一个不完整的块,不强制块对齐;但Java的JCE加密库严格遵循块加密规范,要求密文长度必须是16字节的整数倍,否则抛出javax.crypto.IllegalBlockSizeException。

解决方案

1. 先确认PHP端的加密逻辑

首先检查PHP代码是否真的启用了无填充模式,避免因填充模式混淆导致的问题:

  • 正确的无填充加密写法:
    $cipher = 'aes-256-cbc';
    $plaintext = '待加密内容';
    $key = '32字节长度的AES-256密钥';
    $iv = '16字节长度的初始化向量';
    // 显式指定无填充选项
    $ciphertext = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
    
    如果未添加OPENSSL_ZERO_PADDING,PHP默认会使用PKCS7填充,此时密文长度是16的倍数,Java只需用AES/CBC/PKCS5Padding模式即可解密。

2. Java端适配无填充密文的非对齐情况

如果PHP确实使用无填充模式且密文长度非16倍数,可通过以下步骤处理:

  • 对Base64解码后的密文补零,使其长度变为16字节的整数倍
  • 用无填充模式解密补全后的密文
  • 去掉解密结果末尾的补零(还原原始明文)

示例代码:

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class AESDecryptor {
    public static void main(String[] args) throws Exception {
        String base64Cipher = "你的Base64编码密文";
        String key = "32字节AES密钥";
        String iv = "16字节初始化向量";

        // 解码密文
        byte[] cipherBytes = Base64.getDecoder().decode(base64Cipher);
        int blockSize = 16;
        // 计算补零后的长度
        int paddedLen = ((cipherBytes.length + blockSize - 1) / blockSize) * blockSize;
        byte[] paddedCipher = new byte[paddedLen];
        System.arraycopy(cipherBytes, 0, paddedCipher, 0, cipherBytes.length);
        // 补零
        for (int i = cipherBytes.length; i < paddedLen; i++) {
            paddedCipher[i] = 0;
        }

        // 初始化解密器
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);

        // 解密并去除末尾补零
        byte[] decrypted = cipher.doFinal(paddedCipher);
        int actualLen = decrypted.length;
        while (actualLen > 0 && decrypted[actualLen - 1] == 0) {
            actualLen--;
        }
        String plaintext = new String(decrypted, 0, actualLen);
        System.out.println(plaintext);
    }
}

3. 推荐方案:统一使用标准填充模式

最稳妥的方式是修改PHP端加密逻辑,使用AES标准的PKCS7填充(默认模式),这样加密后的密文长度必然是16的倍数,Java端无需特殊处理即可兼容:

  • PHP加密代码(PKCS7填充):
    $cipher = 'aes-256-cbc';
    $plaintext = '待加密内容';
    $key = '32字节AES密钥';
    $iv = '16字节初始化向量';
    // 默认PKCS7填充,无需额外选项
    $ciphertext = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA, $iv);
    // 如需Base64编码,使用base64_encode($ciphertext)
    
  • Java解密代码:
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.util.Base64;
    
    public class AESDecryptor {
        public static void main(String[] args) throws Exception {
            String base64Cipher = "你的Base64编码密文";
            String key = "32字节AES密钥";
            String iv = "16字节初始化向量";
    
            byte[] cipherBytes = Base64.getDecoder().decode(base64Cipher);
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
            String plaintext = new String(cipher.doFinal(cipherBytes));
            System.out.println(plaintext);
        }
    }
    

内容的提问来源于stack exchange,提问作者Mohamed E.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 06:12:04