Я пытаюсь использовать RSA 2048 для шифрования между моими программами на C# и C. Для этого я встраиваю закрытый ключ в свою программу на C, а открытый ключ — в программу на C#. Затем моя программа на C# шифрует строку и отправляет ее в мою программу на C для расшифровки. Проблема в том, что я получаю ошибку 0xc000000d при вызове функции BCryptDecrypt внутри моей программы на C, когда я использую любой параметр заполнения, кроме BCRYPT_PAD_NONE. Проблема в том, что в библиотеке C# System.Security.Cryptography есть только опции для использования PKCS1 и OAEP, которые кажутся несовместимыми с версией BCrypt (хотя мой открытый ключ сомнительно). Я определил это, потому что установка var EncryptData = rsa.Encrypt(dataToEncryptBytes, true); в значение true означает, что он использует OAEP, а false означает, что он использует PKCS1, и когда я внес эти изменения, я также изменил обе функции BCryptDecrypt вызывает внутри моей функции DecryptRsaData, где это всегда приводит к ошибке 0xc000000d, также известной как INVALID_PARAMETER. Затем, когда я перешел на использование BCRYPT_PAD_NONE, это не привело к ошибке (поскольку данные больше не проверяются), но полученный PUCHAR после расшифровки является тарабарщиной и не содержит моей исходной строки.
Изначально я сгенерировал ключи внутри своей программы на C#, а затем скопировал их ключи в каждую программу, но это было плохо, поскольку отладка открытого ключа была бы намного проще, поскольку у него всего два параметра. Поэтому вместо этого я решил сгенерировать шестнадцатеричный код, связанный как с закрытым, так и с открытым ключом, внутри моей программы на C, а затем скопировать закрытый ключ в массив беззнаковых символов и скопировать открытый ключ в строку C# для дальнейшей обработки. Вот как я это сделал.
static void PrintKeyInHex(PUCHAR key, ULONG keySize) {
for (ULONG i = 0; i < keySize; ++i) {
DebugMessage("%02X", key[i]); // %02X formats the output as two-digit hexadecimal
if (i + 1 < keySize) {
DebugMessage(":");
}
}
DebugMessage("\n");
}
NTSTATUS GenerateAndPrintKeys() {
BCRYPT_ALG_HANDLE hAlgorithm = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
PUCHAR pbPublicKey = NULL, pbPrivateKey = NULL;
ULONG cbPublicKey = 0, cbPrivateKey = 0, cbData = 0;
// Open an algorithm provider for RSA.
status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_RSA_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(status)) {
DebugMessage("Failed to open algorithm provider.");
goto Cleanup;
}
// Generate the key pair.
status = BCryptGenerateKeyPair(hAlgorithm, &hKey, 2048, 0);
if (!NT_SUCCESS(status)) {
DebugMessage("Failed to generate key pair.");
goto Cleanup;
}
// Finalize the key (make it usable).
status = BCryptFinalizeKeyPair(hKey, 0);
if (!NT_SUCCESS(status)) {
DebugMessage("Failed to finalize key pair.");
goto Cleanup;
}
// Export the public key.
status = BCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, 0, &cbPublicKey, 0);
if (!NT_SUCCESS(status)) {
DebugMessage("Failed to get public key size.");
goto Cleanup;
}
pbPublicKey = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, cbPublicKey, '1Tag');
if (!pbPublicKey) {
status = STATUS_NO_MEMORY;
goto Cleanup;
}
status = BCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, pbPublicKey, cbPublicKey, &cbData, 0);
if (!NT_SUCCESS(status)) {
DebugMessage("Failed to export public key.");
goto Cleanup;
}
// Export the private key
status = BCryptExportKey(hKey, NULL, BCRYPT_RSAPRIVATE_BLOB, NULL, 0, &cbPrivateKey, 0);
if (!NT_SUCCESS(status)) {
DebugMessage("Failed to get private key size.");
goto Cleanup;
}
pbPrivateKey = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, cbPrivateKey, '2Tag');
if (!pbPrivateKey) {
status = STATUS_NO_MEMORY;
goto Cleanup;
}
status = BCryptExportKey(hKey, NULL, BCRYPT_RSAPRIVATE_BLOB, pbPrivateKey, cbPrivateKey, &cbData, 0);
if (!NT_SUCCESS(status)) {
DebugMessage("Failed to export private key.");
goto Cleanup;
}
DebugMessage("Public Key: ");
PrintKeyInHex(pbPublicKey, cbPublicKey);
DebugMessage("\n \n");
DebugMessage("Private Key: ");
PrintKeyInHex(pbPrivateKey, cbPrivateKey);
Cleanup:
if (pbPublicKey) ExFreePoolWithTag(pbPublicKey, '1Tag');
if (pbPrivateKey) ExFreePoolWithTag(pbPrivateKey, '2Tag');
if (hKey) BCryptDestroyKey(hKey);
if (hAlgorithm) BCryptCloseAlgorithmProvider(hAlgorithm, 0);
return status;
}
Это гарантировало правильность моего закрытого ключа, что просто означает, что мне нужно было заставить открытый ключ работать с алгоритмом C# System.Security.Cryptography RSA. Но поскольку я знал, что открытый ключ заканчивается модулем (256 байт), то экспонентой (3 байта) в конце было довольно легко. Затем, поскольку я не был уверен в порядке байтов (также просто для того, чтобы охватить все основы), я решил перебрать его, попробовав все 4 комбинации байтов и отправив их в свою программу на C. Под этим я подразумеваю, что я бы вызвал следующий метод C# EncryptDataWithPublicKey 4 раза с i от 0 до 3, гарантируя, что какие-либо ошибки не связаны с неправильным порядком байтов. Несмотря на это, я все равно получал одни и те же проблемы, описанные в первом абзаце, все 4 раза.
public static byte[] EncryptDataWithPublicKey(string dataToEncrypt, int i)
{
RSAParameters rsaParams = ConvertHexToRSAParameters(hexPublicKey, i);
using (var rsa = new RSACryptoServiceProvider())
{
// Load the RSA public key
rsa.ImportParameters(rsaParams);
// Convert the data to encrypt to a byte array
var dataToEncryptBytes = Encoding.UTF8.GetBytes(dataToEncrypt);
// Encrypt the data. OAEP padding is recommended for new applications.
var encryptedData = rsa.Encrypt(dataToEncryptBytes, false); // Set to true to use OAEP padding
return encryptedData;
}
}
private static RSAParameters ConvertHexToRSAParameters(string hexString, int i)
{
byte[] bytes = HexStringToByteArray(hexString);
Console.WriteLine("BytesSize:" + bytes.Length);
bytes.ToList().ForEach(i => Console.Write(i.ToString() + " "));
RSAParameters rsaParams = new RSAParameters();
// Extract the modulus and exponent from the blob
byte[] exponent = ExtractExponent(bytes, 3); // Assuming 3 bytes for exponent
byte[] modulus = ExtractModulus(bytes, 256);
if(i == 1)
{
Array.Reverse(exponent);
Array.Reverse(modulus);
}
else if(i == 2)
{
Array.Reverse(exponent);
}
else if(i == 3)
{
Array.Reverse(modulus);
}
rsaParams.Exponent = exponent;
rsaParams.Modulus = modulus;
return rsaParams;
}
В любом случае, когда моя программа на C пытается расшифровать исходную строку, я получаю статус 0xc000000d при использовании BCRYPT_PAD_PKCS1 или BCRYPT_PAD_OAEP, в то время как BCRYPT_PAD_NONE > не приводит к проблемам со статусом, но к нечитаемым данным. Я предполагаю, что либо библиотека C, известная как BCRYPT_PAD_OAEP и BCRYPT_PAD_PKCS1 BCrypt, несовместима с версией C#, либо я неправильно создаю открытые ключи. Я пришел к такому выводу, поскольку знаю, что закрытый ключ действителен на 100%, поскольку он сгенерирован алгоритмом BCrypt, и я знаю, что шестнадцатеричный код открытого ключа также правильный, но я не уверен, конвертирую ли я шестнадцатеричный ключ открытого ключа в правильный формат. Кроме того, последнее замечание, основанное на предыдущем тестировании функции BCryptImportKeyPair, могло дать сбой, когда я неправильно отформатировал открытый ключ в своем приложении C#, но я вполне уверен, что это просто проверка длины, которая говорит мне, что длина моего поля правильна, но с другой стороны, я также пробую все возможности порядка байтов, поэтому я действительно не понимаю, в чем может быть проблема. При этом ниже приведен мой код C, который я использую для расшифровки реальных данных.
unsigned char g_privateKeyBlob[] = { 0x52, 0x53, 0x41, 0x32, ... }; // I cut off the rest since its 539 bytes long
static void DebugPrintEncryptedData(UCHAR* data, ULONG length) {
for (ULONG i = 0; i < length; ++i) {
DebugMessage("%02X ", data[i]);
if ((i + 1) % 16 == 0) {
DebugMessage("\n"); // New line every 16 bytes for readability
}
}
DebugMessage("\n"); // New line at the end
}
// This code then runs which calls the bellow function and then formats the data in a readable format with the above function
ULONG privateKeyBlobLength = sizeof(g_privateKeyBlob); // Get the size of your private key blob
PUCHAR decryptedData = NULL;
ULONG decryptedDataLength = 0;
NTSTATUS stat = DecryptRsaData(handshakeData->EncryptedData, 256, g_privateKeyBlob, privateKeyBlobLength, &decryptedData, &decryptedDataLength);
DebugMessage("FirstDebug Status:%d \n", stat);
DebugMessage("Status:0x%x \n", stat);
DebugPrintEncryptedData(decryptedData, decryptedDataLength);
DebugMessage("Length:%lu \n", decryptedDataLength);
DebugPrintEncryptedData(handshakeData->EncryptedData, 256);
DebugMessage("PrivateKeyLength:%lu \n \n", privateKeyBlobLength);
NTSTATUS DecryptRsaData(
PUCHAR encryptedData, ULONG encryptedDataLength,
PUCHAR privateKeyBlob, ULONG privateKeyBlobLength,
PUCHAR* decryptedData, PULONG decryptedDataLength) {
BCRYPT_ALG_HANDLE algHandle = NULL;
BCRYPT_KEY_HANDLE keyHandle = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DebugMessage("DecryptRsaData 1 \n");
// Open an algorithm provider for RSA.
status = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_RSA_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(status)) {
return status;
}
DebugMessage("DecryptRsaData 2 \n");
// Import the private key.
status = BCryptImportKeyPair(algHandle, NULL, BCRYPT_RSAPRIVATE_BLOB, &keyHandle, privateKeyBlob, privateKeyBlobLength, 0);
if (!NT_SUCCESS(status)) {
BCryptCloseAlgorithmProvider(algHandle, 0);
return status;
}
DebugMessage("DecryptRsaData 3 \n");
// Get the size needed for the decrypted data buffer.
status = BCryptDecrypt(keyHandle, encryptedData, encryptedDataLength, NULL, NULL, 0, NULL, 0, decryptedDataLength, BCRYPT_PAD_PKCS1); // We are failing here
if (!NT_SUCCESS(status)) {
BCryptDestroyKey(keyHandle);
BCryptCloseAlgorithmProvider(algHandle, 0);
return status;
}
DebugMessage("DecryptRsaData 4 \n");
// Allocate the buffer for the decrypted data.
*decryptedData = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, *decryptedDataLength, 'decR');
if (*decryptedData == NULL) {
BCryptDestroyKey(keyHandle);
BCryptCloseAlgorithmProvider(algHandle, 0);
return STATUS_INSUFFICIENT_RESOURCES;
}
DebugMessage("DecryptRsaData 5 \n");
// Perform the decryption.
status = BCryptDecrypt(keyHandle, encryptedData, encryptedDataLength, NULL, NULL, 0, *decryptedData, *decryptedDataLength, decryptedDataLength, BCRYPT_PAD_PKCS1);
if (!NT_SUCCESS(status)) {
ExFreePoolWithTag(*decryptedData, 'decR');
*decryptedData = NULL;
*decryptedDataLength = 0;
}
DebugMessage("DecryptRsaData 6 \n");
BCryptDestroyKey(keyHandle);
BCryptCloseAlgorithmProvider(algHandle, 0);
return status;
}
Редактировать:
Хорошо, я сделал некоторые улучшения, вот ключи для справки. Я также включил макет открытого ключа в BCrypt.
typedef struct _BCRYPT_RSAKEY_BLOB {
ULONG Magic; // A magic number identifying the blob type, for public key, this is BCRYPT_RSAPUBLIC_MAGIC.
ULONG BitLength; // The length of the key, in bits.
ULONG cbPublicExp; // The length of the public exponent, in bytes.
ULONG cbModulus; // The length of the modulus, in bytes.
// Following this header in memory would be:
// - The public exponent (of length cbPublicExp)
// - The modulus (of length cbModulus)
} BCRYPT_RSAKEY_BLOB;
Public Key: 52:53:41:31:00:08:00:00:03:00:00:00:00:01:00:00:00:00:00:00:00:00:00:00:01:00:01:B6:49:39:26:7C:58:59:09:F3:BE:14:10:A8:A2:E1:7A:1B:1E:32:6F:43:65:B5:56:1A:30:24:7D:DB:AA:6A:58:C9:40:AA:B4:91:5C:D3:9B:1C:9D:DD:97:0E:BF:D8:D2:40:1F:C4:27:E4:1E:74:3A:A1:9E:F7:60:B8:C1:47:86:F5:C4:73:F3:A7:DD:AA:F6:6F:81:FA:FD:FE:4E:00:D4:5A:04:2D:24:16:89:D5:03:30:72:36:A5:FE:D2:8E:96:76:EB:04:BC:92:29:3B:77:37:C8:C3:09:27:A9:CD:52:B9:DD:7C:20:AE:60:F8:50:B9:DA:CD:E2:4A:4C:18:7A:66:FC:8E:F7:84:E8:D1:B6:CB:6B:DD:8E:56:F5:99:80:B3:27:44:60:29:2C:3F:1C:72:72:0A:37:FB:FE:37:FA:61:37:2E:A1:EC:35:DE:FA:91:23:A0:91:C4:AC:E4:2F:C9:6D:85:84:94:5A:8A:F3:47:FD:FB:6C:58:7A:62:49:C7:2C:8E:05:E9:4D:79:5B:61:51:CF:2E:1A:4C:22:A5:15:C6:CB:70:8B:D3:E6:0E:1D:C9:62:37:D2:0E:5F:DD:50:B3:05:E1:5B:CE:1A:F5:22:9B:5F:6E:9C:7B:DF:9A:98:19:93:1B:00:93:27:F3:69:43:CD:08:6D:E6:D8:55
Private Key: 52:53:41:32:00:08:00:00:03:00:00:00:00:01:00:00:80:00:00:00:80:00:00:00:01:00:01:B6:49:39:26:7C:58:59:09:F3:BE:14:10:A8:A2:E1:7A:1B:1E:32:6F:43:65:B5:56:1A:30:24:7D:DB:AA:6A:58:C9:40:AA:B4:91:5C:D3:9B:1C:9D:DD:97:0E:BF:D8:D2:40:1F:C4:27:E4:1E:74:3A:A1:9E:F7:60:B8:C1:47:86:F5:C4:73:F3:A7:DD:AA:F6:6F:81:FA:FD:FE:4E:00:D4:5A:04:2D:24:16:89:D5:03:30:72:36:A5:FE:D2:8E:96:76:EB:04:BC:92:29:3B:77:37:C8:C3:09:27:A9:CD:52:B9:DD:7C:20:AE:60:F8:50:B9:DA:CD:E2:4A:4C:18:7A:66:FC:8E:F7:84:E8:D1:B6:CB:6B:DD:8E:56:F5:99:80:B3:27:44:60:29:2C:3F:1C:72:72:0A:37:FB:FE:37:FA:61:37:2E:A1:EC:35:DE:FA:91:23:A0:91:C4:AC:E4:2F:C9:6D:85:84:94:5A:8A:F3:47:FD:FB:6C:58:7A:62:49:C7:2C:8E:05:E9:4D:79:5B:61:51:CF:2E:1A:4C:22:A5:15:C6:CB:70:8B:D3:E6:0E:1D:C9:62:37:D2:0E:5F:DD:50:B3:05:E1:5B:CE:1A:F5:22:9B:5F:6E:9C:7B:DF:9A:98:19:93:1B:00:93:27:F3:69:43:CD:08:6D:E6:D8:55:E0:13:02:06:E2:A0:BB:63:B7:5E:3C:27:8C:75:E4:EA:92:E4:75:A3:2E:9E:AF:C0:8A:87:A0:50:DC:77:1E:B7:D8:3F:7F:9E:42:4D:06:AB:07:3C:77:F5:00:64:8F:C1:F2:86:89:18:B9:4E:B7:FA:26:CE:58:BA:08:B3:B7:86:D3:53:A5:D5:4E:66:D9:CF:3B:18:C2:FB:D0:DE:B5:A2:6F:72:8F:71:D3:D4:0A:36:97:14:9C:ED:AE:03:DC:BC:4E:B4:CF:1F:DC:F5:C7:35:F6:AC:BB:DD:22:AC:CF:B1:BB:83:ED:86:58:E0:72:18:54:EE:A9:8D:76:23:14:03:D0:42:02:F9:4F:E4:FE:00:2A:76:97:6D:1D:F5:6D:F3:27:4C:9D:4D:C6:C2:6A:B9:30:C3:D0:E8:99:F3:C0:6C:ED:25:85:6F:71:CF:D2:0F:F2:18:E5:FC:EA:16:9C:4B:81:C9:3B:4A:DF:84:BF:CD:C4:1F:04:52:2E:77:D7:51:2B:4B:1F:25:14:7C:E4:1B:A2:7B:57:A1:95:49:A0:4B:0A:C6:6B:9E:99:00:29:E9:B0:07:F1:7D:2C:ED:8F:38:20:3A:EA:36:67:53:C5:29:35:2C:63:7A:50:60:3E:70:8D:24:5C:B0:1A:1E:E1:1C:42:1E:D0:C4:6E:03:6E:C7
Выше мы видим, что в начале есть 4-4-байтовые шестнадцатеричные значения в Little-Endian. Magic и BitLength для нас не имеют значения, но следующие два имеют значение. Здесь мы получаем подтверждение, что Экспонента составляет 3 байта, а Модуль — 256 байт.
byte[] bytes = HexStringToByteArray(publicKey);
int bitLength = BitConverter.ToInt32(bytes, 4);
int cbPublicExp = BitConverter.ToInt32(bytes, 8);
int cbModulus = BitConverter.ToInt32(bytes, 12);
Но гораздо более интересная вещь, которую мы замечаем, это то, что показатель степени на самом деле предшествует модулю, а это означает, что я неправильно его прочитал. Кроме того, здесь следует отметить еще одну вещь: на данный момент у нас есть 16 байтов для определенных полей + 3 байта для экспоненты и 256 байтов для модуля, что дает нам только 275 байтов, в то время как длина нашего открытого ключа составляет 283 байта, что означает, что мы имеем 8 байт дополнительно. Основываясь на здравом смысле, мы можем видеть, что есть 8 байтов нулей, пока мы не доберемся до последних 259 байтов, где первые 3 байта равны 01 00 01, также известному как 65 537 в десятичном формате, что является общей экспонентой для RSA.
private static byte[] ExtractModulus(byte[] keyBlob, int modulusLength)
{
int startIndex = keyBlob.Length - modulusLength;
return keyBlob.Skip(startIndex).Take(modulusLength).ToArray();
}
private static byte[] ExtractExponent(byte[] keyBlob, int exponentLength, int modulusLength)
{
int startIndex = keyBlob.Length - exponentLength - modulusLength;
return keyBlob.Skip(startIndex).Take(exponentLength).ToArray();
}
Так вот? Нет, теперь я со 100% точностью знаю, что представляет собой каждый байт открытого ключа, но даже после внесения этих изменений у меня все еще остается та же проблема, что и в первом абзаце. У кого-нибудь есть идеи?
Я пытаюсь использовать RSA 2048 для шифрования между моими программами на C# и C. Для этого я встраиваю закрытый ключ в свою программу на C, а открытый ключ — в программу на C#. Затем моя программа на C# шифрует строку и отправляет ее в мою программу на C для расшифровки. Проблема в том, что я получаю ошибку 0xc000000d при вызове функции BCryptDecrypt внутри моей программы на C, когда я использую любой параметр заполнения, кроме BCRYPT_PAD_NONE. Проблема в том, что в библиотеке C# System.Security.Cryptography есть только опции для использования PKCS1 и OAEP, которые кажутся несовместимыми с версией BCrypt (хотя мой открытый ключ сомнительно). Я определил это, потому что установка var EncryptData = rsa.Encrypt(dataToEncryptBytes, true); в значение true означает, что он использует OAEP, а false означает, что он использует PKCS1, и когда я внес эти изменения, я также изменил обе функции BCryptDecrypt вызывает внутри моей функции DecryptRsaData, где это всегда приводит к ошибке 0xc000000d, также известной как INVALID_PARAMETER. Затем, когда я перешел на использование BCRYPT_PAD_NONE, это не привело к ошибке (поскольку данные больше не проверяются), но полученный PUCHAR после расшифровки является тарабарщиной и не содержит моей исходной строки. Изначально я сгенерировал ключи внутри своей программы на C#, а затем скопировал их ключи в каждую программу, но это было плохо, поскольку отладка открытого ключа была бы намного проще, поскольку у него всего два параметра. Поэтому вместо этого я решил сгенерировать шестнадцатеричный код, связанный как с закрытым, так и с открытым ключом, внутри моей программы на C, а затем скопировать закрытый ключ в массив беззнаковых символов и скопировать открытый ключ в строку C# для дальнейшей обработки. Вот как я это сделал. [code]static void PrintKeyInHex(PUCHAR key, ULONG keySize) { for (ULONG i = 0; i < keySize; ++i) {
DebugMessage("%02X", key[i]); // %02X formats the output as two-digit hexadecimal if (i + 1 < keySize) { DebugMessage(":"); } } DebugMessage("\n"); }
// Open an algorithm provider for RSA. status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_RSA_ALGORITHM, NULL, 0); if (!NT_SUCCESS(status)) { DebugMessage("Failed to open algorithm provider."); goto Cleanup; }
// Generate the key pair. status = BCryptGenerateKeyPair(hAlgorithm, &hKey, 2048, 0); if (!NT_SUCCESS(status)) { DebugMessage("Failed to generate key pair."); goto Cleanup; }
// Finalize the key (make it usable). status = BCryptFinalizeKeyPair(hKey, 0); if (!NT_SUCCESS(status)) { DebugMessage("Failed to finalize key pair."); goto Cleanup; }
// Export the public key. status = BCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, 0, &cbPublicKey, 0); if (!NT_SUCCESS(status)) { DebugMessage("Failed to get public key size."); goto Cleanup; }
pbPublicKey = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, cbPublicKey, '1Tag'); if (!pbPublicKey) { status = STATUS_NO_MEMORY; goto Cleanup; }
status = BCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, pbPublicKey, cbPublicKey, &cbData, 0); if (!NT_SUCCESS(status)) { DebugMessage("Failed to export public key."); goto Cleanup; }
// Export the private key status = BCryptExportKey(hKey, NULL, BCRYPT_RSAPRIVATE_BLOB, NULL, 0, &cbPrivateKey, 0); if (!NT_SUCCESS(status)) { DebugMessage("Failed to get private key size."); goto Cleanup; }
pbPrivateKey = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, cbPrivateKey, '2Tag'); if (!pbPrivateKey) { status = STATUS_NO_MEMORY; goto Cleanup; }
status = BCryptExportKey(hKey, NULL, BCRYPT_RSAPRIVATE_BLOB, pbPrivateKey, cbPrivateKey, &cbData, 0); if (!NT_SUCCESS(status)) { DebugMessage("Failed to export private key."); goto Cleanup; }
Cleanup: if (pbPublicKey) ExFreePoolWithTag(pbPublicKey, '1Tag'); if (pbPrivateKey) ExFreePoolWithTag(pbPrivateKey, '2Tag'); if (hKey) BCryptDestroyKey(hKey); if (hAlgorithm) BCryptCloseAlgorithmProvider(hAlgorithm, 0);
return status; } [/code] Это гарантировало правильность моего закрытого ключа, что просто означает, что мне нужно было заставить открытый ключ работать с алгоритмом C# System.Security.Cryptography RSA. Но поскольку я знал, что открытый ключ заканчивается модулем (256 байт), то экспонентой (3 байта) в конце было довольно легко. Затем, поскольку я не был уверен в порядке байтов (также просто для того, чтобы охватить все основы), я решил перебрать его, попробовав все 4 комбинации байтов и отправив их в свою программу на C. Под этим я подразумеваю, что я бы вызвал следующий метод C# EncryptDataWithPublicKey 4 раза с i от 0 до 3, гарантируя, что какие-либо ошибки не связаны с неправильным порядком байтов. Несмотря на это, я все равно получал одни и те же проблемы, описанные в первом абзаце, все 4 раза. [code] public static byte[] EncryptDataWithPublicKey(string dataToEncrypt, int i) { RSAParameters rsaParams = ConvertHexToRSAParameters(hexPublicKey, i); using (var rsa = new RSACryptoServiceProvider()) { // Load the RSA public key rsa.ImportParameters(rsaParams);
// Convert the data to encrypt to a byte array var dataToEncryptBytes = Encoding.UTF8.GetBytes(dataToEncrypt);
// Encrypt the data. OAEP padding is recommended for new applications. var encryptedData = rsa.Encrypt(dataToEncryptBytes, false); // Set to true to use OAEP padding
// Extract the modulus and exponent from the blob byte[] exponent = ExtractExponent(bytes, 3); // Assuming 3 bytes for exponent byte[] modulus = ExtractModulus(bytes, 256);
return rsaParams; } [/code] В любом случае, когда моя программа на C пытается расшифровать исходную строку, я получаю статус 0xc000000d при использовании BCRYPT_PAD_PKCS1 или BCRYPT_PAD_OAEP, в то время как BCRYPT_PAD_NONE > не приводит к проблемам со статусом, но к нечитаемым данным. Я предполагаю, что либо библиотека C, известная как BCRYPT_PAD_OAEP и BCRYPT_PAD_PKCS1 BCrypt, несовместима с версией C#, либо я неправильно создаю открытые ключи. Я пришел к такому выводу, поскольку знаю, что закрытый ключ действителен на 100%, поскольку он сгенерирован алгоритмом BCrypt, и я знаю, что шестнадцатеричный код открытого ключа также правильный, но я не уверен, конвертирую ли я шестнадцатеричный ключ открытого ключа в правильный формат. Кроме того, последнее замечание, основанное на предыдущем тестировании функции BCryptImportKeyPair, могло дать сбой, когда я неправильно отформатировал открытый ключ в своем приложении C#, но я вполне уверен, что это просто проверка длины, которая говорит мне, что длина моего поля правильна, но с другой стороны, я также пробую все возможности порядка байтов, поэтому я действительно не понимаю, в чем может быть проблема. При этом ниже приведен мой код C, который я использую для расшифровки реальных данных. [code]unsigned char g_privateKeyBlob[] = { 0x52, 0x53, 0x41, 0x32, ... }; // I cut off the rest since its 539 bytes long
static void DebugPrintEncryptedData(UCHAR* data, ULONG length) { for (ULONG i = 0; i < length; ++i) { DebugMessage("%02X ", data[i]); if ((i + 1) % 16 == 0) { DebugMessage("\n"); // New line every 16 bytes for readability } } DebugMessage("\n"); // New line at the end }
// This code then runs which calls the bellow function and then formats the data in a readable format with the above function ULONG privateKeyBlobLength = sizeof(g_privateKeyBlob); // Get the size of your private key blob PUCHAR decryptedData = NULL; ULONG decryptedDataLength = 0;
// Open an algorithm provider for RSA. status = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_RSA_ALGORITHM, NULL, 0); if (!NT_SUCCESS(status)) { return status; } DebugMessage("DecryptRsaData 2 \n");
// Import the private key. status = BCryptImportKeyPair(algHandle, NULL, BCRYPT_RSAPRIVATE_BLOB, &keyHandle, privateKeyBlob, privateKeyBlobLength, 0); if (!NT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(algHandle, 0); return status; } DebugMessage("DecryptRsaData 3 \n");
// Get the size needed for the decrypted data buffer. status = BCryptDecrypt(keyHandle, encryptedData, encryptedDataLength, NULL, NULL, 0, NULL, 0, decryptedDataLength, BCRYPT_PAD_PKCS1); // We are failing here if (!NT_SUCCESS(status)) { BCryptDestroyKey(keyHandle); BCryptCloseAlgorithmProvider(algHandle, 0); return status; } DebugMessage("DecryptRsaData 4 \n");
// Allocate the buffer for the decrypted data. *decryptedData = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, *decryptedDataLength, 'decR'); if (*decryptedData == NULL) { BCryptDestroyKey(keyHandle); BCryptCloseAlgorithmProvider(algHandle, 0); return STATUS_INSUFFICIENT_RESOURCES; } DebugMessage("DecryptRsaData 5 \n");
Редактировать: Хорошо, я сделал некоторые улучшения, вот ключи для справки. Я также включил макет открытого ключа в BCrypt. [code]typedef struct _BCRYPT_RSAKEY_BLOB { ULONG Magic; // A magic number identifying the blob type, for public key, this is BCRYPT_RSAPUBLIC_MAGIC. ULONG BitLength; // The length of the key, in bits. ULONG cbPublicExp; // The length of the public exponent, in bytes. ULONG cbModulus; // The length of the modulus, in bytes. // Following this header in memory would be: // - The public exponent (of length cbPublicExp) // - The modulus (of length cbModulus) } BCRYPT_RSAKEY_BLOB;
Public Key: 52:53:41:31:00:08:00:00:03:00:00:00:00:01:00:00:00:00:00:00:00:00:00:00:01:00:01:B6:49:39:26:7C:58:59:09:F3:BE:14:10:A8:A2:E1:7A:1B:1E:32:6F:43:65:B5:56:1A:30:24:7D:DB:AA:6A:58:C9:40:AA:B4:91:5C:D3:9B:1C:9D:DD:97:0E:BF:D8:D2:40:1F:C4:27:E4:1E:74:3A:A1:9E:F7:60:B8:C1:47:86:F5:C4:73:F3:A7:DD:AA:F6:6F:81:FA:FD:FE:4E:00:D4:5A:04:2D:24:16:89:D5:03:30:72:36:A5:FE:D2:8E:96:76:EB:04:BC:92:29:3B:77:37:C8:C3:09:27:A9:CD:52:B9:DD:7C:20:AE:60:F8:50:B9:DA:CD:E2:4A:4C:18:7A:66:FC:8E:F7:84:E8:D1:B6:CB:6B:DD:8E:56:F5:99:80:B3:27:44:60:29:2C:3F:1C:72:72:0A:37:FB:FE:37:FA:61:37:2E:A1:EC:35:DE:FA:91:23:A0:91:C4:AC:E4:2F:C9:6D:85:84:94:5A:8A:F3:47:FD:FB:6C:58:7A:62:49:C7:2C:8E:05:E9:4D:79:5B:61:51:CF:2E:1A:4C:22:A5:15:C6:CB:70:8B:D3:E6:0E:1D:C9:62:37:D2:0E:5F:DD:50:B3:05:E1:5B:CE:1A:F5:22:9B:5F:6E:9C:7B:DF:9A:98:19:93:1B:00:93:27:F3:69:43:CD:08:6D:E6:D8:55
Private Key: 52:53:41:32:00:08:00:00:03:00:00:00:00:01:00:00:80:00:00:00:80:00:00:00:01:00:01:B6:49:39:26:7C:58:59:09:F3:BE:14:10:A8:A2:E1:7A:1B:1E:32:6F:43:65:B5:56:1A:30:24:7D:DB:AA:6A:58:C9:40:AA:B4:91:5C:D3:9B:1C:9D:DD:97:0E:BF:D8:D2:40:1F:C4:27:E4:1E:74:3A:A1:9E:F7:60:B8:C1:47:86:F5:C4:73:F3:A7:DD:AA:F6:6F:81:FA:FD:FE:4E:00:D4:5A:04:2D:24:16:89:D5:03:30:72:36:A5:FE:D2:8E:96:76:EB:04:BC:92:29:3B:77:37:C8:C3:09:27:A9:CD:52:B9:DD:7C:20:AE:60:F8:50:B9:DA:CD:E2:4A:4C:18:7A:66:FC:8E:F7:84:E8:D1:B6:CB:6B:DD:8E:56:F5:99:80:B3:27:44:60:29:2C:3F:1C:72:72:0A:37:FB:FE:37:FA:61:37:2E:A1:EC:35:DE:FA:91:23:A0:91:C4:AC:E4:2F:C9:6D:85:84:94:5A:8A:F3:47:FD:FB:6C:58:7A:62:49:C7:2C:8E:05:E9:4D:79:5B:61:51:CF:2E:1A:4C:22:A5:15:C6:CB:70:8B:D3:E6:0E:1D:C9:62:37:D2:0E:5F:DD:50:B3:05:E1:5B:CE:1A:F5:22:9B:5F:6E:9C:7B:DF:9A:98:19:93:1B:00:93:27:F3:69:43:CD:08:6D:E6:D8:55:E0:13:02:06:E2:A0:BB:63:B7:5E:3C:27:8C:75:E4:EA:92:E4:75:A3:2E:9E:AF:C0:8A:87:A0:50:DC:77:1E:B7:D8:3F:7F:9E:42:4D:06:AB:07:3C:77:F5:00:64:8F:C1:F2:86:89:18:B9:4E:B7:FA:26:CE:58:BA:08:B3:B7:86:D3:53:A5:D5:4E:66:D9:CF:3B:18:C2:FB:D0:DE:B5:A2:6F:72:8F:71:D3:D4:0A:36:97:14:9C:ED:AE:03:DC:BC:4E:B4:CF:1F:DC:F5:C7:35:F6:AC:BB:DD:22:AC:CF:B1:BB:83:ED:86:58:E0:72:18:54:EE:A9:8D:76:23:14:03:D0:42:02:F9:4F:E4:FE:00:2A:76:97:6D:1D:F5:6D:F3:27:4C:9D:4D:C6:C2:6A:B9:30:C3:D0:E8:99:F3:C0:6C:ED:25:85:6F:71:CF:D2:0F:F2:18:E5:FC:EA:16:9C:4B:81:C9:3B:4A:DF:84:BF:CD:C4:1F:04:52:2E:77:D7:51:2B:4B:1F:25:14:7C:E4:1B:A2:7B:57:A1:95:49:A0:4B:0A:C6:6B:9E:99:00:29:E9:B0:07:F1:7D:2C:ED:8F:38:20:3A:EA:36:67:53:C5:29:35:2C:63:7A:50:60:3E:70:8D:24:5C:B0:1A:1E:E1:1C:42:1E:D0:C4:6E:03:6E:C7 [/code] Выше мы видим, что в начале есть 4-4-байтовые шестнадцатеричные значения в Little-Endian. Magic и BitLength для нас не имеют значения, но следующие два имеют значение. Здесь мы получаем подтверждение, что Экспонента составляет 3 байта, а Модуль — 256 байт. [code] byte[] bytes = HexStringToByteArray(publicKey); int bitLength = BitConverter.ToInt32(bytes, 4); int cbPublicExp = BitConverter.ToInt32(bytes, 8); int cbModulus = BitConverter.ToInt32(bytes, 12); [/code] Но гораздо более интересная вещь, которую мы замечаем, это то, что показатель степени на самом деле предшествует модулю, а это означает, что я неправильно его прочитал. Кроме того, здесь следует отметить еще одну вещь: на данный момент у нас есть 16 байтов для определенных полей + 3 байта для экспоненты и 256 байтов для модуля, что дает нам только 275 байтов, в то время как длина нашего открытого ключа составляет 283 байта, что означает, что мы имеем 8 байт дополнительно. Основываясь на здравом смысле, мы можем видеть, что есть 8 байтов нулей, пока мы не доберемся до последних 259 байтов, где первые 3 байта равны 01 00 01, также известному как 65 537 в десятичном формате, что является общей экспонентой для RSA. [code] private static byte[] ExtractModulus(byte[] keyBlob, int modulusLength) { int startIndex = keyBlob.Length - modulusLength; return keyBlob.Skip(startIndex).Take(modulusLength).ToArray(); }
private static byte[] ExtractExponent(byte[] keyBlob, int exponentLength, int modulusLength) { int startIndex = keyBlob.Length - exponentLength - modulusLength; return keyBlob.Skip(startIndex).Take(exponentLength).ToArray(); } [/code] Так вот? Нет, теперь я со 100% точностью знаю, что представляет собой каждый байт открытого ключа, но даже после внесения этих изменений у меня все еще остается та же проблема, что и в первом абзаце. У кого-нибудь есть идеи?