RSA-CBC使用C++对其解密

3yhwsihp  于 2023-01-28  发布在  其他
关注(0)|答案(1)|浏览(198)

我遇到了解密AES-CBC加密字符串的问题。我有解密该字符串的JS代码,但我需要在C++中完成。密钥是SHA512散列字符串,消息是Base64字符串。解密的JS代码:

CryptoJS.algo.AES.keySize = 32,
 CryptoJS.algo.EvpKDF.cfg.iterations = 10000,
  CryptoJS.algo.EvpKDF.cfg.keySize = 32;
var r = CryptoJS.AES.decrypt(message, key.toString());

我的C++代码不工作
x一个一个一个一个x一个一个二个x
也许我应该使用OpenSSL?

flseospp

flseospp1#

由发送的CryptoJS代码生成的密文无法由任何AES兼容库解密。这是由于以下行

CryptoJS.algo.AES.keySize = 32

其定义了用于密钥导出的32个字= 32 * 4 = 128字节的密钥大小。这不是有效的AES密钥大小,并且根本没有为AES定义导出的轮数(128字节为38轮,参见此处;AES仅定义10、12和14轮,具体取决于密钥大小)。因此,密文不符合AES。它可以使用CryptoJS解密,但不能使用任何符合AES的库解密,另请参阅CryptoJS问题#293。要使生成的密文与AES兼容,必须使用允许的AES密钥大小之一,例如,密钥大小为8个字= 32字节:

CryptoJS.algo.AES.keySize = 8

此外,请注意这行

CryptoJS.algo.EvpKDF.cfg.iterations = 10000

导致与OpenSSL CLI不兼容,默认情况下,OpenSSL CLI在密钥派生中使用迭代计数1(这是此密钥派生弱的原因之一,请参见here)。
顺便说一句,这条线

CryptoJS.algo.EvpKDF.cfg.keySize = 32

完全被处理忽略并且也可以省略。
如果使用有效的AES密钥大小,例如8个字= 32字节:

CryptoJS.algo.AES.keySize = 8, // 8 words = 32 bytes
 CryptoJS.algo.EvpKDF.cfg.iterations = 10000,
  CryptoJS.algo.EvpKDF.cfg.keySize = 32;
var r = CryptoJS.AES.decrypt(message, key.toString());

密文可以通过编程来解密。正如注解中已经提到的,如果密钥材料作为字符串传递,CryptoJS将使用OpenSSL专有密钥派生函数EVP_BytesToKey()。这将在加密期间生成8字节salt,并使用salt和密码派生密钥和IV。默认情况下,这些用于在CBC模式下使用PKCS#7填充进行加密。OpenSSL将加密的结果格式化为Salted__的ASCII编码的串联,接着是8个字节的salt,最后是实际的密文,通常是Base64编码。
解密时,必须将盐和密文分离,然后根据盐和口令确定密钥和IV,最后对密文进行解密。
因此,为了解密,需要EVP_BytesToKey()的实现,可以在Crypto文档中找到Cryptohere的这种实现,并且可以用来解密CryptoJS代码的密文(在固定密钥大小问题之后)的代码例如是:

#include "aes.h"
#include "modes.h"
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include "md5.h"
#include "base64.h"
#include "secblock.h"

static int OPENSSL_PKCS5_SALT_LEN = 8;

int OPENSSL_EVP_BytesToKey(CryptoPP::HashTransformation& hash, const unsigned char* salt, const unsigned char* data, int dlen, unsigned int count, unsigned char* key, unsigned int ksize, unsigned char* iv, unsigned int vsize);

int main(int, char**) {

    // Pass data and parameter
    std::string passphrase = "my passphrase";
    std::string encryptedB64 = "U2FsdGVkX18AuE7abdK11z8Cgn3Nc+2cELB1sWIPhAJXBZGhnw45P4l58o33IEiJ8fV4oEid2L8wKXpAntPrAQ=="; // CryptoJS ciphertext for a 32 bytes keysize 
    std::string encrypted;
    int iterationCount = 10000;
    int keySize = 32;

    // Base64 decode
    CryptoPP::StringSource ssB64decodeCt(encryptedB64, true,
        new CryptoPP::Base64Decoder(
            new CryptoPP::StringSink(encrypted)
        )
    );

    // Separate
    std::string salt(encrypted.substr(8, 8));
    std::string ciphertext(encrypted.substr(16));

    // Derive key
    CryptoPP::SecByteBlock key(keySize), iv(16);
    CryptoPP::Weak::MD5 md5;
    OPENSSL_EVP_BytesToKey(md5, (const unsigned char*)salt.data(), (const unsigned char*)passphrase.data(), passphrase.size(), iterationCount, key.data(), key.size(), iv.data(), iv.size());

    // Decryption
    std::string decryptedText;
    CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryption(key.data(), key.size(), iv.data());
    CryptoPP::StringSource ssDecryptCt(
        ciphertext,
        true,
        new CryptoPP::StreamTransformationFilter(
            decryption,
            new CryptoPP::StringSink(decryptedText),
            CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme::PKCS_PADDING
        )
    );

    // Output
    std::cout << decryptedText << std::endl; // The quick brown fox jumps over the lazy dog

    return 0;
}

// from: https://www.cryptopp.com/wiki/OPENSSL_EVP_BytesToKey
int OPENSSL_EVP_BytesToKey(CryptoPP::HashTransformation& hash, const unsigned char* salt, const unsigned char* data, int dlen, unsigned int count, unsigned char* key, unsigned int ksize, unsigned char* iv, unsigned int vsize)
{
    if (data == NULL) return (0);

    unsigned int nkey = ksize;
    unsigned int niv = vsize;
    unsigned int nhash = hash.DigestSize();
    CryptoPP::SecByteBlock digest(nhash);

    unsigned int addmd = 0, i;

    for (;;)
    {
        hash.Restart();

        if (addmd++)
            hash.Update(digest.data(), digest.size());

        hash.Update(data, dlen);

        if (salt != NULL)
            hash.Update(salt, OPENSSL_PKCS5_SALT_LEN);

        hash.TruncatedFinal(digest.data(), digest.size());

        for (i = 1; i < count; i++)
        {
            hash.Restart();
            hash.Update(digest.data(), digest.size());
            hash.TruncatedFinal(digest.data(), digest.size());
        }

        i = 0;
        if (nkey)
        {
            for (;;)
            {
                if (nkey == 0) break;
                if (i == nhash) break;
                if (key != NULL)
                    *(key++) = digest[i];
                nkey--;
                i++;
            }
        }
        if (niv && (i != nhash))
        {
            for (;;)
            {
                if (niv == 0) break;
                if (i == nhash) break;
                if (iv != NULL)
                    *(iv++) = digest[i];
                niv--;
                i++;
            }
        }
        if ((nkey == 0) && (niv == 0)) break;
    }

    return ksize;
}

相关问题