Android中AES-GCM文件解密抛出AEADBadTagException异常求助
嘿,我之前踩过AES-GCM解密时这个BAD_DECRYPT的坑,结合你说的情况——字符串加解密正常但文件解密报错,大概率是文件读写时的字节处理出了问题,尤其是GCM模式依赖的核心要素没处理对。咱们一步步来排查:
IV(初始化向量)的存储与读取必须完全一致
AES-GCM要求解密时用的IV和加密时完全相同,推荐用12字节长度的IV。如果加密时你把IV写到了文件开头,但解密时没读够长度,或者用了字符流读取导致字节损坏,就会触发校验失败。
检查你的代码:加密时是不是先写IV再写密文?解密时是不是先读取完整的12字节IV?注意InputStream.read()可能不会一次读完所有字节,大文件场景下要循环读取确保IV完整。比如:// 加密时写IV byte[] iv = new byte[12]; SecureRandom.getInstanceStrong().nextBytes(iv); outputStream.write(iv); // 解密时读IV byte[] iv = new byte[12]; int bytesRead = 0; while (bytesRead < 12) { int read = inputStream.read(iv, bytesRead, 12 - bytesRead); if (read == -1) throw new IOException("IV读取不完整"); bytesRead += read; }附加数据(AAD)必须加密解密完全匹配
如果你加密时调用了cipher.updateAAD(aadBytes)设置了附加数据,解密时必须传入完全一样的AAD(内容、长度都不能变)。如果字符串加解密没用到AAD,但文件加解密用了(或者反过来),就会出现这个异常。不需要AAD的话,两边都别调用这个方法。文件读写必须用字节流,别碰字符流
如果你用了FileReader/FileWriter这类字符流处理文件,会把字节转成字符再转回,过程中可能丢失或修改字节,破坏密文的完整性。字符串加解密用的是字节数组所以没问题,但文件必须用FileInputStream/FileOutputStream这类字节流全程处理。密文必须完整读写(包含GCM认证标签)
AES-GCM加密后的结果是「加密数据+16字节认证标签」,你必须把cipher.doFinal()返回的所有字节都写入文件;解密时也要读取全部密文字节(包括标签)再传给cipher.doFinal(),不能截断。分块处理大文件时,要记得把cipher.update()的输出和doFinal()的输出都写入文件,解密同理。密钥必须完全一致
虽然字符串加解密正常,但也要确认文件加解密时用的是同一个密钥。如果密钥是从密码派生的,要确保PBKDF2的迭代次数、盐值、哈希算法完全一致。
给你一个经过验证的文件加解密示例片段,你可以对照调整自己的代码:
// 加密文件(支持大文件分块) public static void encryptFile(String inPath, String outPath, SecretKey key) throws Exception { try (FileInputStream fis = new FileInputStream(inPath); FileOutputStream fos = new FileOutputStream(outPath)) { // 生成12字节IV并写入文件 byte[] iv = new byte[12]; SecureRandom.getInstanceStrong().nextBytes(iv); fos.write(iv); // 初始化GCM cipher Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv)); // 分块读写 byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { byte[] encryptedChunk = cipher.update(buffer, 0, bytesRead); if (encryptedChunk != null) fos.write(encryptedChunk); } // 写入最后一块和认证标签 byte[] finalChunk = cipher.doFinal(); if (finalChunk != null) fos.write(finalChunk); } } // 解密文件 public static void decryptFile(String inPath, String outPath, SecretKey key) throws Exception { try (FileInputStream fis = new FileInputStream(inPath); FileOutputStream fos = new FileOutputStream(outPath)) { // 读取IV byte[] iv = new byte[12]; int bytesRead = 0; while (bytesRead < 12) { int read = fis.read(iv, bytesRead, 12 - bytesRead); if (read == -1) throw new IOException("无效的加密文件:IV缺失"); bytesRead += read; } // 初始化GCM cipher Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv)); // 分块读写 byte[] buffer = new byte[8192]; while ((bytesRead = fis.read(buffer)) != -1) { byte[] decryptedChunk = cipher.update(buffer, 0, bytesRead); if (decryptedChunk != null) fos.write(decryptedChunk); } // 处理最后一块并校验标签 byte[] finalChunk = cipher.doFinal(); if (finalChunk != null) fos.write(finalChunk); } } // 生成AES-256密钥 public static SecretKey generateAESKey() throws NoSuchAlgorithmException { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); return keyGen.generateKey(); }
对照这些点排查,应该能解决你的AEADBadTagException问题。
内容的提问来源于stack exchange,提问作者Ruthwik Warrier

