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

使用AES GCM模式的CipherOutputStream时遭遇ShortBufferException

AES/GCM解密抛出ShortBufferException的问题解决

核心错误点

你的代码里有两个致命问题导致了这个异常:

1. 加密时未关闭CipherOutputStream,丢失GCM认证标签

AES/GCM加密完成后必须写入认证标签,解密时要靠它验证数据完整性。CipherOutputStream只有在调用close()时才会触发Cipher.doFinal(),把标签写入文件。你之前写完加密数据直接结束,没关流,导致文件缺少标签,解密时GCM模式无法完成验证,直接抛出异常。

2. 解密时重复创建FileInputStream,把nonce当成加密数据解密

你读取完nonce后,重新new了一个FileInputStream,等于让解密流从头开始读取——把前面12字节的nonce也当成加密数据处理,数据结构完全错误,触发缓冲区异常。

修正后的完整代码

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;

public class FileEncrypterDecrypter {
    private static final int ALGORITHM_NONCE_SIZE = 12;
    private static final String ALGORITHM_NAME = "AES/GCM/NoPadding";
    private final static int ALGORITHM_TAG_SIZE = 128;
    private final static HashMap<String, SecretKey> fileToKey = new HashMap<>();

    FileEncrypterDecrypter() {}

    public static void encrypt(InputStream plaintext, String filePath) throws IOException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException {
        SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
        fileToKey.put(filePath, secretKey);

        SecureRandom rand = new SecureRandom();
        byte[] nonce = new byte[ALGORITHM_NONCE_SIZE];
        rand.nextBytes(nonce);
        System.out.println(Arrays.toString(nonce));

        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(ALGORITHM_TAG_SIZE, nonce));

        // 用try-with-resources自动关流,确保CipherOutputStream写完认证标签
        try (FileOutputStream fileOut = new FileOutputStream(filePath);
             CipherOutputStream cipherOut = new CipherOutputStream(fileOut, cipher)) {
            fileOut.write(nonce);
            plaintext.transferTo(cipherOut);
        }
    }

    public static String decrypt(String filePath) throws InvalidKeyException, IOException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
        SecretKey secretKey = fileToKey.get(filePath);
        if (secretKey == null) {
            throw new IllegalArgumentException("找不到该文件对应的密钥");
        }

        try (FileInputStream fileIn = new FileInputStream(filePath)) {
            byte[] nonce = new byte[ALGORITHM_NONCE_SIZE];
            int readBytes = fileIn.read(nonce);
            if (readBytes != ALGORITHM_NONCE_SIZE) {
                throw new IOException("读取nonce不完整,文件可能损坏");
            }
            System.out.println(Arrays.toString(nonce));

            Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(ALGORITHM_TAG_SIZE, nonce));

            // 直接用已经跳过nonce的fileIn,不用重新开流
            try (CipherInputStream cipherIn = new CipherInputStream(fileIn, cipher);
                 InputStreamReader inputReader = new InputStreamReader(cipherIn);
                 BufferedReader reader = new BufferedReader(inputReader)) {

                StringBuilder sb = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
                return sb.toString();
            }
        }
    }
}

额外优化说明

  • 用try-with-resources自动管理流,无需手动关闭,既避免资源泄漏,又保证CipherOutputStream能正确写入认证标签。
  • 添加nonce读取完整性检查,防止文件损坏时出现隐式错误。
  • 添加密钥存在性判断,避免空指针异常。

测试验证

你的主方法现在可以正常运行,解密后会输出HELLO WORLD,不会再抛出异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 04:01:00