eselk
eselk

Reputation: 6884

Convert Crypto API calls to .NET

I have this code in C++ (AnsiString is a wrapper for char* buffer):

BOOL HashIt(AnsiString &pw,BYTE *buf,BYTE &len)
{
   HCRYPTPROV cp;
   CryptAcquireContext(&cp,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT);

   HCRYPTHASH ch;
   CryptCreateHash(cp,CALG_MD5,0,0,&ch);

   CryptHashData(ch,(BYTE*)pw.c_str(),pw.Length(),0);

   HCRYPTKEY kc;
   CryptDeriveKey(cp,CALG_3DES,ch,CRYPT_EXPORTABLE,&kc);
   CryptExportKey(kc,NULL,PLAINTEXTKEYBLOB,0,buf,&dwLen);

   len = (BYTE) dwLen;
}

So far I've got this far converting it to .NET:

    public static byte[] HashItB(string text)
    {
        byte[] textBytes = System.Text.Encoding.UTF8.GetBytes(text);
        System.Security.Cryptography.MD5CryptoServiceProvider sp = new System.Security.Cryptography.MD5CryptoServiceProvider();

        byte[] hashBytes = sp.ComputeHash(textBytes);
        System.Security.Cryptography.PasswordDeriveBytes pdb = new System.Security.Cryptography.PasswordDeriveBytes(hashBytes, new byte[0]);
        hashBytes = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, hashBytes);

        // Need to export key as plain text blob here, CryptExportKey

        return (hashBytes);
    }

Assuming the above code is correct, my last step is to translate the CryptExportKey function to .NET. Can someone point me to the function or a sample? I can't change the C++ method and I need my .NET code to match a legacy app. Or should I give up on .NET methods and either create a C++ DLL and call that (not crazy about that due to extra size of shipping another DLL with my tiny app), or pinvoke all of the Crypto functions?

Upvotes: 1

Views: 2096

Answers (3)

eselk
eselk

Reputation: 6884

Thanks for the help, and incase anyone needs this reading this post later, here is what I ended up with. I'm sure the byte array building could be better, but in my case this code doesn't need to be fast so I just went with what was easy for me to read:

    public static byte[] HashIt(string text)
    {
        byte[] textBytes = System.Text.Encoding.UTF8.GetBytes(text);
        System.Security.Cryptography.PasswordDeriveBytes pdb = new System.Security.Cryptography.PasswordDeriveBytes(textBytes, new byte[0]);
        byte[] hashBytes = pdb.CryptDeriveKey("TripleDES", "MD5", 0, new byte[8]);
        byte[] head = new byte[] { 0x08,0x02, 0x00, 0x00, 0x03, 0x66, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00 };
        byte[] hashBytesWithHead = new byte[hashBytes.Length + head.Length];
        head.CopyTo(hashBytesWithHead,0);
        hashBytes.CopyTo(hashBytesWithHead,head.Length);
        return (hashBytesWithHead);
    }

Upvotes: 1

Rasmus Faber
Rasmus Faber

Reputation: 49619

The .NET method PasswordDeriveBytes.CryptDeriveKey() is the equivalent to using the Win32 CryptoAPI method to hash the password, call CryptDeriveKey and finally export the key by calling CryptExportKey.

Your C++-code is equivalent to:

byte[] textBytes = System.Text.Encoding.Default.GetBytes(text); // Default is the ANSI-code-page
System.Security.Cryptography.PasswordDeriveBytes pdb = new System.Security.Cryptography.PasswordDeriveBytes(textBytes, new byte[0]);
byte[] hashBytes = pdb.CryptDeriveKey("TripleDES", "MD5", 0, new byte[8]);

except that the .NET-code removes some header data from the exported key (a PLAINTEXTKEYBLOB-header). For a 192 bit 3DES key this is "08 02 00 00 03 66 00 00 18 00 00 00". You can just prepend it in the .NET-code if you need it.

Upvotes: 3

logicnp
logicnp

Reputation: 5836

The PasswordDeriveBytes.CryptDeriveKey Method already returns the derived key, so there is no need to export a key as in your C++ code.

Upvotes: 1

Related Questions