На главную
ПРАВИЛА 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,1545 ]   [ 20 queries used ]   [ Generated: 21.01.19, 16:01 GMT ]