На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: JoeUser, Qraizer, Hsilgos
  
> CryptoAPI генерация RSA ключей.
    Здравствуйте.
    Необходимо генерировать закрытый и открытый ключ.

    Код собрал по найденным примерам в сети:

    ExpandedWrap disabled
      int CreateKeys(CString& strPublicKey, CString& strPrivateKey)
      {
          const DWORD KEYLENGTH = 2048 << 16;
       
          HCRYPTPROV hCryptProv = NULL;
          HCRYPTKEY hKey = NULL;
       
          DWORD dwBlobLength = 0;
          LPBYTE ppbKeyBlob = NULL;
       
          if (!CryptAcquireContext(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_NEWKEYSET))    
          {
              if (!CryptAcquireContext(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0))
              {
                  return -1;
              }
          }
       
          if (!CryptGenKey(hCryptProv, CALG_RSA_KEYX, KEYLENGTH | CRYPT_EXPORTABLE | CRYPT_NO_SALT, &hKey))
          {
              return -1;
          }
       
          HRESULT hr = CryptExportKeyHelper(hKey, NULL, PRIVATEKEYBLOB, &ppbKeyBlob, &dwBlobLength);
          if (FAILED(hr))
          {
              return -1;
          }
       
          DWORD dwStringLength = 0;
          if (!CryptBinaryToString(ppbKeyBlob, dwBlobLength, CRYPT_STRING_BASE64HEADER, 0, &dwStringLength))
          {
              return -1;
          }
       
          if (!CryptBinaryToString(ppbKeyBlob, dwBlobLength, CRYPT_STRING_BASE64HEADER, strPrivateKey.GetBuffer(dwStringLength), &dwStringLength))
          {
              return -1;
          }
       
          CoTaskMemFree(ppbKeyBlob);
       
          strPrivateKey.ReleaseBuffer();
       
          hr = CryptExportKeyHelper(hKey, NULL, PUBLICKEYBLOB, &ppbKeyBlob, &dwBlobLength);
          if (FAILED(hr))
          {
              return -1;
          }
       
          dwStringLength = 0;
          if (!CryptBinaryToString(ppbKeyBlob, dwBlobLength, CRYPT_STRING_BASE64HEADER, 0, &dwStringLength))
          {
              return -1;
          }
       
          if (!CryptBinaryToString(ppbKeyBlob, dwBlobLength, CRYPT_STRING_BASE64HEADER, strPublicKey.GetBuffer(dwStringLength), &dwStringLength))
          {
              return -1;
          }
       
          CoTaskMemFree(ppbKeyBlob);
       
          strPublicKey.ReleaseBuffer();
       
          CryptDestroyKey(hKey);
          return 0;
      }
       
      HRESULT CryptExportKeyHelper(_In_ HCRYPTKEY hKey, _In_opt_ HCRYPTKEY hExpKey, DWORD dwBlobType, _Outptr_result_bytebuffer_(*pcbBlob) BYTE **ppbBlob, _Out_ DWORD *pcbBlob)
      {
          *ppbBlob = nullptr;
          *pcbBlob = 0;
       
          DWORD cbBlob = 0;
          HRESULT hr = CryptExportKey(hKey, hExpKey, dwBlobType, 0, nullptr, &cbBlob) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
          if (SUCCEEDED(hr))
          {
              BYTE *pbBlob = reinterpret_cast<BYTE *>(CoTaskMemAlloc(cbBlob));
              hr = (pbBlob != nullptr) ? S_OK : E_OUTOFMEMORY;
              if (SUCCEEDED(hr))
              {
                  hr = CryptExportKey(hKey, hExpKey, dwBlobType, 0, pbBlob, &cbBlob) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
                  if (SUCCEEDED(hr))
                  {
                      *ppbBlob = pbBlob;
                      *pcbBlob = cbBlob;
                      pbBlob = nullptr;
                  }
                  CoTaskMemFree(pbBlob);
              }
          }
          return hr;
      }


    На выходе:
    Цитата
    -----BEGIN CERTIFICATE-----
    BgIAAACkAABSU0ExAAQAAAEAAQBxR/JTB6hqD86PaRQC/CagvUFfJOtap3ocEty4
    KWh8KZR8mrYhquFhd1DRtz4a84Mu9CpIBzMFh1MPFRVebYFwqe11L6upzQCH2ryX
    dbOtcHWGVKRzwg6RhQyljJ9ZLtzD5VL/63HUAodO9xXNhB4eVmmPQ2ETVtrH4axv
    rgf+uA==
    -----END CERTIFICATE-----

    -----BEGIN CERTIFICATE-----
    BwIAAACkAABSU0EyAAQAAAEAAQBxR/JTB6hqD86PaRQC/CagvUFfJOtap3ocEty4
    KWh8KZR8mrYhquFhd1DRtz4a84Mu9CpIBzMFh1MPFRVebYFwqe11L6upzQCH2ryX
    dbOtcHWGVKRzwg6RhQyljJ9ZLtzD5VL/63HUAodO9xXNhB4eVmmPQ2ETVtrH4axv
    rgf+uAed5Ss2kY8iZJec5DKtjr+Jgv+ykcpmyngYkr5hfxESlPBySNCF61DtuJRy
    jGdHcxd5zeiE9MjoRHPR/vGqI+nHUYrYZS+aH12Q9VnsQc5FFG0fzsa+uS+qnEp/
    S+FrTWFUJQeW4wfyw0WhYKa0PYk+vFiFZACswb7BH+bvwiHLV5DexDkNtOSTO+6Q
    hhBXc2NXGk3z+cSo4ilBnSMh/XrQSReusmzDtilcIqPRUPpPOtEGHIL2QQAFi5gC
    xvpJZWOth+zMuqu1iliA9iOQWsNkIyAuAocWvS/IabIs7mCEba+B+lWqeiRDi5eA
    hTTsRY6gKvhxgtXNDyFX1tbLsUdZCGexFvr7Ov0cSn/Iz5jsTPEWI0rIVyH+6jMl
    uK+eaA/4pvd6sNTirkw8ppEoCCFBvj7pwQx5wrsnv8AqF7OAXXdflCyOXCpA1A/e
    rTj7+2H315T2X6sQqdAXVoeZzQxbiMpYB7524KRXBo8XU1m8GbM2MbbSorSQnIHt
    1t6gFIr2ZAdgRumeBpOSA7FEYbZNqBmRc4fob267KSOGwBsvayYXx5g2b5isJsK8
    I0+EN48ox3zmqvc9empgGIG48Wo=
    -----END CERTIFICATE-----


    Что я упустил?
      Цитата WinAx @
      Необходимо генерировать закрытый и открытый ключ.


      Что я упустил?

      А что, что-то не получается ?
      Судя по результатам, как будто бы, всё получилось.
      Однако, можно предложить:
      1. всё это хозяйство оформить классом.
      2. всё, что связано с получением/возвращением ресурсов,
      тоже обернуть классами.
      Например: HCRYPTPROV , HCRYPTKEY , CoTaskMemAlloc/CoTaskMemFree
      Подпись была выключена в связи с наложенным заземлением.
        Должно быть:

        Цитата
        -----BEGIN PUBLIC KEY-----
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArtBewZDcVXXw+dtoDbCR
        6cZZZvkdbeQcNzKpNS8g6F4BuMuzjkrFyOT7HWGuF0nTd9rkgUM+nQu/zR1LClaV
        .............
        -----END PUBLIC KEY-----

        -----BEGIN PRIVATE KEY-----
        MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDGtt1MVGVT7K68
        G1Vyvv6GugCMS2HER+K5hm6Ff+XYkV6WjLHIgEXBcDWHMIuGELUOYBRN2WsO01Nu
        .............
        -----END PRIVATE KEY-----
          Цитата WinAx @
          Должно быть:

          Цитата
          -----BEGIN PUBLIC KEY-----
          MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArtBewZDcVXXw+dtoDbCR
          6cZZZvkdbeQcNzKpNS8g6F4BuMuzjkrFyOT7HWGuF0nTd9rkgUM+nQu/zR1LClaV
          .............
          -----END PUBLIC KEY-----

          -----BEGIN PRIVATE KEY-----
          MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDGtt1MVGVT7K68
          G1Vyvv6GugCMS2HER+K5hm6Ff+XYkV6WjLHIgEXBcDWHMIuGELUOYBRN2WsO01Nu
          .............
          -----END PRIVATE KEY-----

          Скорее всего, у Микрософта так не будет.
          я пробовал - у меня всегда получалось, что в качестве
          приватного ключа было объединение публичного и приватного.
          А публичный - отдельно.
          Поэтому, "приватный ключ" - длиннее.
          Почему так, я и сам бы хотел узнать.
          Поэтому "приватный" у Микрософта можно было использовать
          для обеих операций, а "публичный" - как положено.
          ---
          Проведи прямой эксперимент. Публичным зашифруй, приватным расшифруй.
          Тогда станет ясно, получилось или нет.
          Сообщение отредактировано: ЫукпШ -
          Подпись была выключена в связи с наложенным заземлением.
            Проблема не в длине.
            Когда я генерировал ключи в php (там OpenSSL), ключи всегда были обернуты:

            Цитата
            -----BEGIN PUBLIC KEY-----
            -----END PUBLIC KEY-----

            -----BEGIN PRIVATE KEY-----
            -----END PRIVATE KEY-----


            И начинались с одинаковых символов.
            Предполагаю что это заголовки RSA ключей.

            В CryptoAPI получается сертификаты.
            Тоже начинаются с одинаковых символов, но других.
              Заменил флаг CRYPT_STRING_BASE64HEADER на CRYPT_STRING_BASE64.
              Получается сам BASE64.
              Публичный шифрует, приватный дешифрует.
              Вроде все ок.
                Цитата WinAx @
                Проблема не в длине.
                Когда я генерировал ключи в php (там OpenSSL), ключи всегда были обернуты:

                Цитата
                -----BEGIN PUBLIC KEY-----
                -----END PUBLIC KEY-----

                -----BEGIN PRIVATE KEY-----
                -----END PRIVATE KEY-----

                Такие заголовки, вероятно, результат деятельности "CryptBinaryToString".
                В двоичном образе ключа их вообще нет. Никогда не видел.
                К шифровке/расшифровке они отношения не имеют.
                Хотя у двоичных ключей тоже есть какой-то заголовок 16 байт.
                А зачем вообще в текст преобразовывать ?
                Подпись была выключена в связи с наложенным заземлением.
                  Цитата ЫукпШ @
                  А зачем вообще в текст преобразовывать ?

                  По задумке сервер их должен был отправлять их клиенту по http.

                  В общем, то с этим и проблема...

                  Не силен я в шифровании.
                  Прошу пояснить просвещенных.

                  В чем была задумка:
                  Сервер генерирует пару ключей.
                  Клиент берет публичный ключ и шифрует данные.
                  Другой клиент берез приватный ключ и дешифрует.

                  При шифровании одних данных, одним ключом - результат всегда разный.
                  Если шифрование/дешифрование производится одной программой последовательно - все норм.
                  Если шифрует одни клиент, а дешифрует другой (одной парой ключей) - CryptDecrypt ERROR_INVALID_PARAMETER.

                  Это особенность алгоритма или ошибка в программе?
                    Цитата WinAx @
                    В чем была задумка:
                    Сервер генерирует пару ключей.
                    Клиент берет публичный ключ и шифрует данные.
                    Другой клиент берез приватный ключ и дешифрует.

                    Да, я тоже так делаю. Только в текст не преобразовывал никогда.
                    Микрософтной реализацией, устойчиво работает.

                    Добавлено
                    Цитата WinAx @
                    При шифровании одних данных, одним ключом - результат всегда разный.

                    Специально это я не проверял, надо будет попробовать.
                    Теоретически это может быть - если сами функции
                    шифрования добавляют к данным случайную последовательность.
                    Не проверял. Если будет время, проверю.
                    ---
                    Но конечно, это полезная необходимость. Если этого нет, надо мастерить самому.

                    Добавлено
                    Цитата WinAx @
                    Если шифрует одни клиент, а дешифрует другой (одной парой ключей) - CryptDecrypt ERROR_INVALID_PARAMETER.

                    Это особенность алгоритма или ошибка в программе?

                    Это явная ошибка - поскольку ничего же не работает.
                    Смысл в том, что разные программы на разных компах
                    могут обмениваться шифрованными сообщениями.
                    Сообщение отредактировано: ЫукпШ -
                    Подпись была выключена в связи с наложенным заземлением.
                      Цитата ЫукпШ @
                      Это явная ошибка - поскольку ничего же не работает.
                      Смысл в том, что разные программы на разных компах
                      могут обмениваться шифрованными сообщениями.


                      Я просто подумал что расшифровать может только тот кто создал ключи.
                      Это не так, да?

                      Еще вопрос:
                      В данном алгоритме можно обойтись без выравнивания?
                      Если ключ 2048 выравнивание 256
                        Цитата WinAx @
                        Я просто подумал что расшифровать может только тот кто создал ключи.
                        Это не так, да?

                        Расшифровать может тот, у кого есть приватный ключ.
                        Зашифровать тот, у кого есть публичный.
                        А для цифровой подписи - наоборот.

                        Добавлено
                        Цитата WinAx @
                        Еще вопрос:
                        В данном алгоритме можно обойтись без выравнивания?
                        Если ключ 2048 выравнивание 256

                        Нет. Давно занимался, но насколько я помню, это блочный шифр.
                        Но я никогда не считал это проблемой... надо ему столько-то байт,
                        пусть возьмёт.
                        ---
                        Вообще RSA - это очень трудоёмкий шифр.
                        Если предполагается большие объёмы информации, можно делать так:
                        1. При помощи RSA пересылаем ключ симметричного шифрования, сгенерированный
                        случайно. Например, по алгоритму RC4. RC4 - быстрый алгоритм, фактически XOR.
                        Очевидно, он потоковый, а не блочный.
                        2. Пользуемся всю сессию связи RC4.
                        Сообщение отредактировано: ЫукпШ -
                        Подпись была выключена в связи с наложенным заземлением.
                          Понял. Спасибо!
                          Ошибку нашел. Все заработало нормально.
                            Ты только не используй этот, уже засвеченный, ключ в продакшне.
                            Одни с годами умнеют, другие становятся старше.
                            1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                            0 пользователей:


                            Рейтинг@Mail.ru
                            [ Script Execution time: 0,1363 ]   [ 15 queries used ]   [ Generated: 27.05.19, 09:52 GMT ]