.NET Crypto Classes Interoperability with Win32 Crypto APIs

From: Rajesh Sampath (rajeshs_at_ctd.hcltech.com)
Date: 06/30/03


Date: Mon, 30 Jun 2003 05:20:58 -0700


Hi,

 I don't know if this is the right forum to post this
query, but any help will be greatly appreciated.

I use the RC2CryptoServiceProvider class to perform a
symmetric encryption with a hard-coded key value. I expect
a string to be encrypted by a .NET application in this
manner and the same shall be decrypted by a C++ (Win32)
application using the same algorithm, but using Win32
APIs.

The following scenarios work fine.

1. Encryption and Decryption with .NET
RC2CryptoServiceProvider class (or any other managed
crypto class, for that matter) works fine.

2. Encryption and Decryption with Win32 APIs with CALG_RC2
algorithm works fine too.

But, when i encrypt a string using .NET classes and try to
decrypt it using Win32 APIs, i get some problems. The same
holds good for Win32 Encryption & .NET decryption too.

I've pasted the source i had used for your reference.

C# code

/**********************************************/
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Resources;
using System.Collections;

namespace MySampleCryptoApp
{
        /// <summary>
        /// Summary description for Class1.
        /// </summary>
        class Class1
        {
                [STAThread]
                static void Main(string[] args)
                {
                        Encrypt();
                        Decrypt();
                }

