В настоящее время выполняется обновление с 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.
BLOB закрытого ключа содержит BCRYPT_RSAKEY_BLOB
заголовок, открытый показатель степени, модуль, числа Prime1 и Prime2, см. здесь. Примечательно, что частный показатель не включен. Это учитывается в определении MyRSAPrivateBlob
, но не при реализации keyPrivBlob
. Последний должен быть:
struct MyRSAPrivateBlob keyPrivBlob(modulus_bin, publicExponent_bin, 128, p, q);
privateExponent
и privateExponent_bin
вообще не нужны и поэтому могут быть удалены.
С этим изменением расшифровка работает.
@vengy - Правильно, определение частного показателя (d) из простого числа 1 (p), числа 2 (q) и общего показателя степени (e) является частью обычного генерации ключа RSA, шаг 5.
Отлично работает сейчас! Почему-то я подумал, что частный показатель степени (d) необходим для расшифровки с использованием этого уравнения: открытый текст = (зашифрованный текст^d) mod n, где зашифрованный текст = (открытый текст^e) mod n, поэтому я неправильно передавал privateExponent_bin. Но для BCRYPT_RSAPRIVATE_BLOB вместо этого требуется модуль! Итак, я предполагаю, что с учетом этих значений BCRYPT_RSAPRIVATE_BLOB: открытый показатель степени, модуль, Prime1 и Prime2 он вычисляет (d) с помощью некоторого волшебства математики. Еще раз спасибо @Topaco!