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

如何使用Crypto++库结合口令与盐解密OpenSSL加密内容?

问题分析与解决方案

1. 解密前未做Base64解码

OpenSSL加密后会将二进制密文编码为Base64字符串,而Crypto++直接使用这个Base64字符串进行解密——Base64编码后的长度通常不是AES块大小(16字节)的倍数,这就是触发StreamTransformationFilter块大小异常的直接原因。

修正步骤:解密前先对密文做Base64解码,Crypto++可通过Base64Decoder实现:

std::string decoded_cipher;
CryptoPP::Base64Decoder decoder;
decoder.Attach(new CryptoPP::StringSink(decoded_cipher));
decoder.Put(reinterpret_cast<const unsigned char*>(enc.data()), enc.size());
decoder.MessageEnd();

2. IV推导逻辑完全错误

OpenSSL中通过PBKDF2生成32字节派生数据,前16字节用作AES-128的Key,后16字节用作IV;但你的Crypto++代码存在两处关键错误:

  • 派生数据长度仅设为16 + saltLen(salt为"salt123"时长度是7,仅派生23字节),远不足32字节
  • IV取的是派生数据中16字节之后的saltLen字节(7字节),和OpenSSL的16字节IV完全不匹配

修正步骤:派生32字节数据,取后16字节作为IV:

// 派生32字节数据用于Key和IV
CryptoPP::SecByteBlock derived(32);
CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA1> pbkdf;
pbkdf.DeriveKey(derived, derived.size(), 0,
                reinterpret_cast<const CryptoPP::byte*>(password), strlen(password),
                reinterpret_cast<const CryptoPP::byte*>(salt), saltLen, 1000);

// 前16字节是Key,后16字节是IV
CryptoPP::SecByteBlock key(16), iv(16);
std::memcpy(key.data(), derived.data(), key.size());
std::memcpy(iv.data(), derived.data() + 16, iv.size());

3. 其他代码错误修正

  • 加密函数调用拼写错误:ecrypt("test123", ...) → encrypt("test123", ...)
  • strLen(password) 改为标准C函数 strlen(password)
  • OpenSSL加密函数中补充ctx初始化:EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();,并删除重复定义的unsigned char key[EVP_MAX_KEY_LENGTH];
  • Crypto++解密可使用更简洁的CBC_Mode<AES>::Decryption替代CBC_Mode_ExternalCipher

修正后的完整代码

#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <string.h>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h>
#include <cryptopp/base64.h>
#include <cryptopp/pwdbased.h>
#include <cryptopp/sha.h>
#include <iostream>

std::string stringToBase64(const std::string& input) 
{
    BIO* bio = BIO_new(BIO_s_mem());
    BIO* base64 = BIO_new(BIO_f_base64());
    BIO_set_flags(base64, BIO_FLAGS_BASE64_NO_NL);  // No line breaks

    BIO* bio_chain = BIO_push(base64, bio);    
    BIO_write(bio_chain, input.c_str(), input.length());
    BIO_flush(bio_chain);

    char* encoded_data;
    long encoded_length = BIO_get_mem_data(bio, &encoded_data);
    std::string base64_string(encoded_data, static_cast<size_t>(encoded_length));

    BIO_free_all(bio_chain);    
    return base64_string;
}

std::string encrypt(std::string str, const char* password, const char* salt)
{
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); // 初始化ctx
    if (!ctx) return {};

    unsigned char derived[32]; // 32 bytes for both key and IV
    if(!PKCS5_PBKDF2_HMAC(password, strlen(password), 
      reinterpret_cast<const unsigned char *>(salt), strlen(salt), 
      1000, EVP_sha1(), 32, derived)) 
    {
        EVP_CIPHER_CTX_free(ctx);
        return {};
    }

    unsigned char key[16]; // AES-128 key
    unsigned char iv[16];

    memcpy(key, derived, 16);
    memcpy(iv, derived + 16, 16);

    if (EVP_CipherInit(ctx, EVP_aes_128_cbc(), key, iv, true) != 1) 
    {
        EVP_CIPHER_CTX_free(ctx);
        return {};
    }
        
    int len = 0;
    int outLen;
    int blocksize = EVP_CIPHER_CTX_block_size(ctx);
    std::string cipherBuf;
    cipherBuf.resize(str.size() + blocksize);
        
    if (EVP_CipherUpdate(ctx, reinterpret_cast<unsigned char*>(cipherBuf.data()), &outLen, 
        reinterpret_cast<unsigned char*>(str.data()), str.size()) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return {};
    }        
    len += outLen;
    
    if (EVP_CipherFinal(ctx, reinterpret_cast<unsigned char*>(cipherBuf.data() + len), &outLen) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return {};     
    }        
    len += outLen;
     
    cipherBuf.resize(len);
    EVP_CIPHER_CTX_free(ctx);

    std::string base64_enc = stringToBase64(cipherBuf); 
    memset(cipherBuf.data(), 0, cipherBuf.size());
    return base64_enc;
}

int main(int argc, char* argv[])
{
    const char* password = "password123";
    const char* salt = "salt123";

    std::string enc = encrypt("test123", password, salt);
    std::cout << "Encrypted: " << enc << std::endl;

    size_t saltLen = strlen(salt);
    // 派生32字节数据用于Key和IV
    CryptoPP::SecByteBlock derived(32);
    CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA1> pbkdf;
    pbkdf.DeriveKey(derived, derived.size(), 0,
                    reinterpret_cast<const CryptoPP::byte*>(password), strlen(password),
                    reinterpret_cast<const CryptoPP::byte*>(salt), saltLen, 1000);

    CryptoPP::SecByteBlock key(16), iv(16);
    std::memcpy(key.data(), derived.data(), key.size());
    std::memcpy(iv.data(), derived.data() + 16, iv.size());

    // 打印Key和IV验证
    CryptoPP::HexEncoder encoder;
    std::string key_hex, iv_hex;
    encoder.Attach(new CryptoPP::StringSink(key_hex));
    encoder.Put(key.data(), key.size());
    encoder.MessageEnd();

    encoder.Detach(new CryptoPP::StringSink(iv_hex));
    encoder.Put(iv.data(), iv.size());
    encoder.MessageEnd();

    std::cout << "Key: " << key_hex << std::endl;
    std::cout << "IV: " << iv_hex << std::endl;

    // Base64解码密文
    std::string decoded_cipher;
    CryptoPP::Base64Decoder decoder;
    decoder.Attach(new CryptoPP::StringSink(decoded_cipher));
    decoder.Put(reinterpret_cast<const unsigned char*>(enc.data()), enc.size());
    decoder.MessageEnd();

    // 解密
    std::string dec;
    CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption cbcDecryption(key, key.size(), iv);
    CryptoPP::StreamTransformationFilter stf(cbcDecryption, new CryptoPP::StringSink(dec));
    stf.Put(reinterpret_cast<const unsigned char*>(decoded_cipher.data()), decoded_cipher.size());
    stf.MessageEnd();

    std::cout << "Decrypted: " << dec << std::endl;
    return 0;
}

内容的提问来源于stack exchange,提问作者Jabu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:44:54