InvertableRSAFunction не работает при шифровании ключей Диффи-Хеллмана как константного байта *

Прежде чем мы начнем, у меня есть сервер и клиент. Я хочу отправить клиенту зашифрованную строку, содержащую открытый статический ключ Диффи-Хеллмана сервера и открытый эфемерный ключ. Для этого я использую закрытый ключ 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());

Но я все равно получаю тот же результат.

Кто-нибудь знает, как я могу зашифровать это с помощью закрытого ключа сервера и правильно отправить его через сокет?


person James Combs    schedule 03.12.2016    source источник
comment
Вам действительно нужно изолировать свои проблемы. Возможно, вам стоит заставить что-то работать с локальным файлом. Затем перейдите к фиксированному буферу над сокетом (например, к строке 'A'). После этого перейдите к буферу фиксированного размера со случайными данными (например, данными, содержащими '\0'). Наконец, перейдите к настоящему файлу с настоящей подписью. Если у вас есть проблемы с фиксированным буфером, задайте вопрос об отправке / получении сокета.   -  person jww    schedule 04.12.2016


Ответы (2)


Я фактически решил все свои проблемы, просто закодировав все в 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);
   }
}
person James Combs    schedule 05.12.2016

Что ж, что касается недостающих данных после HexEncoding, это наверняка вызвано nn(spubA), потому что sizeof() возвращает размер указателя на spubA вместо размера вашей строки. Чтобы получить длину строки, вы можете использовать функцию spubA.length() или просто использовать лучший способ сделать это с помощью CryptoPP, IMHO, как вы делаете с клиентом, просто без указания размера:

StringSource saSource(spubA, true,
      new HexEncoder(new StringSink(saEncoded)));

Что касается остальной части вашего кода, похоже, вы пытаетесь выполнить свой собственный учебник 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.

person Lery    schedule 04.12.2016
comment
Я пробовал использовать закрытый ключ для RSAES_OAEP_SHA_Decryptor, но раньше он не работал. Это было некоторое время назад, поэтому я не совсем уверен, в какой ситуации я оказался, почему он не работал, и я переключился на RAW RSA. Я использую свой собственный способ выполнения RSA, поскольку у меня есть только одна пара открытого / закрытого ключей. - person James Combs; 05.12.2016