我尝试使用公钥加密邮件,然后在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;
}
我的问题:
使用https://stackoverflow.com/users/608639/jww中Unable 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的单元测试中使用此代码,一切正常。
答案 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的单元测试中使用此代码,一切正常。
一句谨慎的提醒...... GlobalRNG
是Test
命名空间的一部分。它在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)
),因此结果是试验和错误的显着加速。在我们发布下一版本的库之前,我们切换到了平方法。