AES/GCM解密时调用update后doFinal仅返回部分明文问题
AES/GCM解密时update+doFinal返回部分明文的问题分析与解决
问题现象
使用AES/GCM/NoPadding算法解密时,若先调用cipher.update(ciphertext)再执行cipher.doFinal()(或cipher.doFinal(new byte[0])),仅能得到部分明文。例如输入JSON:
String jsonExample = "{\"dataType\":\"STRING\",\"strValue\":\"000000\"}";
解密后仅返回"000000";但直接调用cipher.doFinal(ciphertext)则能完整还原原始字符串。小体积文本可正常解密,特定尺寸文本出现数据丢失。
核心原因
问题出在忽略了Cipher.update()方法的返回值:
Cipher.update()处理数据时,若输入数据量超过Cipher内部缓冲区大小,会立即返回一部分处理后的明文;剩余未处理的数据会在cipher.doFinal()时完成处理并返回。- 你的解密代码仅返回了
doFinal()的结果,丢弃了update()返回的那部分明文,导致最终只得到后半段数据。 - 小文本能正常运行,是因为数据量未超过缓冲区大小,
update()返回空数组,所有明文都由doFinal()返回,因此未暴露问题。
解决办法
修改解密代码,收集update()和doFinal()的所有返回值并合并,才能得到完整明文。示例代码如下:
方式1:数组直接合并
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", new BouncyCastleProvider()); GCMParameterSpec spec = new GCMParameterSpec(Constants.GCM_TAG_BYTES * 8, nonce); cipher.init(javax.crypto.Cipher.DECRYPT_MODE, dataKey, spec); // 获取update返回的部分明文 byte[] partialData = cipher.update(ciphertext); // 获取doFinal返回的剩余明文 byte[] finalData = cipher.doFinal(); // 合并两部分数据 byte[] decrypted = new byte[partialData.length + finalData.length]; System.arraycopy(partialData, 0, decrypted, 0, partialData.length); System.arraycopy(finalData, 0, decrypted, partialData.length, finalData.length); return decrypted;
方式2:用ByteArrayOutputStream收集(更灵活)
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", new BouncyCastleProvider()); GCMParameterSpec spec = new GCMParameterSpec(Constants.GCM_TAG_BYTES * 8, nonce); cipher.init(javax.crypto.Cipher.DECRYPT_MODE, dataKey, spec); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] chunk; // 收集update的输出 chunk = cipher.update(ciphertext); if (chunk != null && chunk.length > 0) { outputStream.write(chunk); } // 收集doFinal的输出 chunk = cipher.doFinal(); if (chunk != null && chunk.length > 0) { outputStream.write(chunk); } return outputStream.toByteArray();
关键注意事项
- 无论使用哪种对称加密模式(ECB、CBC、GCM等),都不能忽略
Cipher.update()的返回值,这是JCE Cipher类的通用处理逻辑,和具体加密模式无关。 - GCM作为AEAD模式,解密时会自动验证附在密文末尾的认证标签,但标签的验证逻辑不影响
update()和doFinal()的返回值规则,只需正确收集所有输出即可。
内容的提问来源于stack exchange,提问作者P basak
相关产品推荐
相关产品推荐

