Обновление Windows CryptDecrypt() до NCryptDecrypt() возвращает NTE_INVALID_PARAMETER(0x80090027) или STATUS_UNSUCCESSFUL(0xC0000001)?

В настоящее время выполняется обновление с Windows CryptDecrypt() [API устарел] на NCryptDecrypt() [Cryptography Next Generation].

Минимальный пример кода ниже пытается зашифровать/расшифровать с использованием RSA 1024-бит.

Запуск с Microsoft Visual Studio Professional 2019 выдает ошибку NTE_INVALID_PARAMETER (0x80090027)

Failed Decrypt NCryptDecrypt. Error -2146893785

или случайным образом STATUS_UNSUCCESSFUL (0xc0000001)

Failed Decrypt NCryptDecrypt. Error -1073741823

Шифрование кажется нормальным, так как ошибок не возникает, но расшифровка не работает NCryptDecrypt?

#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS
#include <winternl.h>
#include <ntstatus.h>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
#include <ncrypt.h>
#include <bcrypt.h>

#pragma comment(lib, "bcrypt.lib")
#pragma comment(lib, "ncrypt.lib")

//https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_rsakey_blob

#pragma pack(push, 1)
struct MyRSAPublicBlob {
    BCRYPT_RSAKEY_BLOB blob{};
    BYTE exponent[3] = {};
    BYTE modulus[128] = {};

    MyRSAPublicBlob(const std::vector<BYTE>& mod, const std::vector<BYTE>& exp, ULONG modulusSize) : blob{}
    {
        blob.BitLength = modulusSize * 8;
        blob.Magic = BCRYPT_RSAPUBLIC_MAGIC;
        blob.cbModulus = modulusSize;
        blob.cbPublicExp = static_cast<DWORD>(exp.size());
        blob.cbPrime1 = 0;
        blob.cbPrime2 = 0;

        memcpy(exponent, exp.data(), exp.size());
        memcpy(modulus, mod.data(), mod.size());
    }

    MyRSAPublicBlob() : blob{} { }
};
#pragma pack(pop)

#pragma pack(push, 1)
struct MyRSAPrivateBlob {
    BCRYPT_RSAKEY_BLOB blob{};
    BYTE exponent[3] = {};
    BYTE modulus[128] = {};
    BYTE prime1[64] = {};
    BYTE prime2[64] = {};

    MyRSAPrivateBlob(const std::vector<BYTE>& mod, const std::vector<BYTE>& exp, ULONG modulusSize,
        const std::vector<BYTE>& p, const std::vector<BYTE>& q) : blob{}
    {
        blob.Magic = BCRYPT_RSAPRIVATE_MAGIC;
        blob.BitLength = modulusSize * 8;
        blob.cbModulus = modulusSize;
        blob.cbPublicExp = static_cast<DWORD>(exp.size());
        blob.cbPrime1 = static_cast<DWORD>(p.size());
        blob.cbPrime2 = static_cast<DWORD>(q.size());

        memcpy(exponent, exp.data(), exp.size());
        memcpy(modulus, mod.data(), mod.size());
        memcpy(prime1, p.data(), p.size());
        memcpy(prime2, q.data(), q.size());
    }

    MyRSAPrivateBlob() : blob{} { }
};
#pragma pack(pop)

int RSA1024_encrypt(char* plainText, int plainExtent, char* cipherText, int cipherExtent, struct MyRSAPublicBlob keyBlob)
{
    SECURITY_STATUS stat;
    NCRYPT_PROV_HANDLE hProv;
    NCRYPT_KEY_HANDLE hKey;
    DWORD numEncryptedBytes = 0;

    stat = NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0);
    if (ERROR_SUCCESS != stat) {
        std::cout << "Failed NCryptOpenStorageProvider. Error " << stat;
        return 0;
    }

    stat = NCryptImportKey(hProv, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, &hKey, (PBYTE)&keyBlob.blob, sizeof(keyBlob),0);
    if (ERROR_SUCCESS != stat) {
        std::cout << "Failed NCryptImportKey. Error " << stat;
        return 0;
    }

    stat = NCryptEncrypt(hKey,(PBYTE)plainText, plainExtent, NULL, (PBYTE)cipherText, cipherExtent, &numEncryptedBytes, NCRYPT_PAD_PKCS1_FLAG);
    if (ERROR_SUCCESS != stat) {
        std::cout << "Failed NCryptEncrypt. Error " << stat;
        return 0;
    }

    return numEncryptedBytes;
}


