RSA Encryption / Decryption Error Using Next Generation CryptoAPI (CNG) on Windows?

I wrote code to encrypt and decrypt data using a hard-coded RSA key pair that was previously generated using CNG. This is a simple program that simply generates some random input, encrypts it with a public key, and then decrypts the received encrypted buffer using the private key. I print all input, intermediate, and output stages to compare whether the decrypted plaintext is the same as the original source, and repeat all encryption-decryption 10 times.

However, I observe that in some cases encryption and decryption are fine, but in other cases the decrypted plaintext does not match the input plaintext at all. Such error cases are completely random and arbitrary, and there seems to be no scheme for these errors.

Is this a bug in the implementation of CNG RSA or am I doing something wrong?

Code follows:


#include <windows.h> #include <stdio.h> #include <stdlib.h> #include <bcrypt.h> #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) #define PrivateKeySize 283 #define PublicKeySize 155 #define InputDataSize 128 PUCHAR encryptedBuffer; ULONG encryptedBufferSize = 128; VOID printMem(PVOID Mem, int length) { int i; for (i = 0; i < length; i++) printf("%02x ", ((unsigned char *)Mem)[i]); } VOID Decrypt() { unsigned char PrivateKey[PrivateKeySize] = {0x52, 0x53, 0x41, 0x32, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xB7, 0x50, 0x52, 0xDD, 0x58, 0xE4, 0x96, 0xAF, 0x91, 0xE5, 0xB2, 0x7B, 0x0A, 0xE6, 0xAA, 0x1F, 0x71, 0x8A, 0x66, 0xC3, 0xF0, 0x21, 0xD8, 0xE6, 0x2C, 0xD6, 0x25, 0x2E, 0x77, 0x3C, 0x61, 0x08, 0x1B, 0x69, 0xE7, 0x58, 0xDF, 0x3B, 0x07, 0xFE, 0xF1, 0xDB, 0xBF, 0xA6, 0x35, 0xDF, 0xC7, 0x49, 0x06, 0xC8, 0xDB, 0x74, 0x2A, 0xB9, 0xED, 0xB3, 0x04, 0x80, 0x75, 0x5F, 0x71, 0x2C, 0xD0, 0x14, 0x0E, 0x81, 0x18, 0x00, 0x5E, 0x34, 0x5A, 0xC2, 0x3A, 0x84, 0x63, 0xB1, 0x6B, 0x04, 0x21, 0x49, 0x7F, 0xE0, 0xF3, 0x52, 0x5E, 0x61, 0x43, 0xB1, 0x8F, 0x7C, 0xF2, 0x74, 0x29, 0x28, 0x69, 0x20, 0x36, 0xC0, 0x92, 0x17, 0x42, 0x99, 0x72, 0xE5, 0xE7, 0x82, 0xBE, 0x8E, 0x3B, 0x3F, 0xC9, 0x0A, 0xE1, 0xC4, 0x63, 0x68, 0x73, 0x1D, 0x67, 0x8D, 0xC0, 0xA3, 0xB4, 0xBA, 0xF0, 0xB7, 0xB0, 0x9B, 0xBB, 0x3F, 0xB8, 0x6E, 0xC0, 0x34, 0x1E, 0xA0, 0x01, 0x4B, 0x6D, 0x47, 0x73, 0x3F, 0xA5, 0x39, 0x05, 0x27, 0xD4, 0xD1, 0x38, 0x34, 0x32, 0x2C, 0x5B, 0x03, 0x5F, 0x16, 0x21, 0x64, 0x04, 0xD5, 0x19, 0xDB, 0xE7, 0x80, 0xDA, 0xBD, 0xC4, 0x1E, 0xAB, 0x61, 0xC8, 0x84, 0xDF, 0x54, 0x16, 0x77, 0x98, 0x9B, 0x90, 0x03, 0x83, 0xC4, 0x8D, 0x25, 0xB1, 0x32, 0x67, 0x77, 0x6A, 0x1C, 0x64, 0x2D, 0xFA, 0x9E, 0xB9, 0x26, 0xB5, 0xF8, 0x47, 0x4A, 0x9C, 0x35, 0x89, 0x5F, 0x12, 0x0E, 0xFF, 0x60, 0x87, 0x1E, 0x27, 0xC1, 0xC5, 0x7C, 0x77, 0x0A, 0xAE, 0x11, 0x37, 0xE3, 0x42, 0x9B, 0xAF, 0x9D, 0xBC, 0xC2, 0x52, 0xF8, 0x85, 0xBA, 0xED, 0x8E, 0xC3, 0x73, 0x04, 0x0A, 0x53, 0xD2, 0x1D, 0xEF, 0xA0, 0x6A, 0xCD, 0xBE, 0x93, 0x49, 0x34, 0x3A, 0xBD, 0xDF, 0x6A, 0x33, 0x25, 0x91, 0xFC, 0xE7}; BCRYPT_ALG_HANDLE hAlgorithm = NULL; BCRYPT_KEY_HANDLE hKey = NULL; ULONG plaintextSize = 128; PUCHAR decryptedBuffer; ULONG decryptedBufferSize; NTSTATUS status; status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_RSA_ALGORITHM, NULL, 0); if (!NT_SUCCESS(status)) { printf("Failed to get algorithm provider..status : %08x\n",status); goto cleanup; } status = BCryptImportKeyPair( hAlgorithm, NULL, BCRYPT_RSAPRIVATE_BLOB, &hKey, PrivateKey, PrivateKeySize, BCRYPT_NO_KEY_VALIDATION); if (!NT_SUCCESS(status)) { printf("Failed to import Private key..status : %08x\n",status); goto cleanup; } status = BCryptDecrypt( hKey, encryptedBuffer, encryptedBufferSize, NULL, NULL, 0, NULL, 0, &decryptedBufferSize, 0); if (!NT_SUCCESS(status)) { printf("Failed to get required size of buffer..status : %08x\n", status); goto cleanup; } decryptedBuffer = (PUCHAR)HeapAlloc (GetProcessHeap (), 0, decryptedBufferSize); if (decryptedBuffer == NULL) { printf("failed to allocate memory for buffer\n"); goto cleanup; } status = BCryptDecrypt( hKey, encryptedBuffer, encryptedBufferSize, NULL, NULL, 0, decryptedBuffer, decryptedBufferSize, &decryptedBufferSize, 0); if (!NT_SUCCESS(status)) { printf("Failed decrypt buffer..status : %08x\n",status); goto cleanup; } printf("Decrypted buffer\n"); printMem(decryptedBuffer, decryptedBufferSize); printf("\n\n"); cleanup: HeapFree(GetProcessHeap(), 0, decryptedBuffer); BCryptDestroyKey(hKey); BCryptCloseAlgorithmProvider(hAlgorithm, 0); } VOID Encrypt() { unsigned char PublicKey[PublicKeySize] = {0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xB7, 0x50, 0x52, 0xDD, 0x58, 0xE4, 0x96, 0xAF, 0x91, 0xE5, 0xB2, 0x7B, 0x0A, 0xE6, 0xAA, 0x1F, 0x71, 0x8A, 0x66, 0xC3, 0xF0, 0x21, 0xD8, 0xE6, 0x2C, 0xD6, 0x25, 0x2E, 0x77, 0x3C, 0x61, 0x08, 0x1B, 0x69, 0xE7, 0x58, 0xDF, 0x3B, 0x07, 0xFE, 0xF1, 0xDB, 0xBF, 0xA6, 0x35, 0xDF, 0xC7, 0x49, 0x06, 0xC8, 0xDB, 0x74, 0x2A, 0xB9, 0xED, 0xB3, 0x04, 0x80, 0x75, 0x5F, 0x71, 0x2C, 0xD0, 0x14, 0x0E, 0x81, 0x18, 0x00, 0x5E, 0x34, 0x5A, 0xC2, 0x3A, 0x84, 0x63, 0xB1, 0x6B, 0x04, 0x21, 0x49, 0x7F, 0xE0, 0xF3, 0x52, 0x5E, 0x61, 0x43, 0xB1, 0x8F, 0x7C, 0xF2, 0x74, 0x29, 0x28, 0x69, 0x20, 0x36, 0xC0, 0x92, 0x17, 0x42, 0x99, 0x72, 0xE5, 0xE7, 0x82, 0xBE, 0x8E, 0x3B, 0x3F, 0xC9, 0x0A, 0xE1, 0xC4, 0x63, 0x68, 0x73, 0x1D, 0x67, 0x8D, 0xC0, 0xA3, 0xB4, 0xBA, 0xF0, 0xB7, 0xB0, 0x9B}; unsigned char InputData[InputDataSize]; BCRYPT_ALG_HANDLE hAlgorithm = NULL; BCRYPT_KEY_HANDLE hKey = NULL; NTSTATUS status; for (int i=0; i<128; i++) InputData[i] = (unsigned char)rand(); printf("Random Data is \n"); printMem(InputData, InputDataSize); printf("\n\n"); status = BCryptOpenAlgorithmProvider( &hAlgorithm, BCRYPT_RSA_ALGORITHM, NULL, 0 ); if (!NT_SUCCESS(status)) { printf("Failed to get algorithm provider..status : %08x\n",status); goto cleanup; } status = BCryptImportKeyPair( hAlgorithm, NULL, BCRYPT_RSAPUBLIC_BLOB, &hKey, PublicKey, 155, BCRYPT_NO_KEY_VALIDATION ); if (!NT_SUCCESS(status)) { printf("Failed to import Private key..status : %08x\n",status); goto cleanup; } status = BCryptEncrypt( hKey, InputData, InputDataSize, NULL, NULL, 0, NULL, 0, &encryptedBufferSize, 0 ); if (!NT_SUCCESS(status)) { printf("Failed to get required size of buffer..status : %08x\n",status); goto cleanup; } encryptedBuffer = (PUCHAR)HeapAlloc (GetProcessHeap (), 0, encryptedBufferSize); if (encryptedBuffer == NULL) { printf("failed to allocate memory for blindedFEKBuffer\n"); goto cleanup; } status = BCryptEncrypt( hKey, InputData, InputDataSize, NULL, NULL, 0, encryptedBuffer, encryptedBufferSize, &encryptedBufferSize, 0 ); if (!NT_SUCCESS(status)) { printf("Failed encrypt data..status : %08x\n",status); goto cleanup; } printf("Encrypted Data\n"); printMem(encryptedBuffer, encryptedBufferSize); printf("\n\n"); cleanup: if(hKey) BCryptDestroyKey(hKey); if(hAlgorithm) BCryptCloseAlgorithmProvider(hAlgorithm, 0); } int __cdecl wmain( int argc, __in_ecount(argc) LPWSTR *wargv) { int i; for (i=0; i<10; i++) { Encrypt(); Decrypt(); } } 
+4
source share
3 answers

This is not an error in CNG, it is an artifact of how raw RSA encryption works. Some of the random values ​​that you encrypt are larger than the key module (the module is the value of 2 primes in RSA keys multiplied together), which means that the values ​​flow around (if you set InputData[0] = 0 , you will see that the input and output always match).

You will also want to either pass BCRYPT_PAD_PKCS1 or BCRYPT_PAD_OAEP to the dwFlags BCryptEncrypt / BCryptDecrypt , since raw RSA encryption is not secure. If you use BCRYPT_PAD_OAEP , you will also need to provide a BCRYPT_OAEP_PADDING_INFO structure .

+1
source

Is it possible that your problem is with passing NULL IV to BCryptEncrypt , as noted in the community notes below:

BCryptEncrypt Function

-1
source

I tried your code and if I create the InputData generation static and non-random, for example:

 ZeroMemory( InputData, InputDataSize ); std::string strInput = "This is just a test with a long long... very long string"; for( int i = 0; i < (int)strInput.size(); i++ ) { InputData[i] = strInput[i]; } 

It does not return any errors, I think there are problems with the extreme values ​​of rand ().

-1
source

All Articles