Python实现PHP的openssl_decrypt(AES-256-GCM)解密遇MAC校验失败求助
Python中实现PHP openssl_decrypt(AES-256-GCM)解密的问题
原PHP解密代码
需要将这段PHP的AES-256-GCM解密逻辑转换成Python实现:
openssl_decrypt( $acipher, "aes-256-gcm", $secret, OPENSSL_RAW_DATA, $bnonce, $tag);
初始Python代码(参数传递困惑)
我写了一段Python代码,但不知道该如何传入$bnonce和$tag参数:
from crypto.Cipher import AES cipher = AES.new(acipher, AES.MODE_GCM, acipher) plaintext = cipher.decrypt(secret) # 我不知道该把$bnonce和$tag放在哪里,哈哈。
尝试后的错误代码(MAC check failed)
我尝试了以下代码,但一直出现“MAC check failed”错误:
secret = "b34f09fc80e7974a918b50cfda6f48ce" # 这是我对某个字符串使用OpenSSL后得到的内容,仅作补充信息。 content = "EZrjXFQ4o3oCVzy28q/IWYLgg9aP+VlmhQZ+wKs4C20D7k+lZMKlmi9UgZgINz6t/ucWmOzRFLo5DAZ3b6dYrMOz1lV2gJs9v9K6MDFeweREKRDupnTDKgx8AS7OaimofNK8wKxNp5QDnYBZNpY6BQ02mU5586LXGfwJJrAc3S7D85cZZofnUBpNcUztrHYOjCEtvB24p6j8W9ju3ALkAfd1Dk+UdKMVzp9sLa/qbzxPqQeZhPPS43A5b9Wa10DeKvMnMl4Z46M6j+AeudKmS46xCOM84zMbOonxIkCA+kS0BCPaDlpwOyiL31yeJqHw/dKhHuY0qVyHWgrlf3mdHizGKakeAoYsy4Wo1WjeTteSRnw713s=" # Content bcontent = str( base64.b64decode(content) ) bcontent_total_characters = len(bcontent) bcontent_total_characters_minus_12 = bcontent_total_characters - 12 bnonce = bcontent[ bcontent_total_characters_minus_12 : bcontent_total_characters - 12 + bcontent_total_characters ] bcipher = bcontent[ 0 : 0 + bcontent_total_characters_minus_12 ] # default tag bcipher_total_characters = len(bcipher) taglength = 16 tag = bcipher[ bcipher_total_characters - taglength : bcipher_total_characters - taglength + bcipher_total_characters ] acipher = bcipher[ 0 : 0 + bcipher_total_characters - taglength] cipher = AES.new(str.encode(secret), AES.MODE_GCM, str.encode(bnonce) ) decrypted = cipher.decrypt_and_verify(str.encode(content), str.encode(tag))
问题分析与解决方法
核心错误点
- 二进制数据处理错误:把base64解码后的字节流转成字符串,导致二进制数据损坏,应该直接保留字节类型。
- Nonce和Tag截取逻辑错误:
- AES-GCM标准Nonce是12字节,你当前的切片范围完全错误,应该从内容末尾取对应长度的字节。
- Tag是16字节,同样切片范围错误,且错误地从密文里截取,而非整体内容的对应位置。
- 解密传入内容错误:传入了原始base64编码字符串,而非截取后的纯密文部分。
正确的Python实现代码
from Crypto.Cipher import AES import base64 secret = "b34f09fc80e7974a918b50cfda6f48ce" content = "EZrjXFQ4o3oCVzy28q/IWYLgg9aP+VlmhQZ+wKs4C20D7k+lZMKlmi9UgZgINz6t/ucWmOzRFLo5DAZ3b6dYrMOz1lV2gJs9v9K6MDFeweREKRDupnTDKgx8AS7OaimofNK8wKxNp5QDnYBZNpY6BQ02mU5586LXGfwJJrAc3S7D85cZZofnUBpNcUztrHYOjCEtvB24p6j8W9ju3ALkAfd1Dk+UdKMVzp9sLa/qbzxPqQeZhPPS43A5b9Wa10DeKvMnMl4Z46M6j+AeudKmS46xCOM84zMbOonxIkCA+kS0BCPaDlpwOyiL31yeJqHw/dKhHuY0qVyHWgrlf3mdHizGKakeAoYsy4Wo1WjeTteSRnw713s=" # 解码base64内容,保留字节类型,不要转字符串 bcontent = base64.b64decode(content) # 拆分密文、Tag、Nonce:PHP openssl加密后的常见格式为 [密文][Tag(16字节)][Nonce(12字节)] tag_length = 16 nonce_length = 12 nonce = bcontent[-nonce_length:] # 最后12字节是Nonce tag = bcontent[-nonce_length - tag_length : -nonce_length] # Nonce之前的16字节是Tag ciphertext = bcontent[:-nonce_length - tag_length] # 前面的所有字节是纯密文 # 初始化AES-GCM解密器:密钥、模式、Nonce对应PHP的$secret、aes-256-gcm、$bnonce cipher = AES.new(secret.encode('utf-8'), AES.MODE_GCM, nonce=nonce) # 解密并验证Tag:对应PHP的$acipher和$tag plaintext = cipher.decrypt_and_verify(ciphertext, tag) # 输出明文(根据实际编码调整decode的参数) print(plaintext.decode('utf-8'))
关键说明
- 字节类型必须贯穿全程:所有加密解密相关的操作都要用
bytes类型,绝对不能转成字符串,否则会破坏二进制数据。 - 参数对应关系:
- PHP的
$secret→ PythonAES.new()的第一个参数(密钥) - PHP的
$bnonce→ PythonAES.new()的nonce参数 - PHP的
$tag→ Pythondecrypt_and_verify()的第二个参数
- PHP的
- 格式适配:如果上述代码仍报错,可能是加密时的内容拼接顺序不同(比如Nonce在最前面),可尝试调整拆分逻辑:
nonce = bcontent[:12],然后Tag和密文对应调整范围。
内容的提问来源于stack exchange,提问作者Raymond Seger
相关产品推荐
相关产品推荐

