Прежде чем мы начнем здесь, у меня есть сервер и клиент. Я хочу отправить зашифрованную строку клиенту, которая содержит открытый статический ключ сервера Диффи-Хеллмана и открытый эфемерный ключ. Для этого я использую закрытый RSA-ключ сервера для отправки зашифрованной строки, а клиент расшифровывает с помощью открытого RSA-ключа сервера.
Теперь причина, почему мне нужно сделать это таким образом, заключается в том, что сервер является единственным с парой открытого / закрытого ключа. Это нормально, поскольку шифрование одной парой ключей по-прежнему отсекает одну сторону атаки 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
Код, который в настоящее время не закомментирован в разделе Диффи-Хеллмана, был получен из ВОТ. Проблема с закомментированным кодом заключается в том, что когда клиент получает строку в шестнадцатеричном формате, он теряет данные и не может договориться о совместном секрете. Но это проходит через сокет.
Пример этого может быть показан:
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 и выполняйте шифрование RSA, используя хорошую схему заполнения, такую как OAEP, используя шифратор и фильтр, как показано в вики CryptoPP:
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 попытается разложить модуль на множители, что не может быть сделано без знания хотя бы открытого и частного экспонент.
Теперь я должен признаться, что я не проверял шифрование / дешифрование с использованием закрытого ключа и RSA OAEP, если мой последний фрагмент кода не работает (возможно,
RSAES_OAEP_SHA_Decryptor не будет в порядке с закрытым ключом, простые числа которого не определены … Я печатаю вслепую, без компилятора), тогда вам придется создавать свой собственный RSA, как вы, похоже, пытаетесь сейчас. Если вам нужна дополнительная помощь, пожалуйста, прокомментируйте ниже, и завтра я попробую запустить мой компилятор и посмотреть, как он работает.
Но имейте в виду, что учебник по RSA очень слабый, поэтому вам следует дополнять данные с помощью OAEP.