int RSA1024_decrypt(char* plainText, int plainExtent, char* cipherText, int cipherExtent, struct MyRSAPrivateBlob keyBlob)
{
    SECURITY_STATUS stat;
    NCRYPT_PROV_HANDLE hProv;
    NCRYPT_KEY_HANDLE hKey;
    DWORD numDecryptedBytes = 0;

    stat = NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0);
    if (ERROR_SUCCESS != stat) {
        std::cout << "Failed Decrypt NCryptOpenStorageProvider. Error " << stat;
        return 0;
    }

    stat = NCryptImportKey(hProv, NULL, BCRYPT_RSAPRIVATE_BLOB, NULL, &hKey, (PBYTE)&keyBlob.blob, sizeof(keyBlob),0);
    if (ERROR_SUCCESS != stat) {
        std::cout << "Failed Decrypt NCryptImportKey. Error " << stat;
        return 0;
    }

    // NTE_INVALID_PARAMETER(0x80090027) or STATUS_UNSUCCESSFUL(0xC0000001)
    stat = NCryptDecrypt(hKey,(PBYTE)cipherText,cipherExtent, NULL, (PBYTE)plainText, plainExtent, &numDecryptedBytes, NCRYPT_PAD_PKCS1_FLAG);
    if (ERROR_SUCCESS != stat) {
        std::cout << "Failed Decrypt NCryptDecrypt. Error " << stat;
        return 0;
    }

    return numDecryptedBytes;
}


