views:

221

answers:

2

The simple blob that is generated using

CryptExportKey(hKey, hPublicKey, SIMPLEBLOB, 0, lpData, &nSize);

does not match the one generated from the following code (note that client.key is the plain text key value of hKey found using http://www.codeproject.com/KB/security/plaintextsessionkey.aspx )

CspParameters cspParams = new CspParameters();            
cspParams.KeyContainerName = "Container Name";            
cspParams.KeyNumber = (int)KeyNumber.Exchange;
cspParams.ProviderType = 1;            
cspParams.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";            cspParams.Flags = CspProviderFlags.UseMachineKeyStore;            

RSACryptoServiceProvider rsaClient = new RSACryptoServiceProvid(cspParams);               

    rsaClient.ImportCspBlob(File.ReadAllBytes(@"C:\client.key"));//Generate a SIMPLEBLOB session key

byte[] session = GetRC4SessionBlobFromKey(keyMaterial, rsaClient);//Encrypt a key using public key and write it in a SIMPLEBLOB format


   public byte[] GetRC4SessionBlobFromKey(byte[] keyData, RSACryptoServiceProvider publicKey)        
{              
using(MemoryStream ms = new MemoryStream())              
using(BinaryWriter w = new BinaryWriter(ms))            
{                   
w.Write((byte) 0x01); // SIMPLEBLOB                    
w.Write((byte) 0x02); // Version 2                    
w.Write((byte) 0x00); // Reserved                    
w.Write(0x00006801);  // ALG_ID = RC4 for the encrypted key.                
w.Write(0x0000a400);  // CALG_RSA_KEYX                    
w.Write(publicKey.Encrypt(keyData, false));                
w.Flush();                

return ms.ToArray();              
}        
}

Why is that?

A: 
  1. The code in my original answer was not correct. You have to reverse the encrypted bytes. I have updated the answer to fix that problem.

  2. The code above is missing a "w.Write((byte) 0x00); // Reserved". It was not a mistake that my original answer had that line repeated.

  3. RSA PKCS#1 encryption (which is used inside the SIMPLEBLOB) is not deterministic. I.E. You will not get the same result by encrypting the same data twice.

So to sum up: Change the code above to:

public byte[] GetRC4SessionBlobFromKey(byte[] keyData, RSACryptoServiceProvider publicKey)        
{              
  using(MemoryStream ms = new MemoryStream())              
  using(BinaryWriter w = new BinaryWriter(ms))            
  {                   
    w.Write((byte) 0x01); // SIMPLEBLOB                    
    w.Write((byte) 0x02); // Version 2                    
    w.Write((byte) 0x00); // Reserved                    
    w.Write((byte) 0x00); // Reserved                    
    w.Write(0x00006801);  // ALG_ID = RC4 for the encrypted key.                
    w.Write(0x0000a400);  // CALG_RSA_KEYX                    
    byte[] encryptedKey = publicKey.Encrypt(key.Key);
    byte[] reversedEncryptedKey = new byte[encryptedKey.Length];
    for(int i=0;i<encryptedKey.Length;i++){
      reversedEncryptedKey[i] = encryptedKey[encryptedKey.Length - 1 - i];
    }
    w.Write(reversedEncryptedKey); // encrypted key in LSB byte order

    w.Flush();                

    return ms.ToArray();              
  }        
}

and then check that it works by using CryptImportKey() to import the SIMPLEBLOB instead of trying to compare the result with CryptExportKey().

Rasmus Faber
Do you mean attach or copy the actual SIMPLEBLOB contents? I can do that.
Yes, please include the contents of the CryptExportKey()-blob and the GetRC4SessionBlobFromKey()-blob. (Hex-encoded or base64-encoded or whatever).
Rasmus Faber
Hi Rasmus,Sorry for the delayed response and Thanks for your reply, I was away for a bit and didn't look at your comment until today.The comment text box doesn't allow to put more than 300 chars or upload files. Please let me know if there is any other way I can reach you (e.g email). Thanks
The best option is to edit your question and add the information there. Alternatively add the information in an answer to your own question or by splitting it up in multiple comments. If neither of those options work, let me know.
Rasmus Faber
Hi Rasmus. Thank you very much for the updated code. That did the trick and it worked perfectly. Out of curiousity, why LSB byte order? Thanks a lot again!!!!!!
You are welcome. The Win32 CryptoAPI was designed at a time when there was not much agreement on how to represent large numbers. Microsoft chose LSB which made sense since that matches the Intel architecture. Subsequently MSB byte order emerged as the standard representation - probably because...
Rasmus Faber
... that is the representation used by ASN.1. So when the .NET framework was designed, they chose MSB representation for large numbers. Thus you have to reverse the output of the .NET encrypt()-method to match what is expected inside a CryptoAPI SIMPLEBLOB.
Rasmus Faber
A: 

Hi Ramsmus,

I am unable to edit my own quesiton and therefore I am putting the content that you wanted me to attach here in this answer, I have screen capture of the images from the binary editor /immediate window in visual studio below

CryptExportKey - SIMPLEBLOB

KeyMaterial value in Debug Window

Key Material value that is saved in file using the code project article

Session key value from GetRC4SessionBlobFromKey() (integer value of individual bytes separated by a comma)

Thank you very much for looking into this and please let me know if there is any further info I can provide.

You should really register on the site. You get a lot of problems by using a semi-anonymous profile. That being said, I have edited my answer. Hope that helps.
Rasmus Faber