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

C++ OpenSSL AES加密后,Python/PHP解密开头乱码问题求助

问题描述

使用C++的OpenSSL实现AES加密后,用Python的Crypto.Cipher AES库或PHP的OpenSSL解密时,解密结果开头出现随机二进制乱码。例如原明文:{"pass":"azertyu","opt":...},解密后变成:"Ž{mÂ┬X▒ Íł.Q>4","opt":...},推测是填充问题但无法定位。

Python解密代码
import binascii
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

def decrypt(ciphertext_hex):
    key = b"t*H3_B3$t/k$%evr"
    data = binascii.unhexlify(ciphertext_hex)
    iv = data[:AES.block_size]
    data = data[AES.block_size:]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted = unpad(cipher.decrypt(data), AES.block_size)
    print(decrypted)
    # return decrypted.decode("utf-8", errors="replace")
    return decrypted.decode("utf-8")
C++加密代码
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstring>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>

std::string binToHex(const std::string &input) 
{
    std::ostringstream hexStream;
    hexStream << std::hex << std::setfill('0');

    for (unsigned char c : input) 
    {
        hexStream << std::setw(2) << static_cast<int>(c);
    }

    return hexStream.str();
}

std::string crypt(const std::string &data, const std::string &key)
{
    const unsigned char *keyBytes = reinterpret_cast<const unsigned char *>(key.c_str());
    const unsigned char *dataBytes = reinterpret_cast<const unsigned char *>(data.c_str());
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();

    AES_KEY aesKey;
    if (AES_set_encrypt_key(keyBytes, 128, &aesKey) < 0) {
        std::cout << "key";
        return "";
    }
    unsigned char iv[AES_BLOCK_SIZE];
    if (RAND_bytes(iv, AES_BLOCK_SIZE) != 1)
    {
        std::cout << "rand bytes";
        return "";
    }
    
    size_t paddedLen = (data.length() + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE;
    unsigned char *paddedData = new unsigned char[paddedLen];
    std::memcpy(paddedData, dataBytes, data.length());
    memset(paddedData + data.length(), paddedLen - data.length(), paddedLen - data.length());
    unsigned char ciphertext[paddedLen];
    AES_cbc_encrypt(paddedData, ciphertext, paddedLen, &aesKey, iv, AES_ENCRYPT);
    std::string output(reinterpret_cast<char *>(iv), AES_BLOCK_SIZE);
    output.append(reinterpret_cast<char *>(ciphertext), paddedLen);
    
    delete[] paddedData;
    
    return binToHex(output);
}
问题原因与解决方法

核心问题:填充规则不匹配

C++代码手动实现的填充逻辑和Pythonunpad函数默认的PKCS#7填充不兼容:

  • 当明文长度刚好是AES块大小(16字节)的整数倍时,你的C++代码没有添加完整的一个块填充(PKCS#7要求此时必须填充16个值为0x10的字节)。
  • Python的unpad严格遵循PKCS#7规则,填充不匹配会导致解密时块偏移错误,最终表现为开头乱码。

修复步骤

  1. 修正C++的填充逻辑,严格实现PKCS#7:

    // 替换原填充相关代码
    size_t paddingLen = AES_BLOCK_SIZE - (data.length() % AES_BLOCK_SIZE);
    size_t paddedLen = data.length() + paddingLen;
    unsigned char *paddedData = new unsigned char[paddedLen];
    std::memcpy(paddedData, dataBytes, data.length());
    memset(paddedData + data.length(), paddingLen, paddingLen);
    

    该逻辑会自动处理两种情况:

    • 明文长度不是块倍数:填充paddingLen个值为paddingLen的字节
    • 明文长度是块倍数:填充16个0x10字节(符合PKCS#7要求)
  2. 验证Python端代码
    确保使用Crypto.Util.Padding.unpad(默认PKCS#7),无需修改,修正C++填充后即可正常解密。

  3. 额外注意点

    • C++中如果明文是JSON等不含\0的字符串,建议用data.data()替代data.c_str()获取字节(c_str()会自动添加\0终止符,可能导致明文长度错误)。
    • iv与密文的拼接、拆分逻辑是正确的,无需调整。

内容的提问来源于stack exchange,提问作者Ключник

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:17:44