int main()
{
    constexpr BYTE publicExponent[] = {
        0x1, 0x0, 0x1
    };

    constexpr BYTE modulus[] = {
        0xb0,0x71,0x76,0x5d,0xef,0x1b,0x6f,0x3c,
        0xf2,0xde,0x31,0x8c,0xa1,0x66,0x50,0xee,
        0x01,0x96,0x64,0x7e,0x1d,0x3e,0xac,0xa9,
        0xad,0x06,0x31,0x56,0x97,0x1d,0x3c,0x71,
        0xeb,0xcc,0xef,0xa1,0x24,0xb0,0xb7,0x0a,
        0x33,0xf7,0x29,0x00,0x36,0x4c,0x4c,0x72,
        0xd9,0x88,0x39,0xa8,0xf6,0x27,0xc9,0x24,
        0xfc,0x4d,0x38,0xb3,0x03,0x06,0x56,0xe3,
        0xfc,0x67,0x6a,0xb3,0xe2,0xf1,0xc1,0x4a,
        0xf9,0xd2,0xab,0xdd,0xae,0xd0,0x2e,0xca,
        0x11,0x41,0x32,0xd8,0x07,0xff,0x3a,0xb2,
        0x27,0x3c,0xb9,0xcd,0x64,0x4f,0xbd,0x23,
        0xb6,0x01,0x77,0x33,0xd8,0x6f,0x5a,0x84,
        0xaa,0x9c,0x21,0x0b,0xd8,0xdb,0xe7,0xe2,
        0x7e,0x9b,0x6a,0x7b,0x4c,0x5e,0xae,0x1d,
        0xda,0x78,0x02,0x87,0xeb,0xe6,0x4f,0x7b
    };

    constexpr BYTE privateExponent[] = {
        0x1e,0xbc,0x24,0xfa,0x47,0xe4,0x67,0x84,
        0x1e,0x6a,0x46,0x07,0x51,0x36,0x19,0x72,
        0xdc,0x23,0xee,0x6d,0x69,0x7a,0xb9,0x68,
        0xf5,0x12,0xd7,0x15,0x56,0x4d,0x69,0x72,
        0x0e,0xb9,0x2c,0x24,0xcd,0xd7,0x5a,0x8b,
        0x14,0x72,0x41,0x5a,0x20,0x1b,0x3a,0x55,
        0xe7,0x3e,0xab,0x8c,0x9b,0x14,0x63,0x1d,
        0x66,0x35,0xad,0x62,0xc1,0x6c,0x21,0x46,
        0x5b,0x6e,0x1d,0x81,0xe1,0x5a,0xa1,0x45,
        0x37,0x9b,0x70,0xca,0xc3,0x7a,0xee,0x52,
        0xe2,0x50,0xe1,0x5b,0x1c,0xeb,0x57,0x91,
        0x3c,0xad,0xd5,0x6c,0x4f,0x90,0xcc,0x2b,
        0x3d,0xde,0x25,0x3d,0xfc,0x35,0x9f,0x35,
        0x4e,0xf1,0xbf,0x34,0x6a,0x32,0xa4,0xbc,
        0xee,0xcb,0x65,0xdf,0x4d,0x28,0xb8,0x4a,
        0x92,0xc1,0xe1,0xab,0x44,0xd0,0x8f,0x81
    };

    constexpr BYTE prime1[] = {
        0xd5,0x06,0x53,0x6b,0xba,0xd7,0x3e,0x8f,
        0x1d,0xae,0x2c,0xc6,0xb2,0x7c,0x73,0xb8,
        0x83,0xfd,0x5d,0x64,0xf4,0x17,0x77,0xb0,
        0xc8,0x4d,0xb3,0x19,0x19,0xf4,0xad,0xe4,
        0x4d,0x31,0x3c,0xef,0x04,0xc6,0xe8,0xb4,
        0x17,0x9f,0x66,0x4f,0x62,0xf8,0x78,0x03,
        0x39,0xa7,0xa8,0xf1,0x64,0x70,0x86,0x14,
        0x63,0xda,0xe6,0x68,0x98,0x3d,0x29,0x03
    };

    constexpr BYTE prime2[] = {
        0xd4,0x09,0xe2,0x00,0x3e,0x6d,0x55,0x59,
        0x87,0xcf,0xa1,0xee,0xe2,0x5d,0xbc,0x8d,
        0x80,0x43,0x92,0x71,0xdc,0x53,0xa8,0x88,
        0x00,0xc5,0x5b,0xde,0xb2,0x1c,0xb9,0x95,
        0xa7,0x64,0x57,0x0f,0x27,0x88,0xf9,0x31,
        0xab,0x68,0x0c,0xf0,0xfe,0x12,0x76,0x84,
        0xf4,0x09,0x5b,0xc2,0x87,0x0e,0x25,0x73,
        0x99,0x23,0x33,0x26,0x2d,0x8a,0xea,0x29
    };

    char plainText[] = "The quick brown fox jumps over the lazy dog";
    char cipherText[512] = { 0 };
    char plainTextOut[512] = { 0 };

    std::vector<BYTE> publicExponent_bin(std::begin(publicExponent), std::end(publicExponent));
    std::vector<BYTE> modulus_bin(std::begin(modulus), std::end(modulus));
    std::vector<BYTE> privateExponent_bin(std::begin(privateExponent), std::end(privateExponent));
    std::vector<BYTE> p(std::begin(prime1), std::end(prime1));
    std::vector<BYTE> q(std::begin(prime2), std::end(prime2));

    struct MyRSAPublicBlob keyBlob(modulus_bin, publicExponent_bin, 128);
    int numEncryptedBytes = RSA1024_encrypt(plainText, sizeof(plainText)-1, cipherText, sizeof(cipherText), keyBlob);

    struct MyRSAPrivateBlob keyPrivBlob(privateExponent_bin, publicExponent_bin, 128, p, q);
    int numDecryptedBytes = RSA1024_decrypt(plainTextOut, sizeof(plainTextOut), cipherText, numEncryptedBytes, keyPrivBlob);

    return 0;
}

