使用以下函数对其进行编码和解码后,我没有获得相同的会话密钥
我认为你已经接近你所需要的了。还有一个改进你做事方式的机会。我将向您展示改进的方法,您也可以将其应用于现有方法。
改进的方式简单地使用FixedMaxPlaintextLength
, CiphertextLength
和一些朋友来确定尺寸。它还使用了来自集成加密方案 (IES) https://en.wikipedia.org/wiki/Integrated_Encryption_Scheme.
首先,传输原始种子字节,而不是{key, iv}
一对。然后,当你需要{key, iv}
对,您可以从种子字节中导出所需的字节。您的派生应该包括使用标签和版本号。
其次,悬而未决的问题:您传输多少字节作为种子字节。那个答案是FixedMaxPlaintextLength()
or MaxPreimage()
(我不记得是哪一个了)。这是该方案下可以加密的明文的大小,它取决于模数大小和填充方案等因素。
下面的很多代码都在RSA 加密方案 http://cryptopp.com/wiki/RSA_Encryption_Schemes以及 Crypto++ wiki 上的其他地方。但你并不需要去拜访他们,因为你仍在学习一些技术。
下面生成一个随机种子并用公钥对其进行加密。
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));
IF您验证您的数据,然后您可以使用以下内容派生 HMAC 密钥。但一般来说,您可能应该使用认证加密 http://www.cryptopp.com/wiki/Authenticated_Encryption操作模式:
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 中添加 http://cryptopp.com/docs/ref/class_h_k_d_f.html。如果没有,那就抢hkdf.h
from 戴伟的 GitHub http://github.com/weidai11/cryptopp(仅包含标题)。通过从具有独特标签的基础种子中派生,您正在使用一种称为独立派生的技术。
您添加标签和版本信息以避免出现如中讨论的间隙攻击和修复WinZip加密方案 http://homes.cs.washington.edu/~yoshi/papers/WinZip/winzip.pdf。另外,利用整个FixedMaxPlaintextLength
回避一些与消息长度相关的加密攻击。
您可能还想看看集成加密方案 (IES) https://en.wikipedia.org/wiki/Integrated_Encryption_Scheme。我们基本上从 IES 中取消了密钥封装机制 (KEM)。还有一个可以解除的数据封装机制(DEM)。
如果你打算借用KEM和DEM,那么你也可以使用该方案。为此,请参阅 Crypto++ wiki 上的以下内容:
- 椭圆曲线综合加密方案 http://cryptopp.com/wiki/Elliptic_Curve_Integrated_Encryption_Scheme
- 离散对数积分加密方案 http://cryptopp.com/wiki/Discrete_Logarithm_Integrated_Encryption_Scheme
如果您使用集成加密方案之一,那么您正在改变潜在的数学问题。 RSA 是整数分解(IF),而 IES 是 Diffie-Hellman 和离散对数 (FF).
使用集成加密方案是一个不错的选择。它是IND-CCA2 http://en.wikipedia.org/wiki/Ciphertext_indistinguishability,这是一个非常强烈的安全概念。我相信它比您原来的方案具有更好的安全特性。