RSA加密和解密期间的RandomNumberGenerator要求?

时间:2017-06-21 15:43:41

标签: c++ rsa public-key-encryption crypto++

我尝试使用公钥加密邮件,然后在shell中使用crypto ++解密密码:

openssl rsautl -encrypt -inkey id_rsa.pub.pem -pubin -in message -out message.enc

openssl rsautl -decrypt -inkey id_rsa.pem -in message.enc -out message.dec

加密/解密在单独的应用程序中完成。我从https://www.cryptopp.com/wiki/RSA_Cryptography的示例开始。 我的代码:

std::string publicEncrypt(std::string const& plain) {
    auto cipher = std::string{};
    CryptoPP::RSAES_OAEP_SHA_Encryptor e(getPublicKey());
    CryptoPP::StringSource(plain, true,
        new CryptoPP::PK_EncryptorFilter(CryptoPP::NullRNG(), e,
                new CryptoPP::StringSink(cipher)));
   return cipher;
}

std::string privateDecrypt(std::string const& cipher) {
    auto decrypted = std::string{};
    CryptoPP::RSAES_OAEP_SHA_Decryptor d(getPrivateKey());
    CryptoPP::StringSource(cipher, true,
        new CryptoPP::PK_DecryptorFilter(CryptoPP::NullRNG(), d,
                new CryptoPP::StringSink(decrypted)));
    return decrypted;
}

我的问题:

  1. 为什么EncryptorFilter / DecryptorFilter需要随机数生成器(RNG)?
  2. RNG必须与加密/解密相同,对吧?那么,如何在进程之间共享?
  3. 使用https://stackoverflow.com/users/608639/jwwUnable to do RSA Encrption/Decryption using Crypto++ (isValidCoding is false)推荐的NullRNG()导致

    std::exception NullRNG: NullRNG should only be passed to functions that don't need to generate random bytes.
    

    我想我从根本上会错过一些东西。感谢提示和建议。

    如果我在使用全局RNG的单元测试中使用此代码,一切正常。

1 个答案:

答案 0 :(得分:2)

  

为什么EncryptorFilter / DecryptorFilter需要随机数生成器(RNG)?

签名和验证类是cryptlib.h中设置的抽象接口。有些密码系统使用它们,有些则没有。一个班级将专攻并且可以放弃使用发电机。有时一个类不需要其中一个操作的生成器。如果不需要,可以使用NullRNG

RSA在公钥操作期间需要RNG的原因是消息填充。填充通常是消息格式化功能的一部分。正如@PuzzlePalace指出的那样,OAEP填充是随机的,而不是确定性的。

RSA在私钥操作期间需要RNG的原因是盲目的。对于RSA和其他类似RSA的方案(如Rabin-Williams),盲法只是乘以随机值来掩盖priavte键的反转以恢复原始值。之后,在签名或解密后,将删除盲值并且操作结果仍然存在。

相关,DSA或ECDSA在私钥操作期间 需要RNG的原因是RFC 6979, Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)。确定性签名不使用随机格式或随机k

公钥和私钥操作需要RNG的另一个原因是对密钥进行验证检查。例如,可以检查密钥以确保特定约束成立,例如其素数或具有特定的雅可比符号。

  

RNG必须与加密/解密相同,对吧?那么,如何在进程之间共享呢?

不,发电机可以不同。唯一的要求是它们产生一个随机流,用于合理定义它意味着什么"随机"。如果不分割太多毛发,就意味着发生器会产生均匀的分布。

您可以在维基上的RandomNumberGenerator找到有关Crypto ++生成器的更多内容。

  

如果我在使用全局RNG的单元测试中使用此代码,一切正常。

一句谨慎的提醒...... GlobalRNGTest命名空间的一部分。它在test.cpp : 115

中定义
NAMESPACE_BEGIN(CryptoPP)
NAMESPACE_BEGIN(Test)

ANONYMOUS_NAMESPACE_BEGIN
OFB_Mode<AES>::Encryption s_globalRNG;
NAMESPACE_END

RandomNumberGenerator & GlobalRNG()
{
    return dynamic_cast<RandomNumberGenerator&>(s_globalRNG);
}

NAMESPACE_END  // Test
NAMESPACE_END  // CryptoPP

GlobalRNG是一个确定性的生成器,它不属于库本身。如果您依赖它,您的代码将无法在该字段中进行编译。

使用维基上RandomNumberGenerator处讨论的其他生成器之一。 AutoSeededRandomPool是个不错的选择。

  

使用https://stackoverflow.com/users/608639/jww中建议的NullRNG()无法执行RSA   使用Crypto ++进行加密/解密(isValidCoding为false)导致

std::exception NullRNG: NullRNG should only be passed to functions that don't need to generate random bytes.

该信息不正确。我需要解决它。感谢。

有趣的是(以一种病态的方式),由于Rabin-Williams的盲法,Crypto ++花了CVE-2015-2141。致盲值需要是二次残差;否则,攻击者可以准备特殊消息以显示私钥。

Evgeny Sidorov的全文可在Breaking the Rabin-Williams digital signature system implementation in the Crypto++ library获得。在修复Sidorov的攻击(来自rw.cpp)之后,新的和改进的反函数是什么样的:

ModularArithmetic modn(m_n), modp(m_p), modq(m_q);
Integer r, rInv;

do
{
    // Do this in a loop for people using small numbers for testing
    r.Randomize(rng, Integer::One(), m_n - Integer::One());
    // Fix for CVE-2015-2141. Thanks to Evgeny Sidorov for reporting.
    // Squaring to satisfy Jacobi requirements suggested by Jean-Pierre Munch.
    r = modn.Square(r);
    rInv = modn.MultiplicativeInverse(r);
} while (rInv.IsZero());

如果您阅读了Sidorov论文的第6部分,他建议生成一个随机r,然后检查r的雅可比符号以确保其为二次残差。如果它不是QR,则尝试新的随机r。分类使用了该方法,但它显示该方案显着减慢,因为随机r满足条件的概率为1/16。

然而,我们知道平方r确保我们在第一次尝试时满足Jacobi,因为 r 2 mod n 总是二次残差。平方/乘法仅需log (exp)(而不是n log (n)),因此结果是试验和错误的显着加速。在我们发布下一版本的库之前,我们切换到了平方法。