Ключевые значения в образце были взяты из этого вывода:

C:\Projects>openssl genrsa -out private-key.pem 1024
Generating RSA private key, 1024 bit long modulus (2 primes)
.....+++++
......+++++
e is 65537 (0x010001)

C:\Projects>openssl rsa -in private-key.pem -pubout -out public-key.pem
writing RSA key

C:\Projects>openssl rsa -in private-key.pem -text -noout
RSA Private-Key: (1024 bit, 2 primes)
modulus:
    00:b0:71:76:5d:ef:1b:6f:3c:f2:de:31:8c:a1:66:
    50:ee:01:96:64:7e:1d:3e:ac:a9:ad:06:31:56:97:
    1d:3c:71:eb:cc:ef:a1:24:b0:b7:0a:33:f7:29:00:
    36:4c:4c:72:d9:88:39:a8:f6:27:c9:24:fc:4d:38:
    b3:03:06:56:e3:fc:67:6a:b3:e2:f1:c1:4a:f9:d2:
    ab:dd:ae:d0:2e:ca:11:41:32:d8:07:ff:3a:b2:27:
    3c:b9:cd:64:4f:bd:23:b6:01:77:33:d8:6f:5a:84:
    aa:9c:21:0b:d8:db:e7:e2:7e:9b:6a:7b:4c:5e:ae:
    1d:da:78:02:87:eb:e6:4f:7b
publicExponent: 65537 (0x10001)
privateExponent:
    1e:bc:24:fa:47:e4:67:84:1e:6a:46:07:51:36:19:
    72:dc:23:ee:6d:69:7a:b9:68:f5:12:d7:15:56:4d:
    69:72:0e:b9:2c:24:cd:d7:5a:8b:14:72:41:5a:20:
    1b:3a:55:e7:3e:ab:8c:9b:14:63:1d:66:35:ad:62:
    c1:6c:21:46:5b:6e:1d:81:e1:5a:a1:45:37:9b:70:
    ca:c3:7a:ee:52:e2:50:e1:5b:1c:eb:57:91:3c:ad:
    d5:6c:4f:90:cc:2b:3d:de:25:3d:fc:35:9f:35:4e:
    f1:bf:34:6a:32:a4:bc:ee:cb:65:df:4d:28:b8:4a:
    92:c1:e1:ab:44:d0:8f:81
prime1:
    00:d5:06:53:6b:ba:d7:3e:8f:1d:ae:2c:c6:b2:7c:
    73:b8:83:fd:5d:64:f4:17:77:b0:c8:4d:b3:19:19:
    f4:ad:e4:4d:31:3c:ef:04:c6:e8:b4:17:9f:66:4f:
    62:f8:78:03:39:a7:a8:f1:64:70:86:14:63:da:e6:
    68:98:3d:29:03
prime2:
    00:d4:09:e2:00:3e:6d:55:59:87:cf:a1:ee:e2:5d:
    bc:8d:80:43:92:71:dc:53:a8:88:00:c5:5b:de:b2:
    1c:b9:95:a7:64:57:0f:27:88:f9:31:ab:68:0c:f0:
    fe:12:76:84:f4:09:5b:c2:87:0e:25:73:99:23:33:
    26:2d:8a:ea:29
exponent1:
    00:c1:4d:0d:33:ab:86:97:e8:ec:08:d9:ee:af:95:
    c8:b8:3d:65:12:73:82:1f:2d:68:08:4a:a1:62:fc:
    af:8f:7f:a4:20:32:e7:bd:50:f5:66:3e:2d:51:7c:
    66:15:8b:69:79:ce:ce:b9:c4:e7:6a:73:64:2d:05:
    79:11:f4:25:9b
