[英]InvertableRSAFunction Fails on encrypting Diffie-Hellman Keys as const byte*
在这里开始之前,我有一个服务器和一个客户端。 我希望将包含服务器的Diffie-Hellman公共静态密钥和公共临时密钥的加密字符串发送给客户端。 为此,我使用服务器的私有RSA密钥发送加密的字符串,客户端使用服务器的公共RSA密钥解密。
现在,我需要这样做的原因是因为服务器是唯一具有公钥/私钥对的服务器。 这很好,因为使用一对密钥进行加密仍然可以切断针对Diffie-Hellman的MITM攻击的一侧,对于我的要求,这是可以的。
将静态密钥和临时密钥转换为十六进制编码的字符串并通过套接字发送,这给我带来了私钥加密阶段的问题。
我的服务器正在做:
DH2 dhA(dh);
SecByteBlock sprivA(dhA.StaticPrivateKeyLength()), spubA(
dhA.StaticPublicKeyLength());
SecByteBlock eprivA(dhA.EphemeralPrivateKeyLength()), epubA(
dhA.EphemeralPublicKeyLength());
dhA.GenerateStaticKeyPair(rnd, sprivA, spubA);
dhA.GenerateEphemeralKeyPair(rnd, eprivA, epubA);
string sendBuf, recvBuf;
string saEncoded, eaEncoded, encoding;
cout << "spubA: " << (char*) spubA.data() << endl << "epubA: "
<< (char*) epubA.data() << endl;
SecByteBlock nil;
nil.CleanNew(HMAC< SHA256 >::DEFAULT_KEYLENGTH);
HMAC< SHA256 > hmac;
hmac.SetKey(nil.data(), nil.size());
HashFilter filter(hmac, new HexEncoder(new StringSink(encoding)));
filter.Put(spubA.data(), spubA.size());
filter.MessageEnd();
saEncoded = encoding;
encoding = "";
filter.Put(epubA.data(), epubA.size());
filter.MessageEnd();
eaEncoded = encoding;
encoding = "";
// StringSource saSource(spubA, sizeof(spubA), true,
// new HexEncoder(new StringSink(saEncoded)));
//
// StringSource eaSource(epubA, sizeof(epubA), true,
// new HexEncoder(new StringSink(eaEncoded)));
//
sendBuf = saEncoded + " " + eaEncoded;
cout << "Send Buffer: " << sendBuf << endl;
SendMsg(sendBuf, tdata);
其中, SendMsg()
包含加密过程。
此时失败:
void SendMsg( string sendBuf, struct ThreadData * tdata )
{
AutoSeededRandomPool rng;
Integer m, c, r;
stringstream ss;
try
{
// Encode the message as an Integer
m = Integer((const byte *) sendBuf.c_str(), sendBuf.size());
//Encrypt
c = tdata->privateKey.CalculateInverse(rng, m); //HERE!
带有错误信息:
InvertibleRSAFunction: computational error during private key operation
Diffie-Hellman部分目前未注释掉的代码是从HERE获得的。 注释代码的问题在于,当客户端收到十六进制编码的字符串时,它丢失了数据并且无法就共享机密达成共识。 但是它确实通过套接字。
可以显示一个示例:
Server:
spubA: &a�|՜D2�tu�cJ����B�R�8�*i�x?N���p��Q�����K��+O �"��P:k�d|3�����6Z
epubA: 4v������M�E�`l�K��[dN�|Q^r�-ż�����A~D�>4$�9���"v�*:Y��s�O���J��ow�M�߬�C�9n�;���Z�D�6lp�V��oowZ��WSv��",��A3��XL��8��
Send Buffer: 2661DC7CD59C4432AF747584634AF69BE60298429C52C738 3476CBCCFAA3B0A14DBE45E3606CC84B171DAC1CCE5B644E
Client:
Recovered: 2661DC7CD59C4432AF747584634AF69BE60298429C52C738 3476CBCCFAA3B0A14DBE45E3606CC84B171DAC1CCE5B644E
SA: 2661DC7CD59C4432AF747584634AF69BE60298429C52C738
EA: 3476CBCCFAA3B0A14DBE45E3606CC84B171DAC1CCE5B644E
Decoded SA: &a�|՜D2�tu�cJ����B�R�8
Decoded EA: 4v������M�E�`l�K��[dN
关于此实例,我尝试在客户端执行以下操作:
// Get spubA and epubA from server
recovered = recoverMsg(serverKey, sockServer);
//Calculate shared secret.
string sa, ea;
ss.str(recovered);
ss >> sa >> ea;
ss.str("");
ss.clear();
cout << "SA: " << sa << endl << "EA: " << ea << endl;
string decodedSA, decodedEA;
StringSource decodeSA(sa, true,
new HexDecoder(new StringSink(decodedSA)));
StringSource decodeEA(ea, true,
new HexDecoder(new StringSink(decodedEA)));
cout << "Decoded SA: " << decodedSA << endl;
cout << "Decoded EA: " << decodedEA << endl;
SecByteBlock spubA((const byte*) decodedSA.data(), decodedSA.size());
if ( spubA.size() < dhB.StaticPublicKeyLength() ) spubA.CleanGrow(
dhB.StaticPublicKeyLength());
else spubA.resize(dhB.StaticPublicKeyLength());
SecByteBlock epubA((const byte*) decodedEA.data(), decodedEA.size());
if ( epubA.size() < dhB.EphemeralPublicKeyLength() ) epubA.CleanGrow(
dhB.EphemeralPublicKeyLength());
else epubA.resize(dhB.EphemeralPublicKeyLength());
但是我仍然得到相同的结果。
有人知道我如何使用服务器的私钥对此加密并正确地通过套接字发送它吗?
实际上,我通过对所有内容进行Base64编码来解决所有问题,然后再通过网络发送。 我还在每次发送后添加了一个接收,并在每次接收后添加了一个发送,以确保服务器和客户端正确同步在一起,以避免出现时序问题。 不需要所有的“ HexEncoding”废话。 只需将其转换为字符串即可,然后将其发送到这些函数中,我们就可以开始了。
string RecoverMsg( struct ThreadData * tdata )
{
try
{
Integer c = 0, r = 0, m = 0;
size_t req = 0, bytes = 0;
AutoSeededRandomPool rng;
string recovered = "", ack = "", decodedCipher = "";
byte byteBuf[ 2000 ];
memset(byteBuf, 0, sizeof(byteBuf));
// Retrieve message from socket
cout << "Waiting to receive a message from client " << tdata->tid << endl;
bytes = tdata->sockSource.Receive(byteBuf, sizeof(byteBuf));
cout << "Bytes Read: " << bytes << endl;
cout << "Encoded Cipher Received: " << byteBuf << endl;
decodedCipher;
StringSource(byteBuf, sizeof(byteBuf), true,
new Base64Decoder(new StringSink(decodedCipher)));
c = Integer(decodedCipher.c_str());
// Decrypt
r = tdata->privateKey.CalculateInverse(rng, c);
cout << "r: " << r << endl;
// Round trip the message
req = r.MinEncodedSize();
recovered.resize(req);
r.Encode((byte *) recovered.data(), recovered.size());
cout << "Recovered: " << recovered << endl;
ack = "ACK";
bytes = tdata->sockSource.Send((const byte*) ack.c_str(), ack.size());
return recovered;
}
catch ( Exception& e )
{
cerr << "caught Exception..." << endl;
cerr << e.what() << endl;
tdata->sockSource.ShutDown(SHUT_RDWR);
}
}
void SendMsg( string sendBuf, struct ThreadData * tdata )
{
try
{
AutoSeededRandomPool rng;
stringstream ss("");
string cipher = "", encodedCipher = "";
Integer m = 0, c = 0, r = 0;
size_t bytes = 0;
byte ack[ 10 ];
memset(ack, 0, sizeof(ack));
// Treat the message as a big endian array
m = Integer((const byte *) sendBuf.c_str(), sendBuf.size());
cout << "m: " << m << endl;
// Encrypt
c = tdata->privateKey.CalculateInverse(rng, m);
ss << c;
cipher = ss.str();
ss.str("");
ss.clear();
// Base64 encode the cipher
encodedCipher;
StringSource(cipher, cipher.size(),
new Base64Encoder(new StringSink(encodedCipher)));
cout << "Encoded Cipher Sent: " << encodedCipher << endl;
// Send the cipher
bytes = tdata->sockSource.Send((const byte*) encodedCipher.c_str(),
encodedCipher.size());
cout << "Bytes Written: " << bytes << endl;
bytes = tdata->sockSource.Receive(ack, sizeof(ack));
}
catch ( Exception& e )
{
cerr << "caught Exception..." << endl;
cerr << e.what() << endl;
tdata->sockSource.ShutDown(SHUT_RDWR);
}
}
好吧,关于HexEncoding之后丢失的数据,这肯定是由nn(spubA)
引起的,因为sizeof()
返回的是指向spubA
的指针的大小,而不是字符串的大小。 要获取字符串的长度,您可以使用spubA.length()
函数,或者像使用客户端一样简单地使用最佳方式(使用CryptoPP,IMHO)来做到这一点,而无需指定大小即可:
StringSource saSource(spubA, true,
new HexEncoder(new StringSink(saEncoded)));
现在,关于其余的代码,您似乎正在尝试执行自己的教科书RSA,因为您首先将缓冲区转换为整数,所以请不要准备自己的RSA! 注定要失败,RSA教科书薄弱!
因此,利用CryptoPP的功能并使用良好的填充方案(例如OAEP)通过使用加密器和过滤器来执行RSA加密,如CryptoPP的wiki所示:
RSAES_OAEP_SHA_Encryptor enc(key);
StringSource(inputString, true,
new PK_EncryptorFilter(rng, enc,
new HexEncoder(
new StringSink(output), false)));
cout << output << endl;
现在,您需要注意的一点是使用正确的密钥,因为您要使用私钥进行加密,因此必须按以下方式初始化密钥:
RSA::PublicKey key;
key.Initialize(privateKey.GetModulus(), privateKey.GetPrivateExponent();
就是这样,应该已经可以使用了。 要解密,只需使用:
RSA::PrivateKey clientkey;
clientkey.SetModulus(publicKey.GetModulus());
clientkey.SetPrivateExponent(publicKey.GetPublicExponent());
RSAES_OAEP_SHA_Decryptor d(privateKey);
StringSource(decodedInput, true,
new PK_DecryptorFilter(rng, d,
new HexEncoder(
new StringSink(output), false //lowercase
)));
cout << output << endl;
但是,有一个问题:使用私钥加密后,请勿使用带有私钥的Initialize进行解密,因为Initialize会尝试分解因数模数,而它至少在不知道公共和私有指数的情况下是无法做到的。
现在,我必须承认,如果我最新的代码片段失败(我可能尚未使用未定义素数的私钥对RSAES_OAEP_SHA_Decryptor表示怀疑,那么我尚未测试过使用私钥和RSA OAEP进行加密/解密的操作...我盲目输入,没有编译器),那么您就必须像现在尝试的那样酿造自己的RSA。 如果您需要更多帮助,请在下方评论,明天我将尝试生成我的编译器,并查看其工作原理。
但是请注意,RSA教科书非常薄弱,因此您应该使用OAEP填充数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.