                private static void Encrypt()
                {
                        RC2CryptoServiceProvider RCCrypto
= new RC2CryptoServiceProvider();
                        RCCrypto.Mode = CipherMode.CBC;
                        RCCrypto.KeySize = 40;
                        RCCrypto.EffectiveKeySize = 40;
                        RCCrypto.BlockSize = 64;

                        UnicodeEncoding UE = new
UnicodeEncoding();
                        byte[] Key = UE.GetBytes
("abcdefgh");
                        
                        byte[] IV = {0x00, 0x00,0x00,
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
0x00,0x00, 0x00};
                                                        
        
                        FileStream oFStream = new
FileStream
(@"E:\Users\SRajesh\Tekies\RnD\DotNet\SampleCryptoApp\Outpu
t.txt", FileMode.OpenOrCreate, FileAccess.Write);
  
                        //Create a CryptoStream, pass it
the NetworkStream, and encrypt
                        //it with the RC2Crypto class.
                        CryptoStream CryptStream = new
CryptoStream(oFStream,
                                RCCrypto.CreateEncryptor
(Key, IV),
                                CryptoStreamMode.Write);

                        
                        //Create a StreamWriter for easy
writing to the
                        //file stream.
                        StreamWriter SWriter = new
StreamWriter(CryptStream);

                        long lLength = oFStream.Length;
                        //Write to the stream.
                        SWriter.WriteLine("How is it???");

                        //Inform the user that the message
was written
                        //to the stream.
                        Console.WriteLine("The message was
sent.");

                        //Close all of the connections.
                        SWriter.Close();
                        CryptStream.Close();
                        oFStream.Close();
                        
                }

                private static void Decrypt()
                {
                        UnicodeEncoding UE = new
UnicodeEncoding();
                        byte[] Key = UE.GetBytes
("abcdefgh");
                        
                        byte[] IV = {0x00, 0x00,0x00,
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
0x00,0x00, 0x00};
                        
                        FileStream oFStream = new
FileStream
(@"E:\Users\SRajesh\Tekies\RnD\DotNet\SampleCryptoApp\Outpu
t.txt", FileMode.Open, FileAccess.Read);
                        
                        //Create a new instance of the
RC2Crypto class
                        // and decrypt the stream.
                        
                        RC2CryptoServiceProvider RCCrypto
= new RC2CryptoServiceProvider();
                        RCCrypto.Mode = CipherMode.CBC;
                        RCCrypto.KeySize = 40;
                        RCCrypto.EffectiveKeySize = 40;
                        RCCrypto.BlockSize = 64;

                        //Create a CryptoStream, pass it
the NetworkStream, and decrypt
                        //it with the Rijndael class using
key and IV.
                        CryptoStream CryptStream = new
CryptoStream(oFStream,
                                RCCrypto.CreateDecryptor
(Key, IV),
                                CryptoStreamMode.Read);

                        //Read the stream.
                        StreamReader SReader = new
StreamReader(CryptStream);

                        //Display the message.
                        Console.WriteLine("The decrypted
original message: {0}", SReader.ReadToEnd());

                        //Close the streams.
                        CryptStream.Close();
                        SReader.Close();
                        oFStream.Close();
                        
                        
                }
        }
}
/**********************************************/

Here is the C++ Win32 code

/**********************************************/

// SampleCryptoNativeApp.cpp : Defines the entry point for
the console application.
//

#include "stdafx.h"
#include "windows.h"
#include <wincrypt.h>
#include <TCHAR.h>

int main(int argc, char* argv[])
{
        
        BOOL bResult;
        HCRYPTPROV hProv;
        HCRYPTHASH hHash;
        HCRYPTKEY hKey;

        char pEncryptedString[100];
        memset(pEncryptedString, 0,100);

        FILE *fp = fopen
("E:\\Users\\SRajesh\\Tekies\\RnD\\DotNet\\SampleCryptoApp\
\Output.txt", "rb");
        fscanf(fp, "%s", &pEncryptedString);
        fclose(fp);

        
        int nLen = strlen(pEncryptedString);

        BYTE *pByte = NULL;
        pByte = new BYTE[100];
        memset(pByte, 0, 100);
        memcpy(pByte, pEncryptedString, nLen);
        
        DWORD dwError;

        bResult = CryptAcquireContext( &hProv,
      NULL, //generates default (username) keystore,
      MS_ENHANCED_PROV,
      PROV_RSA_FULL,
      CRYPT_NEWKEYSET);
        if(!bResult)
                dwError = GetLastError();

        bResult = CryptAcquireContext(&hProv, NULL,
MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
        //bResult = CryptAcquireContext(&hProv, NULL,
MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
        if(!bResult)
                dwError = GetLastError();

        bResult = CryptCreateHash(hProv, CALG_MD5, 0, 0,
&hHash);
        if(!bResult)
                dwError = GetLastError();

        BYTE pByteBaseData[] =
{97,0,98,0,99,0,100,0,101,0,102,0,103,0,104,0};
        int nKeyLen = 16;

        
        bResult = CryptHashData(hHash, pByteBaseData,
(DWORD) nKeyLen, 0);
        if(!bResult)
                dwError = GetLastError();
        
        //bResult = CryptDeriveKey(hProv, CALG_RC2, hHash,
CRYPT_CREATE_SALT, &hKey);
        bResult = CryptDeriveKey(hProv, CALG_RC2, hHash,
CRYPT_NO_SALT, &hKey);
        if(!bResult)
                dwError = GetLastError();

        BYTE pByteIV[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
        bResult = CryptSetKeyParam(hKey,KP_IV, pByteIV, 0);
        if(!bResult)
                dwError = GetLastError();

        char pToEncryptString[] = "How is it???";
        //TCHAR pToEncryptString[] = L"How is it???";
        BYTE *pToEncryptBuffer = new BYTE[100];
        memset(pToEncryptBuffer, 0, 100);
        memcpy(pToEncryptBuffer, pToEncryptString, strlen
(pToEncryptString));

        DWORD dwInputLen = strlen(pToEncryptString);
        bResult = CryptEncrypt(hKey, NULL, TRUE, 0,
pToEncryptBuffer, &dwInputLen, 16);
        if(!bResult)
                dwError = GetLastError();

        fp = fopen
("E:\\Users\\SRajesh\\Tekies\\RnD\\DotNet\\SampleCryptoNati
veApp\\Win32Output.txt", "w");
        for(int nIndex = 0; nIndex < (int) dwInputLen;
nIndex++)
        {
                fprintf(fp, "%c", pToEncryptBuffer
[nIndex]);
        }
        fclose(fp);

        DWORD dwOutputLen = nLen;

        bResult = CryptDecrypt(hKey, NULL, TRUE, 0, pByte,
&dwOutputLen);
        if(!bResult)
                dwError = GetLastError();
        
        bResult = CryptDecrypt(hKey, NULL, TRUE, 0,
pToEncryptBuffer, &dwOutputLen);
        if(!bResult)
                dwError = GetLastError();
        
        return 0;
}

/**********************************************/

When i try to decrypt the string encrypted using .NET, i
get an error (2148073487).

Any help in sorting this out would be appreciated. The
last option i have is to use PInvoke to call the Win32
APIs themselves in .NET.

Thanx & Rgds
Rajesh Sampath,
Project Lead,
HCL Technologies,
Chennai

***********************************************************
************
Disclaimer:
This document is intended for transmission to the named
recipient only. If you are not that person, you should
note that legal rights reside in this document and you are
not authorized to access, read, disclose, copy, use or
otherwise deal with it and any such actions are prohibited
and may be unlawful. The views expressed in this document
are not necessarily those of HCL Technologies Ltd. Notice
is hereby given that no representation, contract or other
binding obligation shall be created by this e-mail, which
must be interpreted accordingly. Any representations,
contractual rights or obligations shall be separately
communicated in writing and signed in the original by a
duly authorized officer of the relevant company.
***********************************************************
************
.