exponent2:
    4a:ec:eb:15:56:f9:df:6c:f1:96:a7:0b:f8:a5:52:
    d9:55:77:8b:29:fc:c6:fb:08:83:ed:39:57:69:ec:
    c8:8f:5f:45:0f:96:65:4b:fb:72:57:b5:3e:cd:71:
    9a:28:93:36:80:90:12:1f:13:1a:9c:cc:82:29:b2:
    d5:e8:fe:71
coefficient:
    16:61:78:25:39:97:45:9d:f0:f1:f1:79:89:48:ac:
    11:8b:1c:bd:00:d4:b1:ed:6f:20:8e:da:6d:ad:8b:
    cf:59:d0:6b:87:6d:44:bd:25:5b:a5:09:99:f5:e1:
    2c:68:d0:e6:8e:6b:e7:bb:8a:b8:fd:d1:0e:d7:c8:
    d6:46:7f:3c

C:\Projects>

ОБНОВЛЕНО

Зафиксированный. Как отмечено в ответе, чтобы расшифровка работала правильно, измените эту строку кода с

struct MyRSAPrivateBlob keyPrivBlob(privateExponent_bin, publicExponent_bin, 128, p, q);

к

struct MyRSAPrivateBlob keyPrivBlob(modulus_bin, publicExponent_bin, 128, p, q);

Виола! Расшифровка работает!

ОПТИМИЗАЦИЯ

Чтобы еще больше ускорить процесс, используя оптимизацию Китайской теоремы об остатках (CRT), замените текущий BCRYPT_RSAPRIVATE_BLOB на BCRYPT_RSAFULLPRIVATE_BLOB, а затем разверните MyRSAPrivateBlob чтобы охватить все следующие значения:

BCRYPT_RSAKEY_BLOB
    PublicExponent[cbPublicExp] // Big-endian.
    Modulus[cbModulus] // Big-endian.
    Prime1[cbPrime1] // Big-endian.
    Prime2[cbPrime2] // Big-endian.
    Exponent1[cbPrime1] // Big-endian.
    Exponent2[cbPrime2] // Big-endian.
    Coefficient[cbPrime1] // Big-endian.
    PrivateExponent[cbModulus] // Big-endian.
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
119
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

BLOB закрытого ключа содержит BCRYPT_RSAKEY_BLOB заголовок, открытый показатель степени, модуль, числа Prime1 и Prime2, см. здесь. Примечательно, что частный показатель не включен. Это учитывается в определении MyRSAPrivateBlob, но не при реализации keyPrivBlob. Последний должен быть:

struct MyRSAPrivateBlob keyPrivBlob(modulus_bin, publicExponent_bin, 128, p, q);

privateExponent и privateExponent_bin вообще не нужны и поэтому могут быть удалены.

С этим изменением расшифровка работает.

Отлично работает сейчас! Почему-то я подумал, что частный показатель степени (d) необходим для расшифровки с использованием этого уравнения: открытый текст = (зашифрованный текст^d) mod n, где зашифрованный текст = (открытый текст^e) mod n, поэтому я неправильно передавал privateExponent_bin. Но для BCRYPT_RSAPRIVATE_BLOB вместо этого требуется модуль! Итак, я предполагаю, что с учетом этих значений BCRYPT_RSAPRIVATE_BLOB: открытый показатель степени, модуль, Prime1 и Prime2 он вычисляет (d) с помощью некоторого волшебства математики. Еще раз спасибо @Topaco!

vengy 01.04.2023 15:56

@vengy - Правильно, определение частного показателя (d) из простого числа 1 (p), числа 2 (q) и общего показателя степени (e) является частью обычного генерации ключа RSA, шаг 5.

Topaco 01.04.2023 17:59

Другие вопросы по теме