You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python转C++的AES GCM解密报错:HashVerificationFilter验证失败

AES GCM解密:PyCryptoDome转Crypto++时MAC验证失败的解决办法

把PyCryptoDome实现的AES GCM解密代码转成Crypto++版本后,一直抛出HashVerificationFilter: message hash or MAC not valid错误。核对过密钥、IV长度都没问题,问题出在几个细节差异上:


问题点分析

  1. TAG长度不匹配
    PyCryptoDome的GCM模式默认生成16字节的TAG,Python代码里解密后通过[:-16]去掉了末尾16字节的TAG。但C++代码里定义TAG_SIZE = 12,这直接导致MAC验证失败。

  2. 密文与TAG的处理逻辑错误
    Python中buffer[15:]包含密文+TAG,解密后手动截断TAG。但Crypto++的AuthenticatedDecryptionFilter需要自动识别末尾的TAG,原来的代码没有正确配置,导致过滤器无法找到正确的TAG进行验证。

  3. Nonce提取可以简化
    Python里取buffer[3:15](12字节)作为Nonce,C++里buffer.substr(0,15).substr(3)等价于buffer.substr(3,12),后者更清晰。

  4. 文件打开模式错误
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 00:30:55