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

Java AES/CBC文件加解密异常:前半段明文解密失败

问题分析与解决方案

你遇到的解密前半段乱码问题,核心原因是加密时实际使用的IV和写入文件头的IV不一致,再加上几处流处理的逻辑错误,导致CBC模式下解密的初始块完全错乱,后续内容也受连锁影响。下面具体拆解问题并给出修复方案:

核心问题1:加密时IV使用逻辑错误

当fileStoreIV为true时,你的代码初始化Cipher时没有传入指定的initVector:

if(fileStoreIV) { 
    SecretKeySpec skeySpec = new SecretKeySpec(key, ALGORITHM); 
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec); // 这里未传入指定的IV!
}

对于AES/CBC模式,若不主动指定IV,Cipher会自动生成一个随机IV,但你却把自己传入的initVector写到了文件头。解密时用文件头的IV去解密,自然和加密实际用的IV不匹配,导致第一个16字节块(AES块大小)解密乱码,CBC的链式特性还会影响后续块的解密结果。

核心问题2:输入流读取方式错误

加密时你用FileEncryptor.class.getResourceAsStream(loadFile.getName())读取文件,这个方法是从类路径下读取资源,而非读取用户指定的任意路径文件。如果用户输入的文件不在类路径下,这个流会是null,要么读取失败,要么读取到错误的文件。

核心问题3:IV写入时机错误

你在第一次读取明文后才写入IV到文件头:

for(int length=fin.read(bytes); length!=-1; length = fin.read(bytes)){
    if(fileStoreIV) { 
        fout.write(initVector); 
        fileStoreIV = false; 
    }
    cipherOut.write(bytes, 0, length);
}

如果文件为空或者第一次读取就到末尾,IV根本不会被写入;而且即使写入,也是在读取了一部分明文之后,逻辑上不符合“IV在文件最开头”的设计。

修复后的加密方法

private static void encrypt(byte[] key, byte[] initVector, String inputFile, String outputFile) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException {
    // 初始化Cipher:无论是否存储IV,都使用指定的initVector
    Cipher cipher = Cipher.getInstance(CIPHER);
    IvParameterSpec iv = new IvParameterSpec(initVector);
    SecretKeySpec skeySpec = new SecretKeySpec(key, ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

    // 文件检查逻辑保持不变
    Path loadFilePath = Paths.get(inputFile);
    if (!Files.exists(loadFilePath)){
        System.out.println("The inputFile you specified does not exist");
        return;
    }
    Path saveFile = Paths.get(outputFile);
    Path parentDir = saveFile.getParent();
    if (parentDir != null && !Files.exists(parentDir)) {
        System.out.println("The outputFile directory/s you specified does not exist");
        return;
    }

    System.out.println("Secret key is " + Base64.getEncoder().encodeToString(key));
    System.out.println("IV is " + Base64.getEncoder().encodeToString(initVector));

    // 修正流处理逻辑:先写IV,再处理加密流
    try (InputStream fin = Files.newInputStream(loadFilePath); // 替换为正确的文件输入流
         OutputStream fout = Files.newOutputStream(saveFile)) {

        // 先把IV写入文件头部
        if(fileStoreIV) {
            fout.write(initVector);
        }

        // 初始化CipherOutputStream,开始加密写入
        try (CipherOutputStream cipherOut = new CipherOutputStream(fout, cipher)) {
            final byte[] bytes = new byte[1024];
            int length;
            while ((length = fin.read(bytes)) != -1) {
                cipherOut.write(bytes, 0, length);
            }
        }
    } catch (IOException e) {
        System.out.println("Something went wrong with reading and writing these files!");
        System.out.println("Please check you have the latest version of this program");
        System.out.println("Contact your IT admin to make sure you have sufficient privileges");
    }
    System.out.println("SUCCESS! Encryption finished, saved at specified location");
}

修复后的解密方法

还要修复解密时读取IV的逻辑,确保完整读取16字节IV:

private static void decrypt(String inputKEY, String inputIV, String inputFile, String outputFile) throws NoSuchAlgorithmException, NoSuchPaddingException, IOException, InvalidKeyException, InvalidAlgorithmParameterException {
    Cipher cipher = Cipher.getInstance(CIPHER);
    SecretKeySpec skeySpec = new SecretKeySpec(Base64.getDecoder().decode(inputKEY), ALGORITHM);

    Path loadFilePath = Paths.get(inputFile);
    if (!Files.exists(loadFilePath)){
        System.out.println("The inputFile you specified does not exist");
        return;
    }
    Path saveFile = Paths.get(outputFile);
    Path parentDir = saveFile.getParent();
    if (parentDir != null && !Files.exists(parentDir)) {
        System.out.println("The outputFile directory/s you specified does not exist");
        return;
    }

    try (InputStream encryptedData = Files.newInputStream(loadFilePath)) {
        IvParameterSpec ivSpec;
        if(fileStoreIV) {
            // 确保完整读取16字节IV
            byte[] fileIV = new byte[16];
            int bytesRead = 0;
            while (bytesRead < 16) {
                int read = encryptedData.read(fileIV, bytesRead, 16 - bytesRead);
                if (read == -1) {
                    throw new IOException("Invalid encrypted file: IV missing or incomplete");
                }
                bytesRead += read;
            }
            System.out.println(Base64.getEncoder().encodeToString(fileIV));
            ivSpec = new IvParameterSpec(fileIV);
        } else {
            ivSpec = new IvParameterSpec(Base64.getDecoder().decode(inputIV));
        }

        // 初始化解密Cipher
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);

        // 解密写入文件
        try (CipherInputStream decryptStream = new CipherInputStream(encryptedData, cipher);
             OutputStream decryptedOut = Files.newOutputStream(saveFile)) {
            final byte[] bytes = new byte[1024];
            int length;
            while ((length = decryptStream.read(bytes)) != -1) {
                decryptedOut.write(bytes, 0, length);
            }
        }
    } catch (IOException e) {
        System.out.println("Something went wrong with reading and writing these files!");
        System.out.println("Please check you have the latest version of this program");
        System.out.println("Contact your IT admin to make sure you have sufficient privileges");
    }
    System.out.println("SUCCESS! Decryption finished, saved at specified location");
}

关键修改说明

  1. 加密时强制使用指定的IV:无论fileStoreIV是否为true,都用传入的initVector初始化Cipher,保证加密用的IV和写入文件头的IV完全一致。
  2. 替换输入流为文件流:用Files.newInputStream读取用户指定的文件,避免类路径限制导致的读取错误。
  3. 提前写入IV:在读取明文前就把IV写入文件头部,保证文件结构符合设计预期。
  4. 解密时完整读取IV:循环读取直到拿到16字节IV,避免因流读取不完整导致IV错误。

这样修改后,加解密的IV就能完全匹配,流处理逻辑也正确,乱码问题应该就能解决了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:02:46