在RSA解码有效载荷后,不会得到相同的会话密钥 [英] Not getting same session key after decoding payload under RSA

查看:155
本文介绍了在RSA解码有效载荷后,不会得到相同的会话密钥的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  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块(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;
}
返回加密;
}

PAES_KEY_WITH_IV decrypt_session_key(std :: string& ciphered)
{
CryptoPP :: SecByteBlock rec(ciphered.size());
CryptoPP :: SecByteBlock块((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个字节的拖尾未与原始会话密钥的字节匹配。 p>

有人可以帮助我吗?



提前感谢

解决方案


我使用以下函数编码和解码后,我没有得到相同的会话密钥


我认为你是接近你所需要的。还有一个改善方式的机会。我会告诉你改进的方式,你也可以将它应用到现有的方法。



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



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



其次,打开的问题:您作为种子字节传输多少个字节。那个答案是 FixedMaxPlaintextLength() MaxPreimage()(我不记得哪个)。这是可以根据方案加密的明文的大小,它取决于像模数大小和填充方案之类的东西。



下面很多代码是在 RSA加密方案以及Crypto ++ wiki上的其他地方进行了讨论。但是,由于您还在学习一些技巧,因此您不需要访问它们。






以下生成随机种子,并在公钥下进行加密。

  RSA_master_pubKey = RSA :: PublicKey(parameters); 
RSAES< OAEP< SHA> > :: Encryptor enc(RSA_master_pubKey);
SecByteBlock种子(enc.FixedMaxPlaintextLength());

AutoSeededX917RNG< AES> RNG;
rng.GenerateBlock(seed,seed.size());

SecByteBlock块(enc.CiphertextLength(seed.size())));
size_t req = enc.Encrypt(rng,seed,seed.size(),block);
block.resize(req);

//传输块到对等体作为会话种子






当对等体收到加密的种子块时,它们必须对其进行解密。以下是如何做到这一点。

  //从对等体
接收SecByteBlock块(...);

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 ,block,block.size(),seed); 
if(!result.isValidCoding)
throw异常(OTHER_ERROR,解密种子字节失败);

seed.resize(result.messageLength);






当您要使用AES进行加密或解密时,你需要导出一个键,iv和可能的一个hmac键(你在认证数据?)。

  //随机种子从
SecByteBlock种子;

HKDF< SHA256> KDF;
SecByteBlock aesKey(AES :: DEFAULT_KEYLENGTH);
SecByteBlock aesIV(AES :: BLOCKSIZE);

const byte aesLabel [] =AES加密密钥,版本1;
kdf.Derive(aesKey,aesKey.size(),seed,seed.size(),NULL,0,aesLabel,COUNTOF(aesLabel));

const byte ivLabel [] =AES初始化向量,版本1;
kdf.Derive(aesIV,aesIV.size(),seed,seed.size(),NULL,0,ivLabel,COUNTOF(ivLabel));

如果验证您的数据,则可以导出HMAC密钥下列。但一般来说,您应该使用验证加密操作模式:

  const byte hmacLabel [] =HMAC认证密钥,版本1; 
kdf.Derive(hmacKey,hmacKey.size(),seed,seed.size(),NULL,0,hmacLabel,COUNTOF(hmacLabel));

HKDF被添加在5.6.3或5.6.4 。如果没有,请从获取 hkdf.h 魏代的GitHub (仅限标题)。通过使用独特的标签从基础种子派生,您正在使用一种称为独立派生的技术。



添加标签和版本信息,以避免在攻击和修复WinZip加密方案。此外,使用整个 FixedMaxPlaintextLength 侧步骤与消息长度相关的一些加密攻击。






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



如果你要借用KEM和DEM,那么你也可以使用该方案。为此,请参阅Crypto ++ wiki上的以下内容:





如果使用集成加密方案之一,那么您正在更改潜在的数学问题。 RSA是整数因式分解( IF ),而IES是Diffie-Hellman和离散日志( FF )。 / p>

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


I am not getting same session key after encoding and decoding it using below functions which uses crypto++ library:

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;
}

Tailing of 192 bytes are not getting matched with original session key's bytes.

Can some one help me on this ?

Thanks in advance.

解决方案

I am not getting same session key after encoding and decoding it using below functions

I think you are close to what you need. There's also an opportunity for improvement in the way you are doing it. I'll show you the improved way, and you can apply it to the existing method as well.

The improved way simply uses FixedMaxPlaintextLength, CiphertextLength and some friends to determine sizes. It also uses a technique from Integrated Encryption Schemes (IES).

First, transport the raw seed bytes, and not the {key, iv} pair. Then, when you need the {key, iv} pair, you derive the bytes you need from the seed bytes. Your derivation should include a usage label and a version number.

Second, the open question: how many bytes do you transport as seed bytes. That answer is FixedMaxPlaintextLength() or MaxPreimage() (I don't recall which). That's the size of the plaintext that can be encrypted under the scheme, and it depends on things like the modulus size and the padding scheme.

A lot of the code below is discussed at RSA Encryption Schemes and other places on the Crypto++ wiki. But its not readily apparent you need to visit them because you are still learning some of the techniques.


The following generates a random seed and encrypts it under the public key.

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


When the peer receives the encrypted seed block, they must decrypt it. Here's how to do it.

// 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);

You could even thrown an exception if result.isValidCoding returns 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);


When you want to encrypt or decrypt with AES, you need to derive a key, iv and possibly an hmac key (are you authenticating the data?).

// 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));

IF you authenticate your data, then you can derive an HMAC key with the following. But generally speaking, you should probably use an Authenticated Encryption mode of operation:

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

HKDF was added at 5.6.3 or 5.6.4. If you don't have it, then grab hkdf.h from Wei Dai's GitHub (its header-only). By deriving from a base seed with unique labels, you are using a technique called independent derivation.

You add the labels and the version information to avoid gaps like discussed in Attacking and Repairing the WinZip Encryption Scheme. Also, using the entire FixedMaxPlaintextLength side steps some cryptographic attacks related to message length.


You might also want to look at Integrated Encryption Schemes (IES). We basically lifted the Key Encapsulation Mechanism (KEM) from IES. There's a Data Encapsulation Mechanism (DEM) that could be lifted, too.

If you are going to borrow the KEM and the DEM, then you may as well use the scheme. For that, see the following on the Crypto++ wiki:

If you use one of the Integrated Encryption Schemes, then you are changing the underlying mathematical problem. RSA is Integer Factorization (IF), while IES is Diffie-Hellman and Discrete Logs (FF).

Using an Integrated Encryption Scheme is a good choice. Its IND-CCA2, which is a very strong notion of security. I believe it has better security properties than your original scheme.

这篇关于在RSA解码有效载荷后,不会得到相同的会话密钥的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