Crypt in C++ and Decrypt in C# (and C++)
[stextbox id=”info” caption=”Comment or Leave a Message”]
Please Comment or Leave a Message if this is what you are looking for or if you have any question/comment/suggestion/request.
[/stextbox]
So, now you (actually Me) want to encode and crypt something in C++ and then (maybe after a network message), decrypt and decode this info in C#.
Let me explain exactly what I mean with encode/decode, crypt/decrypt.
We use the user’s two public/private key pairs to crypt/decrypt a session key that we will use to encode/decode a message. If all this make sense to you then let’s start.
First of all, let me answer one natural question: “Why all this?”
The reason of the double-step procedure to encode a message is due to two major reason:
- crypt/decrypt (or signing and verifying) with an algorithm for public-key cryptography such as RSA (the one that I am using), uses a key pair, where one key is used to crypt some data, and the other (asymmetric) key is used to decrypt it. The things can actually work in both ways and in one case we will talk about signing and verifying.
The protection here is very strong but the cost is resources, especially time. The algorithm is slow compared to others and for this it is not suitable for protecting a large amount of data. Another reason seems to be security itself, where many blocks encrypted with the same RSA key can help an attacker to reach his intent. - encode/decode instead is achieved using a symmetric key algorithm that can be very fast and if it is a stream cipher, it works great with streaming data :). I will use RC4 for this.
The Scenario
Two PC, connected through internet, where one behave as a server and the other is the client.
We assume that the server contains (well protected) its private and public keys that it will use to sign and decrypt some data that will go to or will come from the client.
For the client we assume that he knows the server’s public key and that he will use this to verify and crypt the data that he will receive from or that he will send to the server.
More about the Key Pair and Session Key
With Windows you can store/retrieve user keys (key pair) using the Crypto API. With it you can manage machine or user key in a quite simple way.
On the server we will use this API (in C++) to generate/retrieve (and then store) our machine key pair and we will tell the client our public key.
On the client will create a session key (C++) that will be used to encode a message with RC4.
Because the client knows the server public key, he will then use this to encrypt the session key.
Once this is done, the client will proceed to pack the two things together (encrypted session key plus the message) and send the packet to the server.
The Server at this point will save the message in a file, because we want to have more fun later on.
In fact, to make things more interesting then we will create a .NET (C#) project that will retrieve the same key pair previously stored, reads the file, imports the session key and decodes the message. All this in pure .NET.
Does it not seem fun?
To simplify stuff I will not use any network code and I will assume that the data are passed somehow between client and server. This can be network, file, or whatever (DB, mind power,…).
The only storage that I will use will be a file (the encrypted file) to allow a simple code in C# to open it and read and decrypt its content.
To simplify the code and shrink it to the fundamental parts I will provide code snippets that are mostly implemented as simple methods (C style) and I will assume the presence of some globally scoped variable (that should really be class scoped variable in a real environment). This will hopefully simplify the understanding of the concepts without losing the focus tracking the objects relationships.
Let’s start then.
Initial Server Code (C++)
In C++ we can easily use the Crypto API to do all we can (reasonably) think about when we want to protect our data.
This is available including “wincrypt.h” in your code.
The first thing to do is acquire the context where our secret keys are stored. This can be easily done with a code such as:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
HCRYPTPROV hCryptProvServer; char KEY_CONTAINER_SERVER[] = "Pippo Container"; LPCSTR SERVICE_PROVIDER = NULL; BOOL ServerAcquireContext() { // SERVER: Acquire Server Key Context BOOL res = ::CryptAcquireContext( &hCryptProvServer, KEY_CONTAINER_SERVER, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET ); if ( !res ) { // If we fail to acquire the context then we must create one! // Try to get a handle to a new key container res = ::CryptAcquireContext( &hCryptProvServer, KEY_CONTAINER_SERVER, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET ); if (!res) { printf("SERVER: Error Acquiring Key Context\n"); return FALSE; } } printf("SERVER: Key Context Acquired\n"); return TRUE; } |
After this we can retrieve our exchange keys from the container. If the keys doesn’t exist I will create a brand new pair. This code does exactly this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
HCRYPTKEY hKeyPairServer; BOOL ServerGetExchangeKeys() { // SERVER: Acquire Server Exchange Keys BOOL res = ::CryptGetUserKey( hCryptProvServer, AT_KEYEXCHANGE, &hKeyPairServer ); if (!res) { printf("SERVER: No exchange key is available. Generating new one...\n"); const DWORD RSA2048BIT_KEY = 0x8000000; DWORD dwParams; dwParams = RSA2048BIT_KEY | CRYPT_EXPORTABLE | CRYPT_NO_SALT; //set the key length to 2048 bits, allow the keys to be exported, no salt res = ::CryptGenKey( hCryptProvServer, AT_KEYEXCHANGE, dwParams, &hKeyPairServer ); if (!res) { printf("SERVER: Unable to generate exchange keys\n"); return FALSE; } printf("SERVER: Exchange keys generated\n"); } else { printf("SERVER: Exchange keys retrieved\n"); } return TRUE; } |
Happy with the result the next thing to do is to export our public key and give this to our client… or everyone… after all is a public key!
In my sample code I will use a memory buffer. The important thing here is that we are not here to cheat and this buffer will be the only thing that we will give to the client code pretending that it is a completely separate application. The export looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
BOOL ServerExportExchangePublicKey(BYTE **ppExportedPublicKeyBlob, DWORD &dwPublicKeyBufferSize) { // SERVER: Export Server Exchange Public Key // Get the size of the buffer needed for the Public Key BOOL res = ::CryptExportKey( hKeyPairServer, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyBufferSize ); if (!res) { printf("SERVER: Unable to retrieve the buffer size to export the public key\n"); return -1; } *ppExportedPublicKeyBlob = new BYTE[dwPublicKeyBufferSize]; memset( *ppExportedPublicKeyBlob, 0, dwPublicKeyBufferSize ); res = ::CryptExportKey( hKeyPairServer, NULL, PUBLICKEYBLOB, 0, *ppExportedPublicKeyBlob, &dwPublicKeyBufferSize ); if (!res) { printf("SERVER: Could not export the public key blob\n"); delete[] *ppExportedPublicKeyBlob; return FALSE; } printf("SERVER: Public key exported\n"); return TRUE; } |
If everything is fine until now we can temporarily leave our server and switch to our client code.
Client Code (C++)
Because we are still working with Crypto API, the pattern is similar and so the first thing we have to do is acquire the container context in the same way we already did for the server.
The only difference is that we will use a different container to continue simulating a separate environment:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
HCRYPTPROV hCryptProvClient; char KEY_CONTAINER_CLIENT[] = "Pippo Container Client"; BOOL ClientAcquireContext() { // CLIENT: Acquire Client Key Context BOOL res = ::CryptAcquireContext( &hCryptProvClient, KEY_CONTAINER_CLIENT, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET ); if ( !res ) { // If we fail to acquire the context then we must create one! // Try to get a handle to a new key container res = ::CryptAcquireContext( &hCryptProvClient, KEY_CONTAINER_CLIENT, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET ); if (!res) { printf("Error Acquiring Context\n"); return FALSE; } } printf("CLIENT: Key Context Acquired\n"); return TRUE; } |
We can then import immediately the server public key:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
HCRYPTKEY hImportedPublicKeyClient; BOOL ClientImportServerPublicKey(BYTE* pExportedPublicKeyBlob, DWORD dwPublicBlobSize) { // CLIENT: Import Server Public Key BOOL res = ::CryptImportKey(hCryptProvClient, pExportedPublicKeyBlob, dwPublicBlobSize, 0, 0, &hImportedPublicKeyClient); if (!res) { printf("CLIENT: Unable to import the server public key\n"); return FALSE; } printf("CLIENT: Server public key imported\n"); return TRUE; } |
The initial “handshaking” is completed now! The server is configured with his Key Pair and the client has received the the public part of this pair.
The next move is in client’s hand now. It has the freedom to choose a session key that will be used to encode a secret message.
For this task I am simply generating a brand new session key, especially generated for the algorithm that I will use to encode the message:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
HCRYPTKEY hSessionKeyClient; BOOL ClientGenerateSessionKey() { // CLIENT: Generate session key BOOL res = ::CryptGenKey( hCryptProvClient, CALG_RC4, CRYPT_EXPORTABLE, &hSessionKeyClient ); if (!res) { printf("CLIENT: Unable to generate the session key\n"); return FALSE; } printf("CLIENT: Session key generated\n"); return TRUE; } |
With this key I can finally encode my message!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
BOOL ClientEncryptMessage(char *pMessage, BYTE **ppMessageEncrypted, DWORD &dwMessageEncryptedBufferSize) { // CLIENT: Encrypt message dwMessageEncryptedBufferSize = (DWORD)strlen(pMessage) + 1; // Add the null terminator to simplify later DWORD dwMessageBufferSizeBeforeEncryption = dwMessageEncryptedBufferSize; BOOL res = ::CryptEncrypt( hSessionKeyClient, NULL, 1, 0, NULL, &dwMessageEncryptedBufferSize, 0 ); if (!res) { printf("CLIENT: Unable to detect the buffer size\n"); return FALSE; } if (dwMessageEncryptedBufferSize < dwMessageBufferSizeBeforeEncryption) { dwMessageEncryptedBufferSize = dwMessageBufferSizeBeforeEncryption; } *ppMessageEncrypted = new BYTE[dwMessageEncryptedBufferSize]; strcpy_s((char *)(*ppMessageEncrypted), dwMessageEncryptedBufferSize, pMessage); // CLIENT: Encrypt the message with the session key res = ::CryptEncrypt( hSessionKeyClient, NULL, 1, 0, (*ppMessageEncrypted), &dwMessageBufferSizeBeforeEncryption, dwMessageEncryptedBufferSize ); if (!res) { printf("CLIENT: Unable to encrypt the message with the session key\n"); return FALSE; } printf("CLIENT: Message encrypted with the session key\n"); return TRUE; } |
Cool! The message is now protected… well, almost. In order to complete the transmission of the encrypted message, back to the server, we still need to take care of a few more details.
The first one is related to the protection of the session key itself. The server needs it in order to decode the message, but for sure we cannot send it as it is otherwise all this messing around with keys would be just a waste of time (human and cpu).
As already mentioned (and I hope obvious at this point) we will send our session key encrypted with the server public key. The server, and only the server, will then be able to decrypt it.
Enough chatting, let’s go to encrypt the session key then:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
BOOL ClientExportEncryptedSessionKey(BYTE **ppExportedEncryptedSessionKey, DWORD &dwExportedSessionKeyBufferSize) { // CLIENT: Now Export the Session Key, encrypting it with the public key pair BOOL res = ::CryptExportKey( hSessionKeyClient, hImportedPublicKeyClient, SIMPLEBLOB, 0, NULL, &dwExportedSessionKeyBufferSize ); if (!res) { printf("CLIENT: Unable to retrieve the buffer size to export the session key\n"); return FALSE; } *ppExportedEncryptedSessionKey = new BYTE[dwExportedSessionKeyBufferSize]; memset( *ppExportedEncryptedSessionKey, 0, dwExportedSessionKeyBufferSize ); res = ::CryptExportKey( hSessionKeyClient, hImportedPublicKeyClient, SIMPLEBLOB, 0, *ppExportedEncryptedSessionKey, &dwExportedSessionKeyBufferSize ); if (!res) { printf("CLIENT: Could not export the (encrypted) session key\n"); delete[] *ppExportedEncryptedSessionKey; return FALSE; } printf("CLIENT: Session key exported (encrypted)\n"); return TRUE; } |
In my sample scenario I will simulate the transmission of the encrypted message using a simple file where I will write all the server needs to successfully decrypt the message.
My main focus here is to write a C# server that will be able to do this.
Anyway, just to complete the circle in the C++ world, I will add few more lines that will show how read and decrypt the same message in C++ as well, although I will not follow the same path and I will just continue to use memory buffers, just to avoid to add more details that are not relevant for what I have in mind now. If there is the need I will write another side article for it.
This said, let’s go back to our server (C++).
More Server Code (C++)
Assuming the message as received together with the encrypted key the first thing to do is decrypt the key of course:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
HCRYPTKEY hSessionKeyServer; BOOL ServerImportEncryptedSessionKey(BYTE *pClientExportedEncryptedSessionKey, DWORD dwClientExportedEncryptedSessionKeySize) { // SERVER: Imports the session key decrypting it with the server private key BOOL res = ::CryptImportKey(hCryptProvServer, pClientExportedEncryptedSessionKey, dwClientExportedEncryptedSessionKeySize, hKeyPairServer, 0, &hSessionKeyServer); if (!res) { printf("SERVER: Unable to import the encrypted session key\n"); return FALSE; } printf("SERVER: Encrypted session key imported\n"); return TRUE; } |
As a result we will have another key object that can be used (at last!) to decrypt the message:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
BOOL ServerDecryptMessage(BYTE* pMessageEncrypted, DWORD dwEncryptedMessageBufferSize, char **ppMessageDecrypted, DWORD &dwMessageBufferSize) { // SERVER: Decrypts the message with the session key dwMessageBufferSize = dwEncryptedMessageBufferSize; *ppMessageDecrypted = new char[dwEncryptedMessageBufferSize]; memcpy(*ppMessageDecrypted, pMessageEncrypted, dwEncryptedMessageBufferSize); //(*ppMessageDecrypted)[dwMessageBufferSize] = '\0'; BOOL res = ::CryptDecrypt( hSessionKeyServer, NULL, 1, 0, (BYTE *)(*ppMessageDecrypted), &dwMessageBufferSize ); if (!res) { printf("SERVER: Unable to decrypt the message\n"); return FALSE; } printf("SERVER: Message decrypted!\n"); return TRUE; } |
Job done! (in C++).
The last (really) thing to do is to create the file that will be used by the C# server.
I will use a single file where I will store both the encrypted key and the encoded message. To keep things a little bit more interesting (and realistic) I will write few bytes at the start of the file to communicate the size of the key.
Because we already have all we need, we can simply create a new file and write this info as described:
1 2 3 4 5 6 7 8 9 |
FILE *encryptedMessageFile; errno_t fileOpenResult = fopen_s(&encryptedMessageFile, "TheMessage.txt", "wb"); if (fileOpenResult == 0) { fwrite(&dwExportedSessionKeyBufferSize, 1, sizeof(DWORD), encryptedMessageFile); fwrite(pExportedEncryptedSessionKey, dwExportedSessionKeyBufferSize, 1, encryptedMessageFile); fwrite(pMessageEncrypted, sizeof(char), dwMessageEncryptedBufferSize, encryptedMessageFile); fclose(encryptedMessageFile); } |
If you want to do some cleanup (and you should), remember to release the keys and the context when we don’t need them anymore.
This can be done easily in this way (this assumes that the keys are null if not initialised):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
void ReleaseKeys() { if (hKeyPairServer != NULL) { BOOL res = ::CryptDestroyKey( hKeyPairServer ); if (!res) { printf("SERVER: Server Key Pair released!\n"); hKeyPairServer = NULL; } } if (hSessionKeyServer != NULL) { BOOL res = ::CryptDestroyKey( hSessionKeyServer ); if (!res) { printf("SERVER: Server Session Key released!\n"); hSessionKeyServer = NULL; } } if (hImportedPublicKeyClient != NULL) { BOOL res = ::CryptDestroyKey( hImportedPublicKeyClient ); if (!res) { printf("CLIENT: Server Public Key released!\n"); hImportedPublicKeyClient = NULL; } } if (hSessionKeyClient != NULL) { BOOL res = ::CryptDestroyKey( hSessionKeyClient ); if (!res) { printf("CLIENT: Session Key released!\n"); hSessionKeyClient = NULL; } } } BOOL ServerReleaseContext() { BOOL res = ::CryptReleaseContext(hCryptProvServer, 0); if (!res) { printf("SERVER: Unable to Release the Context\n"); return FALSE; } printf("SERVER: Context Released\n"); return TRUE; } BOOL ClientReleaseContext() { BOOL res = ::CryptReleaseContext(hCryptProvClient, 0); if (!res) { printf("CLIENT: Unable to Release the Context\n"); return FALSE; } printf("CLIENT: Context Released\n"); return TRUE; } |
We are ready to switch to C#!
Server Code C#
The server here has to do basically the same things that we already done in C++, but in a slightly different way. In order to retrieve the Key Pair (I am not considering the creation of it in this context) we can use an RSACryptoServiceProvider object specifically configured (with a CspParameters object) to access the same server container we used in the previous app. You can find those classes in System.Security.Cryptography.
I will configure it to use the same RSA algorithm and forcing it to retrieve existing keys without attempting to create a new pair.
This is how we can do it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
private RSACryptoServiceProvider CreateKeyProvider(string _container) { RSACryptoServiceProvider rsa; const int PROVIDER_RSA_FULL = 1; const string SERVICE_PROVIDER = "Microsoft Enhanced Cryptographic Provider v1.0"; //128 bit encryption CspParameters cspParams; cspParams = new CspParameters(PROVIDER_RSA_FULL); cspParams.KeyContainerName = _container; cspParams.Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore; cspParams.ProviderName = SERVICE_PROVIDER; rsa = new RSACryptoServiceProvider(cspParams); return rsa; } |
We need something to decrypt now. I will use the file that has been created in C++ reading all its content in specific objects:
1 2 3 4 5 6 7 8 9 10 11 12 |
var file = File.Open("TheMessage.txt", FileMode.Open); byte[] keySizeBuffer = new byte[4]; file.Read(keySizeBuffer, 0, 4); var keySize = BitConverter.ToUInt32(keySizeBuffer, 0); byte[] keyBuffer = new byte[keySize]; file.Read(keyBuffer, 0, (int)keySize); long messBuffSize = file.Length - file.Position; byte[] messageBuffer = new byte[messBuffSize]; file.Read(messageBuffer, 0, (int)messBuffSize); |
Once identified the encrypted session key we need to use the RSA service provider to decrypt it.
The only awkward things here is that the .NET encryption algorithm swaps the bytes after encryption (and before decryption) and we will have to take care of this directly, because this key was encrypted by the Crypto API in C++. Another thing to consider is that the encrypted blob doesn’t simply contain the encrypted key but some extra informations about the version, the algorithm to use with that key and the algorithm used to encrypt the key itself. I will not go into any detail for this here and I will instead jump in the session blob, straight to where the encrypted key is stored and use it (we already know what kind of key we generated for this toy app).
The encryption is then as simple as doing:
1 2 |
var skip = 4 + IntPtr.Size * 2; var TheRC4Key = m_RSACryptoServiceProvider.Decrypt(sessionBlobEncrypted.Skip(skip).Reverse().ToArray(), false); |
If everything is correct we will not receive any exception and the original session key is in our hands.
Let’s just use then to decrypt the message… well… no unfortunately.
In .NET there is no implementation of the RC4 algorithm that we used to encode the message.
Although annoying, this is not a big issue because its implementation is not so complex and can be easily hand-crafted.
I will not cover this algorithm in this post (but I will do soon in another one. Update: the post is available here: RC4 Cypher Algorithm in C#) and I will instead assume that you have access to a class/method that takes care of the algorithm details, providing a simple method to decode a message with a specific provided key.
If this is true we can then simply complete the task in this way:
1 2 3 4 5 |
// Assuming the existence of the RC4 decoder // byte[] RC4Decode(byte[] key, byte[] message) var decodedMessage = RC4Decode(TheRC4Key, TheEncodedMessage); var messageString = System.Text.ASCIIEncoding.ASCII.GetString(decripted); |
Display the decoded message and jump of joy :).
Final Notes
Wait a second… nice, but… our keys? What if I want to cleanup the keys I generate with this sample code?
Correct. It would be nice to do some housekeeping when we are done with this.
In C++ we can remove out keys from our test container very easily. Sometime it is so easy that risks to be dangerous, but we know what we are doing now :).
1 2 3 4 5 6 7 8 9 10 11 12 |
BOOL ServerDestroyExchangeKeys() { // SERVER: Acquire Server Key Context BOOL res = ::CryptAcquireContext( &hCryptProvServer, KEY_CONTAINER_SERVER, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET | CRYPT_DELETEKEYSET ); if ( !res ) { printf("SERVER: Error Destroying Exchange Keys\n"); return FALSE; } printf("SERVER: Exchange Keys Destroyed\n"); return TRUE; } |
The same thing can be done as easily as this in C# (actually it is even easier than C++):
1 2 3 4 5 6 |
public void RemoveKeysFromContainer() { m_RSACryptoServiceProvider.PersistKeyInCsp = false; m_RSACryptoServiceProvider.Clear(); } |
If you try to run the C# server code again after removing the keys you will obtain a CryptographicException “Keyset does not exist” (as expected).
This will give you the knowledge that you correctly did your house-keeping :).
This website is mostly a walk-through for all of the info you wished about this and didn’t know who to ask. Glimpse here, and you’ll positively uncover it.
Wow a really useful article on BW , Whatever next
Great Article! Extremely helpful. Please keep sharing!
I know the article is a year old, but I’m hoping you still check the comments. Is there any chance you could show me how to export a public key from C# and use it in C++?
Hi Drew,
Yes I still check the comments :).
I will need to setup the environment to try it properly (it has been some time since I have played with it). I will try to have a go asap.
Hi Drew,
I manage to find the time to have a go with it.
Imagining that you have your keys generated in C#, already described in the article, just be sure to remove the “CspProviderFlags.UseExistingKey” flag from the line:
cspParams.Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;
if you are creating the keys.
You can easily export the public key (the one you want to share) communicating exponent and modulus to the client. The exponent and modulus are retrieved using something like this:
RSAParameters par = m_RSACryptoServiceProvider.ExportParameters(false); // export the public key
Exponent is here: par.Exponent
Modulus is here: par.Modulus
I noticed that you need to reverse the modulus when passing this to the C++ client.
On the client side (C++), once you received those info (and acquired the key context as already explained in the article), you can import the public key using a code similar to this (I assume that the exponent is stored in “dwExponent” and the modulus array is pointed by “pModulus”, with “modulusSize” representing the number of bytes in the “pModulus” array):
DWORD dwKeyBlobSize = modulusSize + sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY);
BYTE *pBlob = new BYTE[dwKeyBlobSize];
PUBLICKEYSTRUC *pPublicKey = (PUBLICKEYSTRUC *) pBlob;
pPublicKey->bType = PUBLICKEYBLOB;
pPublicKey->bVersion = CUR_BLOB_VERSION;
pPublicKey->reserved = 0;
pPublicKey->aiKeyAlg = CALG_RSA_KEYX; // RSA public-key key exchange
RSAPUBKEY *pRsaPubKey = (RSAPUBKEY *) (pBlob + sizeof(PUBLICKEYSTRUC));
pRsaPubKey-> magic = 0x31415352; // Set to RSA1 (0x31415352) for public keys and to RSA2 (0x32415352) for private keys
pRsaPubKey-> bitlen = modulusSize * 8; //Number of bits in the modulus
pRsaPubKey-> pubexp = dwExponent; //Exponent
BYTE *pKey = (BYTE *) ((BYTE *)pRsaPubKey + sizeof (RSAPUBKEY));
memcpy(pKey, pModulus, modulusSize);
At this point you can use the same method in the article (ClientImportServerPublicKey) to import the public key. The parameters will be “pBlob” and “dwKeyBlobSize” of course :).
Note that if you want to you can avoid to export the exponent and use directly the value: “0x10001” that is the RSA Exponent.
Hope this helps.
Refering to your remark
Currently I tried to reverse the complete byte array and still am not able to encrypt via CryptEncrypt.
Could you explain what you mean by “reversing” the modulus? Maybe I just have to swap every two bytes (Big Endian vs. Little Endian)?
Hi Uwe,
I actually mean reverse the whole byte array:
So if the original array is for example: 1, 2, 3, 4, 10, 20, …
Import is in C++ as: … 20, 10, 4, 3, 2, 1
(each element is a single byte).
What error do you have and when (what is your scenario)?
Thanks, Riccardo
Actual, my scenario is a bit “unusual”; I want to encrypt in C++ and decrypt in C# without transfering anything but the encrypted data.
I.e. I do not want to use session keys, or transfer keys between the encryption and decryption part. I’ve tried this for hours and I doubt whether this is actually possible to solve with the Crypto API.
(I’m aware that this is not the highest security, I could get; it is sufficient, though…)
Do you think this is possible to acchieve?
Hi Uwe,
If I understand correctly you are trying to use the RSA key to directly encrypt your message and you receive an error when you are trying to execute the CryptEncrypt function.
The key specified in the CryptEncrypt method should be a handle to the encryption key (obtained by using either the CryptGenKey or the CryptImportKey function).
You can still use the public key directly to encrypt the data, but there are some problems and limitations:
One is the security issue as you were saying, the other is the performance, but the third one is that you have some (annoying) limitations with the data you are trying to encrypt.
One of the error you are most likely to obtain is:
errorCode = 0x80090004 – NTE_BAD_LEN (Bad Length: The size of the output buffer is too small to hold the generated ciphertext)
or:
errorCode = 0x000000EA – ERROR_MORE_DATA (More data is available: the buffer allocated is not large enough to hold the encrypted data)
The problem is that you can encrypt a buffer no longer than the length of the key modulus, minus eleven bytes (the eleven bytes is the chosen minimum for PKCS #1 padding).
In this case (modulus 128), you can then encrypt max 117 bytes.
So, in order to do this kind of encryption you can use a code similar to this:
DWORD dwLength = 128 - 11;
res = ::CryptEncrypt( hClientImportedPublicKey, NULL, 1, 0,
NULL, &dwLength, 0 );
You can verify that “res” now should be TRUE and “dwLength” should be 128.
If this is correct you can then allocate your 128 bytes buffer and encrypt your message:
res = ::CryptEncrypt( hClientImportedPublicKey, NULL, 1, 0,
(BYTE *)DataToEncrypt, &dwDataBufferLength, dwLength );
Where “hClientImportedPublicKey” is your RSA (public) key, “DataToEncrypt” is the buffer to encrypt (allocated with the extra space to contain the final encrypted test (then 128 bytes in this case) , “dwDataBufferLength” is the length of the data to encrypt in that buffer (117 in the example), and “dwLength” is total length of the buffer (128 of course).
To help you in the debugging remember to use the function “GetLastError()” if any of these methods do not work. The error code (even if not very descriptive in some occasions) should tell you what is the main cause of the problem ;).
Hope this helps. Let me know how it goes.
Awesome, Riccardo! I’m trying this ASAP to get it working. Thanks a lot for your assistance! If I’m having a working version, I’ll write an article on my blog or on Code Project about it, since it might help others, too.
Sure. Remember to link back to this article and let me know the link so that I can add it here :).
Hi thanks for your very well written article !
I am having the exact same problem Uwe was describing. I have a public/private key pair with a 512 byte modulus. I am allocating a 512 bytes buffer and Encrypting with CryptEncrypt.
Then I am using RSACryptoServiceProvider to decrypt the encrypted data which was written in a file in binary mode meanwhile. I made sure to reverse the bytes in my c# code (with Array.Reverse)before trying to decrypt. I am still having a “bad data” error message in a CryptographicException.
Any hint on what might be missing ?
Here is the exception call stack :
à System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
à System.Security.Cryptography.RSACryptoServiceProvider.DecryptKey(SafeKeyHandle pKeyContext, Byte[] pbEncryptedKey, Int32 cbEncryptedKey, Boolean fOAEP, ObjectHandleOnStack ohRetDec
ryptedKey)
à System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP)
à GIRO.Licensor.EncryptionContext.Decrypt(Byte[] iData, Byte[]& oData) dans c:\sessions\V2015\V2015_HTOK\src\ogl\prog\GIRO.Licensor.exe\EncryptionContext.cs:ligne 56
maybe you can help me,
I have a C++ code for encrypting and decrypting with hard coded password.
When i use c++ code to ancrypt and then decrypt it frm another code (C# which calls C++ code) it works just fine.
But when i try to decrypt from a third application (also C# calling the c++ code) the result decrypted text comes as garbage.
what can be the reason? all projects are defined to work with unicode.
Hi Sarah,
My suggestion is to try to make sure that the Key Container name you are using is the same (correct) one. It sounds like something related to a small error like that one.
I hope this helps.
Terrific article. Kudos for your efforts! I’ve now spent several painful weeks wrapping my head around all this stuff and largely understand it now (no thanks to MSFT, whose docs on the subject are woeful). I do have one issue maybe you’ld care to weigh in on though. Before you can even begin to decrypt a session key, you need to know the provider that was used for the encryption in the first place (in order to call “CryptAcquireContext()”). It would be much better however to extract that info from the encrypted data itself. You could then decrypt the data from any arbitrary provider without knowing the provider ahead of time. The problem is that to do this, you would need to write the provider name (and its type) to the encrypted file itself. But you would want to encrypt this as well, so that the name of the provider isn’t stored as plain text (since you don’t want anyone to know anything about how the file is encrypted). But how to do this? You can’t encrypt the provider name using the session key itself, since the server would then be facing a chicken/egg situation, i.e., it needs the provider name before it can get access to the session key, but can’t access the provider name until it knows the session key. So maybe the best thing to do is encrypt the provider name using the public key itself, and always with MSFT’s basic or enhanced provider (which may be different than that used by the session key and its data but both client and server need to agree on a common provider used to encrypt the provider name itself). Even if this will work still pondering the situation), the resulting encrypted file will now (usually) contain at least 1 kb or more of overhead (typically 500+ bytes or more for the encrypted session key itself plus another 500+ bytes or more for the encrypted provider name). I find this all problematic, in particular when when a lot of small files (or other sources of data) need to be encrypted (too much overhead involved). Any suggestions or comments? Thanks.
Hi Larry,
I am glad that you found the article useful and interesting and thanks for the original question.
While reading your question I thought that the most natural solution is to have both client and server agree upon a common provider to use to encrypt the “main” provider name.
For the size-overhead problem I was thinking about using a sort of lookup system. You could add a single extra byte to each file. In that way you can identify up to 256 possible alternatives. You can then use an additional set of files that contains (only) the encrypted provider name (one for each file).
So for example you could name your file in this way:
provider_000
provider_001
provider_002
…
provider_255
When the client receives a file (message.txt) with, for example, the provider byte set to “4”, it will request the file “provider_004” from the server, decrypt it (with the common provider name if this is the solution you go for) and use the decrypted provider name to perform the decryption of the original file (message.txt).
This will definitely eliminate the overhead for the encrypted provider name.
You can decide to adopt the same solution for the session key (1, 2 or 4 bytes in this case that reference a set of files with an encrypted key in each one), but this is not really in line with the concept of “session key”, that should not be relative to a file (or a set of files), but should be valid for only a specific session with one client.
In this case a better solution is therefore to encrypt the files on-the-fly from the server, using a single specific session key (valid only for this session, as it should), and transmitting such key only once to the client before initiating the transmission of the data.
In this way you will have only one single initial overhead for the encrypted session key and the rest would be pure data.
Let me know if this helps and covers the scenario you are trying to solve.
HI Riccardo (is that your first name?)
(Sorry, I know the following is a little long :)
Thanks for the feedback. It’s appreciated. I’ll give your suggestion some thought. It may work or perhaps some hybrid system but I’ll have to think about it. In the general case though, it would obviously be cleaner for everything to be self-contained in a single file, but it’s likely not doable without incurring the overhead. I had already given some thought however (before you responded), about adding a single byte to the file to identify the provider name. However, I was thinking about mapping it back to the provider name using a hard-coded table in the program itself. It’s hardly ideal, and not nearly as clean as grabbing the provider name from the file (and I’d also be restricted to a predefined collection of providers – not good), but at least no extraneous files would be required. The technique could also be applied to any scenario, not just client/server. I’ve already spent the past several weeks writing some very generic (application independent) classes for encrypting/decrypting any file (or other arbitrary source of data), and it’s not restricted to a client/server scenario (since almost everything I write has a very heavy emphasis on re-use). Clients of my classes can therefore use it in any type of application. I’ll give your idea some thought though and perhaps put something generic together specifically for a client/server environment (built on-top of my other generic routines but leveraging your idea when requested by users of my class). Thanks for the suggestion (didn’t think it).
On another unrelated issue (while I got you), would you happen to know if it’s safe to open the container for a standard public/private key pair (AT_KEYEXCHANGE) that was originally created using one provider, but using another provider instead. IOW, when calling “CryptAcquireContext()” to access a public/private key pair, I want to pass the container name originally used to create that key, but use a different provider name instead. I know it’s irregular, but the reason I ask is that my generic decryption classes can also handle “.pfx” files meaning they can obtain the private key for decrypting the session key by invoking MSFT’s “PFXImportCertStore()” function. Unfortunately, this function seems to be hardwired to work with MSFT’s enhanced provider, meaning that the private key you end up with is in a container created by the enhanced provider (with no way to change it I could find). If the session key and its encrypted data uses another provider however (say, MSFT’s AES provider), you’re forced to pass the container name created by “PFXImportCertStore()” to “CryptAcquireContext()”, but pass the session key’s provider name instead. IOW, you’re accessing a container originally created by the enhanced provider but using another provider instead (such as the AES provider). This actually works with the AES provider and probably all other mainstream MSFT providers (since public/private key pairs are likely always handled the same way), but I don’t know if it’s safe for a 3rd-party provider. I’m assuming it’s not, since presumably a 3rd-party provider would expect the public/private key pair to be in its own container, not MSFT’s (which it would know nothing about). I know you might just be speculating on this yourself, but any ideas? If not, don’t worry, I’ll just have to live with it (or figure out if it’s possible to safely copy the key created by “PFXImportCertStore()” into a new container under the provider I want).
Thanks again for your assistance.
Hi Larry,
(Yep, my name is Riccardo :))
When you call CryptAcquireContext() and the container doesn’t exist, then you create it adding the flag CRYPT_NEWKEYSET, and it is now generated with that specific provider (PROV_RSA_FULL in the example).
At this point I would expect that the method returns an error if you try to acquire it with a different provider name.
This is what should happen (assuming that that’s the case):
res = ::CryptAcquireContext( &hCryptProvServer, KEY_CONTAINER_SERVER, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET );
will probably return false and the same should happen for the following call, when it tries to create a container that exists already:
res = ::CryptAcquireContext( &hCryptProvServer, KEY_CONTAINER_SERVER, SERVICE_PROVIDER, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET );
Specifically, the error that I would expect from the first call should be:
NTE_PROV_TYPE_NO_MATCH (0x8009001BL):
The provider type specified by dwProvType does not match the provider type found. Note that this error can only occur when pszProvider specifies an actual CSP name.
If they seems to work as you are saying than it probably means that they are compatible somehow, but I agree that this could not always be the case.
One thing I can think of is that, in order to transform the provider used for the container created by “PFXImportCertStore()” (that is the MSFT’s enhanced provider), you could try to acquire the context using the same (original) MSFT’s enhanced provider, and then extract the keys and recreate the same container (the same name, removing the previous one), but with the custom provider type you want to use.
Let me know how it goes with it or if you find an alternative solution :).
I am wondering if the code sample would work for the reverse scenario i.e. Server sending an encrypted message to the Client. The Server sends the public key, with the encrypted session key to the client.
Thanks
I’m a bit new to CryptoAPI and I need some help…I suspect I’m approaching things incorrectly.
What I want to do is:
– to generate a Session key on the SERVER
– Encrypt the Session Key
I’d like to then send this encrypted session key to a client who will then decrypt it using the Public Exchange key previously sent from the server. The conversation will then continue using the session key.
Does this seem sensible?
What I am trying to do is reverse scenario of what you have described. Any help ?
Thanks
Hi Riccardo,
I am facing a difficulty while verifying a digital signature in C# application.
The signature is created using Win32 Crypto APIs.
Would you please help me what could be wrong in below snippet.
RSACryptoServiceProvider rsa;
const int PROVIDER_RSA_FULL = 1;
const string SERVICE_PROVIDER = “Microsoft Enhanced Cryptographic Provider v1.0″; //128 bit encryption
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
//cspParams.KeyContainerName = ” “;
cspParams.Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = SERVICE_PROVIDER;
rsa = new RSACryptoServiceProvider(cspParams);
byte[] PublicKeyBlob;
using (var fsKeyFile = new FileStream(“..\..\..\Files\pubKey.txt”, FileMode.Open))
{
PublicKeyBlob = new byte[fsKeyFile.Length];
fsKeyFile.Read(PublicKeyBlob, 0, PublicKeyBlob.Length);
}
// RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
// //RSA.FromXmlString(ReadFile(“..\..\..\Files\pub.txt”));
rsa.ImportCspBlob(PublicKeyBlob);
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(rsa);
RSADeformatter.SetHashAlgorithm(“SHA1”);
SHA1CryptoServiceProvider SHhash = new SHA1CryptoServiceProvider();
//string signature = ReadFile(“..\..\..\Files\DigiSign.sig”);
byte[] digitalSign;
using (var fsSignFile = new FileStream(“..\..\..\Files\DigiSign.sig”, FileMode.Open))
{
digitalSign = new byte[fsSignFile.Length];
fsSignFile.Read(digitalSign, 0, digitalSign.Length);
// digitalSign = digitalSign.Reverse().ToArray();
}
var bytes = new ASCIIEncoding().GetBytes(“HelloWorld”);
var returnVal = RSADeformatter.VerifySignature(SHhash.ComputeHash(bytes),
digitalSign);
if (returnVal)
{
Console.WriteLine(“Valid signature”);
}
else
{
Console.WriteLine(“Wrong sign”);
}
}
The output is: “Wrong sign”
Whereas the signature is successfully validated in C++ application (where it is generated).
Would you please help me out in this?
Hello, I checked your code but I am a junior developer, it is so complex for me, I am trying to do the reverse of your idea where we encrypt in C#, decrypt in C.
And this is my question, my code in this post for more detail.