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

Java加密文件长度需为16倍数?解密遇IllegalBlockSizeException求助

解决AES解密时的IllegalBlockSizeException问题

首先,先明确你遇到的核心错误:

错误:javax.crypto.IllegalBlockSizeException: 使用带填充的密码解密时,输入长度必须是16的倍数

我仔细看了你的代码和尝试过的方案,问题主要出在CBC模式的IV处理不一致、过时的BASE64工具类,还有一些代码细节上的问题,咱们一步步来修复:

核心问题分析

  • CBC模式必须使用相同的IV进行加密和解密:你给Cipher指定了AES/CBC/PKCS5Padding,但加密时没有显式指定IV(此时Cipher会自动生成随机IV),解密时也没有传入加密时用的那个IV,这会导致解密过程完全混乱,不仅会抛出长度异常,就算不抛异常,解密结果也是错的。
  • 过时的BASE64实现:你用的sun.misc.BASE64Encoder/Decoder是Oracle JDK的私有API,不仅不兼容其他JDK(比如OpenJDK),还可能存在编码/解码的隐性问题,建议换成Java 8+自带的java.util.Base64。
  • 字符串比较方式错误:你用function_type == "ENCRYPT"来判断操作类型,这是引用比较,应该用"ENCRYPT".equals(function_type),避免空指针和引用不匹配的问题。
  • 文件写入未保留换行:加密后写入文件时直接用outputFile.write(convertedValue),没有添加换行符,导致解密时读取的是整段拼接的内容,BASE64解码后长度不符合要求。

修复后的完整代码

import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.io.PrintWriter;
import java.io.FileWriter;

public class Crypto {
    private Scanner inputFile;
    private PrintWriter outputFile;
    private static final String ALGO = "AES/CBC/PKCS5Padding";
    private byte[] keyValue;
    // 使用安全随机生成IV
    private final SecureRandom secureRandom = new SecureRandom();

    public Crypto(String key) {
        // 你的密钥"dk201anckse29sns"是16位,刚好符合AES-128的要求
        keyValue = key.getBytes();
    }

    public String encrypt(String data) throws Exception {
        Key key = generateKey();
        // 生成16位随机IV
        byte[] iv = new byte[16];
        secureRandom.nextBytes(iv);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);

        Cipher cipher = Cipher.getInstance(ALGO);
        cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

        byte[] encryptedBytes = cipher.doFinal(data.getBytes());
        // 将IV和加密内容拼接:IV在前,加密内容在后,然后一起BASE64编码
        byte[] combined = new byte[iv.length + encryptedBytes.length];
        System.arraycopy(iv, 0, combined, 0, iv.length);
        System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length);

        return Base64.getEncoder().encodeToString(combined);
    }

    public String decrypt(String encryptedData) throws Exception {
        Key key = generateKey();
        // 解码BASE64,拆分IV和加密内容
        byte[] combined = Base64.getDecoder().decode(encryptedData);
        byte[] iv = new byte[16];
        byte[] encryptedBytes = new byte[combined.length - 16];
        System.arraycopy(combined, 0, iv, 0, iv.length);
        System.arraycopy(combined, 16, encryptedBytes, 0, encryptedBytes.length);

        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance(ALGO);
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        return new String(decryptedBytes);
    }

    public Key generateKey() throws Exception {
        return new SecretKeySpec(keyValue, "AES");
    }

    public void encrypt_decrypt(String function_type, String source_file, String target_file) {
        String lineValue = "";
        String convertedValue = "";
        try {
            inputFile = new Scanner(new File(source_file));
        } catch (Exception e) {
            System.out.println("( " + source_file + ") - File Opening Error");
            return; // 打开失败直接退出
        }
        try {
            outputFile = new PrintWriter(new FileWriter(target_file));
        } catch (Exception e) {
            System.out.println("( " + target_file + ") - File Opening Error");
            return;
        }

        while (inputFile.hasNext()) {
            lineValue = inputFile.nextLine();
            System.out.println("Source Line: " + lineValue);
            try {
                // 修复字符串比较方式
                if ("ENCRYPT".equals(function_type)) {
                    convertedValue = encrypt(lineValue);
                } else if ("DECRYPT".equals(function_type)) {
                    convertedValue = decrypt(lineValue);
                }
            } catch (Exception e) {
                System.out.println("处理错误:" + e.getMessage());
                convertedValue = "";
            }
            System.out.println("Converted Line : " + convertedValue);
            // 写入时添加换行符,保留每行结构
            outputFile.println(convertedValue);
        }
        inputFile.close();
        outputFile.close();
    }

    public static void main(String args[]) throws IOException {
        Crypto c = new Crypto("dk201anckse29sns");
        c.encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt", "CryptoCiphertext.txt");
        c.encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt", "CryptoDeciphered.txt");

        // 对比文件
        System.out.println(compareFiles() ? "The files are identical!" : "The files are NOT identical.");
    }

    public static boolean compareFiles() throws IOException {
        Scanner pt = new Scanner(new File("CryptoPlaintext.txt"));
        Scanner dc = new Scanner(new File("CryptoDeciphered.txt"));

        while (pt.hasNextLine() && dc.hasNextLine()) {
            if (!pt.nextLine().equals(dc.nextLine())) {
                return false;
            }
        }

        if (pt.hasNextLine() || dc.hasNextLine()) {
            return false;
        }

        return true;
    }
}

关键修改点说明

  1. IV的统一处理:加密时生成随机IV,和加密内容一起拼接后再BASE64编码;解密时先拆分出IV,再用同一个IV初始化Cipher,保证加密解密的IV一致。
  2. 替换BASE64工具:用java.util.Base64替代过时的sun.misc类,兼容性和稳定性更好。
  3. 修复字符串比较:用equals方法替代==,避免逻辑错误。
  4. 文件写入保留换行:用outputFile.println()替代write(),保证每行加密后的内容单独占一行,解密时能正确读取每行内容。

这样修改后,你应该能解决IllegalBlockSizeException的问题,并且加密解密后的文件能完全匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:17:49