在 RSA 下解码有效负载后未获得相同的会话密钥



使用以下使用加密++库的函数进行编码和解码后,我没有获得相同的会话密钥:

CryptoPP::RSA::PrivateKey RSA_master_privKey;
CryptoPP::RSA::PublicKey  RSA_master_pubKey;
std::string generate_Master_Keys()
{
    std::string rsaParams;
    try {
        CryptoPP::InvertibleRSAFunction parameters;
        RSA_master_privKey = CryptoPP::RSA::PrivateKey(parameters);
        RSA_master_pubKey = CryptoPP::RSA::PublicKey(parameters);
    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        b_success = false;
    }
    return rsaParams;
}
PAES_KEY_WITH_IV create_session_key(void)
{
    CryptoPP::AutoSeededX917RNG<CryptoPP::AES> rng;
    PAES_KEY_WITH_IV  aes_info = new AES_KEY_WITH_IV;
    try {
        aes_info->key.resize(CryptoPP::AES::DEFAULT_KEYLENGTH);
        rng.GenerateBlock(aes_info->key, aes_info->key.size());
        aes_info->iv.resize(CryptoPP::AES::BLOCKSIZE);
        rng.GenerateBlock(&aes_info->iv[0], aes_info->iv.size());
    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        b_success = false;
    }
    return (aes_info);
}
std::string encrypt_session_key(PAES_KEY_WITH_IV pKey)
{
    std::string ciphered;
    CryptoPP::SecByteBlock block(pKey->key.size());
    try {
        CryptoPP::RSAES< CryptoPP::OAEP<CryptoPP::SHA> >::Encryptor enc(RSA_master_pubKey);
        enc.Encrypt(rng, pKey->key, pKey->key.size(), block);
        ciphered.assign((char *)block.BytePtr(), 192);
    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        b_success = false;
    }
    return ciphered;
}
PAES_KEY_WITH_IV decrypt_session_key(std::string & ciphered)
{
    CryptoPP::SecByteBlock rec(ciphered.size());
    CryptoPP::SecByteBlock block((const byte *)ciphered.data(), ciphered.size());
    PAES_KEY_WITH_IV pKey = new AES_KEY_WITH_IV;
    try {
        CryptoPP::RSAES< CryptoPP::OAEP<CryptoPP::SHA> >::Decryptor dec(RSA_master_privKey);
        dec.Decrypt(rng, block, block.size(), rec);
        pKey->key = rec;
    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        b_success = false;
    }
    return pKey;
}

192 字节的尾部与原始会话密钥的字节不匹配。

有人可以帮助我吗?

提前谢谢。

使用以下函数对其进行编码和解码后,我没有获得相同的会话密钥

我认为你已经接近你需要的东西了。你做事的方式也有机会改进。我将向您展示改进的方法,您也可以将其应用于现有方法。

改进后的方法只是使用FixedMaxPlaintextLengthCiphertextLength和一些朋友来确定大小。它还使用集成加密方案 (IES( 中的技术。

首先,传输原始种子字节,而不是{key, iv}对。然后,当您需要{key, iv}对时,您可以从种子字节派生所需的字节。派生应包括用法标签和版本号。

其次,悬而未决的问题:您以种子字节的形式传输多少字节。这个答案是FixedMaxPlaintextLength()MaxPreimage()(我不记得是哪个(。这是可以在方案下加密的明文的大小,这取决于模数大小和填充方案等因素。

下面的许多代码在RSA加密方案和Crypto++维基的其他地方进行了讨论。但是,您需要访问它们并不明显,因为您仍在学习一些技术。


下面生成一个随机种子,并在公钥下对其进行加密。

RSA_master_pubKey = RSA::PublicKey(parameters);
RSAES< OAEP<SHA> >::Encryptor enc(RSA_master_pubKey);
SecByteBlock seed(enc.FixedMaxPlaintextLength());
AutoSeededX917RNG<AES> rng;
rng.GenerateBlock(seed, seed.size());
SecByteBlock block(enc.CiphertextLength(seed.size())));    
size_t req = enc.Encrypt(rng, seed, seed.size(), block);
block.resize(req);
// Transport block to peer as session seed

当对等方收到加密的种子块时,他们必须对其进行解密。这是如何做到的。

// Received from peer
SecByteBlock block(...);
RSAES< OAEP<SHA> >::Decryptor dec(RSA_master_privKey);
size_t req = dec.MaxPlaintextLength(block.size());
SecByteBlock seed(req);
DecodingResult result = dec.Decrypt(rng, block, block.size(), seed);
seed.resize(result.isValidCoding ? result.messageLength : 0);

如果result.isValidCoding返回false,您甚至可以抛出异常:

DecodingResult result = dec.Decrypt(rng, block, block.size(), seed);
if (!result.isValidCoding)
    throw Exception(OTHER_ERROR, "Failed to decrypt seed bytes");
seed.resize(result.messageLength);

当您想使用 AES 加密或解密时,您需要派生一个密钥、iv 和可能的 hmac 密钥(您是否在对数据进行身份验证?

// Random seed from above
SecByteBlock seed;
HKDF<SHA256> kdf;
SecByteBlock aesKey(AES::DEFAULT_KEYLENGTH);
SecByteBlock aesIV(AES::BLOCKSIZE);   
const byte aesLabel[] = "AES encryption key, version 1";
kdf.Derive(aesKey, aesKey.size(), seed, seed.size(), NULL, 0, aesLabel, COUNTOF(aesLabel));
const byte ivLabel[] = "AES initialization vector, version 1";
kdf.Derive(aesIV, aesIV.size(), seed, seed.size(), NULL, 0, ivLabel, COUNTOF(ivLabel));

如果对数据进行身份验证,则可以使用以下方法派生 HMAC 密钥。但一般来说,您可能应该使用经过身份验证的加密操作模式:

const byte hmacLabel[] = "HMAC authentication key, version 1";
kdf.Derive(hmacKey, hmacKey.size(), seed, seed.size(), NULL, 0, hmacLabel, COUNTOF(hmacLabel));

HKDF在5.6.3或5.6.4中增加。如果你没有它,那就从Wei Dai的GitHub上获取hkdf.h(它的标题(。通过从具有唯一标签的基础种子派生,您正在使用一种称为独立派生的技术。

添加标签和版本信息以避免出现漏洞,如攻击和修复 WinZip 加密方案中所述。此外,使用整个FixedMaxPlaintextLength旁敲一些与消息长度相关的加密攻击。


您可能还需要查看集成加密方案 (IES(。我们基本上从IES中解除了密钥封装机制(KEM(。也可以解除数据封装机制(DEM(。

如果您打算借用 KEM 和 DEM,那么您也可以使用该计划。为此,请参阅Crypto++维基上的以下内容:

  • 椭圆曲线集成加密方案
  • 离散对数集成加密方案

如果使用集成加密方案之一,则正在更改基础数学问题。RSA是整数分解(IF(,而IES是Diffie-Hellman和离散对数(FF(。

使用集成加密方案是一个不错的选择。它的IND-CCA2,这是一个非常强烈的安全概念。我相信它比您原来的方案具有更好的安全特性。

最新更新