Python AES加密后Java解密触发AEADBadTagException问题求助
AES-GCM Tag Mismatch When Java Uses DECRYPT_MODE (PyCryptodome Encryption)
问题背景
基于PyCryptodome实现的AES-GCM加密代码,需对接固定的Java解密工具。RSA加密AES密钥、传递AES nonce流程正常,但Java端使用Cipher.DECRYPT_MODE解密时抛出javax.crypto.AEADBadTagException: Tag mismatch,切换为Cipher.ENCRYPT_MODE可正常解密,但生产环境Java代码固定为DECRYPT_MODE无法修改。Python端调用cipher.decrypt()结果与encrypt()一致,复现代码如下:
Python原加密代码
import base64 import Crypto.Cipher.AES aesnonce = base64.b64decode(b"nsHTfsEFKyVytEk2") aeskey = base64.b64decode(b"2hn3S9IsCTut7Hr9SX/7Pg==") cipher = Crypto.Cipher.AES.new(aeskey, Crypto.Cipher.AES.MODE_GCM, nonce=aesnonce) plaintext = b'''Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore.''' crypted = cipher.encrypt(plaintext) print( base64.b64encode(crypted) ) # 输出:w0ehWquycruj2KGkRES4M8pc6w4ZM+9HyI+KOXgqybmiYIuU1nsjZJwisZprxx2mczP280ZT5KlCXzSX0h24136m708N8sbX6m+5QlDVa73/r9dRyfSzFeByi51NqVm35wP4oHdxYW5iVx9P8iFFpcYd9c8=
Java原解密代码
import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; public class MainTest { static public void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException { byte[] aesnonce = Base64.getDecoder().decode("nsHTfsEFKyVytEk2"); byte[] aeskeybytes = Base64.getDecoder().decode("2hn3S9IsCTut7Hr9SX/7Pg=="); byte[] crypted = Base64.getDecoder().decode("w0ehWquycruj2KGkRES4M8pc6w4ZM+9HyI+KOXgqybmiYIuU1nsjZJwisZprxx2mczP280ZT5KlCXzSX0h24136m708N8sbX6m+5QlDVa73/r9dRyfSzFeByi51NqVm35wP4oHdxYW5iVx9P8iFFpcYd9c8="); SecretKeySpec secretKey = new SecretKeySpec(aeskeybytes, "AES"); GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, aesnonce); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec); // 可正常解密 // cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec); // 报错 ByteArrayInputStream inputStream = new ByteArrayInputStream(crypted); CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); byte[] plaintext = cipherInputStream.readAllBytes(); System.out.print( new String(plaintext) ); } }
问题原因
核心在于AES-GCM加密输出格式不匹配:
- PyCryptodome的
AES-GCM.encrypt()方法仅返回密文,128位认证标签(Tag)需通过cipher.digest()单独获取; - Java的
AES/GCM/NoPadding在DECRYPT_MODE下,默认期望输入为密文 + 128位认证标签(代码中GCMParameterSpec(128, aesnonce)指定了标签长度); - 当前Python仅输出密文,未附加标签,导致Java解密时无法找到有效校验标签,抛出
Tag mismatch; - Java切换为
ENCRYPT_MODE时,CipherInputStream的错误用法跳过了标签校验,因此能解析明文,但这不符合GCM安全规范。
解决方法
修改Python代码,将密文与认证标签拼接后再进行Base64编码,确保Java端能读取到完整的加密数据(密文+标签),从而在DECRYPT_MODE下正常解密。
修改后的Python加密代码
import base64 from Crypto.Cipher import AES aesnonce = base64.b64decode(b"nsHTfsEFKyVytEk2") aeskey = base64.b64decode(b"2hn3S9IsCTut7Hr9SX/7Pg==") cipher = AES.new(aeskey, AES.MODE_GCM, nonce=aesnonce) plaintext = b'''Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore.''' crypted = cipher.encrypt(plaintext) # 拼接密文和128位认证标签 full_encrypted_data = crypted + cipher.digest() print(base64.b64encode(full_encrypted_data))
验证
将修改后的Python输出的Base64字符串替换到Java代码的crypted变量中,启用cipher.init(Cipher.DECRYPT_MODE, ...)即可正常解密,无需修改Java代码。
内容的提问来源于stack exchange,提问作者Anaconda
相关产品推荐
相关产品推荐

