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; } }
关键修改点说明
- IV的统一处理:加密时生成随机IV,和加密内容一起拼接后再BASE64编码;解密时先拆分出IV,再用同一个IV初始化Cipher,保证加密解密的IV一致。
- 替换BASE64工具:用
java.util.Base64替代过时的sun.misc类,兼容性和稳定性更好。 - 修复字符串比较:用
equals方法替代==,避免逻辑错误。 - 文件写入保留换行:用
outputFile.println()替代write(),保证每行加密后的内容单独占一行,解密时能正确读取每行内容。
这样修改后,你应该能解决IllegalBlockSizeException的问题,并且加密解密后的文件能完全匹配。
内容的提问来源于stack exchange,提问作者Gagan
相关产品推荐
相关产品推荐

