Python转C++的AES GCM解密报错:HashVerificationFilter验证失败
AES GCM解密:PyCryptoDome转Crypto++时MAC验证失败的解决办法
把PyCryptoDome实现的AES GCM解密代码转成Crypto++版本后,一直抛出HashVerificationFilter: message hash or MAC not valid错误。核对过密钥、IV长度都没问题,问题出在几个细节差异上:
问题点分析
TAG长度不匹配
PyCryptoDome的GCM模式默认生成16字节的TAG,Python代码里解密后通过[:-16]去掉了末尾16字节的TAG。但C++代码里定义TAG_SIZE = 12,这直接导致MAC验证失败。密文与TAG的处理逻辑错误
Python中buffer[15:]包含密文+TAG,解密后手动截断TAG。但Crypto++的AuthenticatedDecryptionFilter需要自动识别末尾的TAG,原来的代码没有正确配置,导致过滤器无法找到正确的TAG进行验证。Nonce提取可以简化
Python里取buffer[3:15](12字节)作为Nonce,C++里buffer.substr(0,15).substr(3)等价于buffer.substr(3,12),后者更清晰。文件打开模式错误
C++中打开文件用了std::ios::out(输出模式),应该改为std::ios::in(输入模式),虽然不影响读取,但不符合规范。
修正后的C++代码
#include <cryptopp/cryptlib.h> #include <cryptopp/filters.h> #include <cryptopp/aes.h> #include <cryptopp/gcm.h> #include <fstream> #include <string> #include <iostream> #include <cassert> #include <sstream> using CryptoPP::BufferedTransformation; using CryptoPP::AuthenticatedSymmetricCipher; using CryptoPP::Redirector; using CryptoPP::StringSink; using CryptoPP::StringSource; using CryptoPP::AuthenticatedEncryptionFilter; using CryptoPP::AuthenticatedDecryptionFilter; using CryptoPP::AES; using CryptoPP::GCM; // PyCryptoDome GCM默认TAG长度是16字节 const int TAG_SIZE = 16; std::string decrypt(std::string buffer, std::string key) { GCM<AES>::Decryption d; std::string plain; // 直接提取第3到第14字节(共12字节)作为Nonce,和Python的buffer[3:15]一致 std::string nonce = buffer.substr(3, 12); const CryptoPP::byte* aes_key = reinterpret_cast<const CryptoPP::byte*>(key.data()); const CryptoPP::byte* nonce_byte = reinterpret_cast<const CryptoPP::byte*>(nonce.data()); d.SetKeyWithIV(aes_key, key.size(), nonce_byte, nonce.size()); // 配置过滤器:让它从输入的末尾提取TAG进行验证 AuthenticatedDecryptionFilter df( d, new StringSink( plain ), AuthenticatedDecryptionFilter::DEFAULT_FLAGS, TAG_SIZE ); // buffer.substr(15)包含密文+TAG,直接传给过滤器,它会自动拆分验证 StringSource ss( buffer.substr(15), true, new Redirector(df) ); bool b = df.GetLastResult(); assert( true == b ); return plain; } int main() { // 修正文件打开模式为输入模式 std::ifstream key_f("key.txt", std::ios::binary | std::ios::in); std::ifstream cipher_f("cipher.txt", std::ios::binary | std::ios::in); std::stringstream key; std::stringstream cipher; key << key_f.rdbuf(); cipher << cipher_f.rdbuf(); try { std::string output = decrypt(cipher.str(), key.str()); std::cout << "Decrypted: " << output << std::endl; } catch (CryptoPP::Exception& e) { std::cerr << "Caught Exception: " << e.what() << "\n"; } }
关键修改说明
- 把
TAG_SIZE改为16,匹配PyCryptoDome的默认TAG长度 - Nonce提取改为
buffer.substr(3,12),和Python逻辑完全对齐 - 文件打开模式从
std::ios::out改为std::ios::in - 保持
buffer.substr(15)直接传给过滤器,Crypto++会自动从末尾提取16字节的TAG进行验证,不需要手动截断(这和Python的手动截断逻辑对应,过滤器验证通过后输出的就是纯明文)
内容的提问来源于stack exchange,提问作者rsa16
相关产品推荐
相关产品推荐

