Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

RSA Encryption with .NET 2.0 Cryptography Services and Crypto++ Wrapped as a Managed C++ Class Library

4.72/5 (13 votes)
17 Aug 2007LGPL320 min read 1   5.7K  
Encrypt in .NET and decrypt in Crypto++, a cryptography newbie's journey.

Screenshot - encrypt1.png

Contents

Introduction

The purpose of this article is to show the interaction between Crypto++ and .NET 2.0 Cryptography services using RSA PKCS#1 encryption and to show how to wrap a Crypto++ as a managed class. Often a client and server use different cryptographic services and need to interact correctly e.g. the public portions of the encryption key need to be exchanged between them and data from the client needs to be decrypted by the server and vice versa. Most of the articles I could find were for older versions of .NET and the documentation on how to use the Cryptography services was a bit sparse, hence the need for this article.

In order to simplify things, I have stripped away the usual communication links between the client and the server. Byte arrays will be passed between them instead. No existing standards will be used to exchange keys either, the public modulus and exponent integers will be sent as byte arrays again. Putting these components together is a plumbing job – explaining this would be very specific to my problem and would confuse a complicated article even more. Below is a short description of what will be achieved by this article:

I want to use Crypto++ from C#, so I wrap it using a managed C++ class as follows:

  1. Compile Crypto++ 5.5.1 either as a static lib or as a DLL using Visual Studio 2005 with dynamically linked standard multithreaded libraries (/MD or /MDd)
  2. Create a native wrapper class which provides a simple interface to Crypto++ and encrypts or decrypts a byte array of any size
  3. Create a managed C++ class which encapsulates the native wrapper class and converts .NET managed types to native types (and vice versa) and then calls the Crypto++ wrapper class methods

A simple console tester application tests the following combinations of interop:

  1. Encrypt data using .NET Cryptography services and decrypt using .NET Cryptography services
  2. Encrypt data using Crypto++ and decrypt using Crypto++
  3. Encrypt data using .NET Cryptography services and decrypt using Crypto++
  4. Encrypt data using Crypto++ and decrypt using .NET Cryptography services

I have only explored using RSA PKCS#1 encryption in Crypto++, although I imagine you could use this article as a basis for creating a wrapper class for any aspect of Crypto++. Wrapping the whole of Crypto++ is something I don't have time for at the moment, although I'd be open to the idea of creating an open source project which does. If you are interested in this, I would suggest looking at Mono's cryptography as it will not be dependent on Microsoft's CryptoAPI.

Disclaimer

This article refers to Crypto++ Version 5.5.1 and uses the .NET Framework 2.0. It is very likely that this article will not apply to the .NET Framework 3.0 as Microsoft has indicated there will be changes to the Cryptography services at the time of writing this article. If you are using any other versions of the .NET Framework (1.0, 1.1 or 3.0) – this code might not work for you as the format of data may be different and methods/classes may have been deprecated. You will also require Visual Studio 2005. I will not post any projects for Visual Studio 2003 as the marshalling of native C++ code which uses stdlib are different in VS2005, as is the syntax for managed C++ classes. Please refer to the following articles if you are using VS2003, but please do not ask me any questions relating to this as I will not be able to answer your questions:

  1. C++ at Work by Paul DiLascia, MSDN Magazine, February 2005
  2. How To: Remove Dependency on _vcclrit.h, Visual Studio 8.0 online documentation, MSDN

Using the code

In order to build this project yourself, you will need to download and compile Crypto++ first as either a static library or as a DLL. Jeff Walton has a great article on how to do this: Compiling and Integrating Crypto++ into the Microsoft Visual C++ Environment. Read this article carefully, it does explain everything you need to know. You will need to modify the project settings so that the build is done using Unicode and uses the DLL versions of the standard library - refer to the following section CLR, Crypto++ and the C++ Standard Library later in the article. You will also need to ensure that the following DLLs are either in your search path or in the same folder as the interop DLL:

  • msvcm80.dll or msvcm80d.dll
  • msvcp80.dll or msvcp80d.dll
  • msvcr80.dll or msvcr80d.dll

If you want to use the managed class from C#, then add a reference to the DLL and create an object as follows:

C++
// Create the object
CryptoPPDotNet.RSAES_PKCS15 cryptoRSA = new CryptoPPDotNet.RSAES_PKCS15();
...

// Encrypt some data
byte[] pbEncrypted = cryptoRSA.EncryptPublicKey(pbSecret);

If you get exceptions about bad images or libraries not loading, I suggest using the Dependency Walker to ensure that Windows can find of the dependency DLLs for CryptoPPInterop.dll. If you are using the DLL version of Crypto++, ensure it is in the search path or the same directory as the interop DLL - again the Dependency Walker will show you what is missing.

Glossary

PKCSPublic Key Cryptography Standards. These are standards set by RSA Security
PKCS#1A public key encryption system defined in RFC3447
ASN.1Abstract Syntax Notation One
BERBasic Encoding Rules. Defined in ASN.1.
DERDistinguished Encoding Rules. Defined in ASN.1.

Background

At work we have a project to integrate with some 3rd party software which uses an encrypted TCP/IP connection to establish communication with clients. Sadly, they never used something like SSL and implemented a custom solution which does not use standard mechanisms such as key exchange envelopes. Then to add to the confusion, they have poor documentation. So it is pretty much your standard scenario where someone provides you this 'nice' interface to their software, however fails to tell you all the details and provides no example code. After a fair amount of consultation we finally got some example code out of them and discovered they used Crypto++ to handle all the cryptography. Originally one of the other software developers had attempted to write our client using Microsoft's Cryptographic Services (CryptoAPI) which is part of Windows. However, being Microsoft there is only one way to use their software and it didn't appear to be possible to get it to talk to the server without much effort. Eventually our client was written with Crypto++ and everything worked well.

More of our customers have been requesting this connection to the 3rd party server and we have run into some trouble testing things as the 3rd party will not give us an evaluation copy of the server. My solution to this was to write a simulator of the 3rd party server, with which we could test and demonstrate our software interacting with the server. Being a big fan of C# and .NET my plan was to use the Cryptography services in .NET 2.0. Making .NET Cryptography work with Crypto++ turned out to be easier said than done. I am a newbie to cryptography so I lack the background knowledge to know exactly what to do, which as I have discovered is probably the most important thing for smooth interop. There were many articles on the web discussing .NET's Cryptography services. Many of them explained how the Cryptography services wrapped the underlying CryptoAPI. This meant the byte ordering of encrypted data was little-endian instead of big-endian as expected by Crypto++. Eventually I went back to basics and wrote a whole bunch of small testers to establish that the procedures for encrypting and decrypting data in both .NET and Crypto++ were as I expected. I then wrote some interop testers to ensure that encrypted data from .NET could be decrypted in Crypto++. As I have no background in the subject my solutions are possibly not entirely orthodox, however, they do shed some light and the workings of both .NET 2.0 Cryptography and Crypto++. I am hoping that this article will save someone else a lot of time which I wasted trying to understand both libraries.

I don't think that our 3rd party's method of key exchange and setting up an encrypted channel is particularly good or secure – I think that defined key exchange methods which use certificates should rather be used. But this was the procedure we had to conform to; I understand it is not ideal.

The Problem

We are looking to establish an encrypted communication channel between a client and a server. The client will connect to the server, they will exchange keys and then encrypted communication will commence. We cannot be guaranteed that the server and client use the same cryptography service, so there could be all sorts of issues with regards to byte ordering (endianess) and byte packing. But for the moment we will only consider two interop scenarios for this article:

  1. Server uses Crypto++ and client uses .NET Cryptography
  2. Server uses .NET Cryptography and client uses Crypto++

The communication channel between the server and the client is fairly arbitrary and could be anything from network sockets such as TCP/IP to simply exchanging files. All of the example code does not uses a communication channel between the client and server, but simply passes a byte array between them. Adding the communication between the client and server is something covered by many other articles on this site!

The connection process between the client and server will occur as follows:

  1. The server generates a private key and waits for connections
  2. The client connects to the server
  3. The server sends the client its public key
  4. The client receives the server's public key and generates its own private key
  5. The client sends its public key to the server (encrypted with the server's public key)
  6. The server receives the client's public key and returns an acknowledge message encrypted with the client's public key
  7. Encrypted communication begins

The connection process is shown in the message sequence chart below:

Key Exchange

Figure 1: Key exchange message sequence chart.

Key Exchange

Exchanging keys between the server and the client occurs in two steps - first the server sends the client a plain text message with its encoded public key:

Server exchanges public key with client

Figure 2: Server exchanges public key with client.

In the example code the modulus and exponent are both encoded by getting pointers to the byte arrays for each and Base64 encoding them. Crypto++ sometimes uses BER or DER to encode/decode integers – I will cover this briefly at the end of the article.

The client receives the server's public key after which it generates its own private key. The client's public key is then encrypted with the server's public key and transmitted back to the client:

Client exchanges public key with server

Figure 3: Client exchanges public key with server.

If the key exchange has occurred correctly, the client will have the server's public key and the server will have the client's public key. An acknowledge message is sent by the server to the client. If the client can successfully decrypt this, then the key exchange has been successful. Otherwise, the connection has either been compromised or the format of the keys was incorrect (a common problem in interop, which is why key exchange standards exist!).

Data Transfer

Once the key exchange has taken place correctly an encrypted communication channel will have been established. When the server sends data to the client it will encrypt the data using the client's public key, which the client will decrypt, as shown below:

Server encrypts data with client's public key

Figure 4: Server encrypts data with client's public key.

When the client returns data to the server, it will encrypt the data with the server's public key, which the server can decrypt with its private key, as shown below:

Client encrypts data with server's public key

Figure 5: Client encrypts data with server's public key.

The data is transferred between the client and server as chunks of encrypted bytes. If your communication channel transfers data using a text protocol then you would need to Base64 encode the data to ensure it does not interfere with the data packet which contains it. However, this article assumes binary data can be transferred between the server and the client.

Block Encryption and Decryption

There is a limit as to the number of bytes which can be encrypted or decrypted in one go. Therefore it is necessary to break an array of bytes up into chunks of the maximum length, encode them and concatenate the results.

For RSA encryption the maximum number of bytes which can be encrypted in one operation is the length of the modulus in bytes less 11 bytes for padding (using v1.5 padding). For a 1024 bit key this is (1024 / 8) – 11 = 117 bytes.

When decrypting the maximum length of the cipher text (the encrypted byte array) is the length of the modulus in bytes. So far a 1024 bit key this is 128 bytes.

If you attempt to encrypt or decrypt a buffer which is longer than this number, an exception will be thrown in both .NET and Crypto++. Unfortunately .NET throws the very descriptive error of "Bad Data".

Interop Classes

Ideally I wanted to produce a .NET class which wrapped Crypto++ so that it matched the .NET 2.0 Cryptographic services. The idea of trying to derive the class from the .NET Cryptography base classes seemed like more trouble than it was worth, so instead I decided to match the function definition for the Encrypt and Decrypt methods in RSACryptoServiceProvider. A generic solution can be provided by defining a base class from which classes can be derived which encapsulate RSACryptoServiceProvider and the Crypto++ wrapper class.

First we need to expose the Crypto++ functionality we want by creating a native C++ class. This will provide methods to exchange public keys, encrypt binary data and decrypt binary data.

In order to make this class visible from .NET we need to create a managed class in C++ which contains the native C++ class (see the MSDN article [1] for more details).

I have created a tester application in C# which tests the managed C++ classes in all of the interop combinations.

CLR, Crypto++ and the C++ Standard Library

When statically linking Crypto++ to a managed class DLL you must ensure that the static library uses the same standard libraries as the DLL. This means it must be compiled to use a Multithread DLL (/MD or /MDd) and uses Unicode characters. See the Known Issues section at the end to troubleshoot some compilation issues. Ensure the project settings for Crypto++ are as follows for both the debug and release versions (the debug settings are shown):

Image 7

Image 8

Figure 6: Crypto++ static build settings (debug)

Image 9

Figure 7: Crypto++ static build settings (release)

Image 10

Image 11

Figure 8: Crypto++ DLL build settings (debug)

If you want to use the DLL version of Crypto++, make sure that you export the GetNewAndDeleteForCryptoPP function from your DLL. This will pass pointers to the new and delete operators to Crypto++. If these get mixed up, you will be attempting to allocate memory with one new operator and delete it in another. This creates a host of nasty errors and exceptions.

Setting pointers to the new and delete operators

When Crypto++ is used as a DLL you need to ensure that it is using the same new and delete operators as the calling code. This is done by passing pointers to your new and delete operators to Crypto++ (by exporting GetNewAndDeleteForCryptoPP from your DLL) or by letting Crypto++ set them (the default behaviour). Below are two code snippets for each exported method, which I have included for completeness.

C++
extern "C" __declspec(dllexport) void __cdecl GetNewAndDeleteForCryptoPP
                    (PNew & fnNew, PDelete & fnDelete)
{
#if 1
    // C++ style
    fnNew = &operator new;
    fnDelete = &operator delete;
#else
    // C style
    fnNew = malloc;
    fnDelete = free;
#endif
}

C++
static PNew s_pNew = NULL;
static PDelete s_pDelete = NULL;

extern "C" __declspec(dllexport) void __cdecl SetNewAndDeleteFromCryptoPP(
                    PNew pNew, PDelete pDelete, PSetNewHandler pSetNewHandler)
{
    s_pNew = pNew;
    s_pDelete = pDelete;
}

void * __cdecl operator new (size_t size)
{
    return s_pNew(size);
}

void __cdecl operator delete (void * p)
{
    s_pDelete(p);
}

Implementation Details

The following classes are defined in the managed class DLL:

class RSAES_PKCS15 (Managed C++)

namespace CryptoPPDotNet.RSAES_PKC15

Public Methods

SetPublicKeySets the internal public key by passing in the modulus and exponent as byte arrays.
GetPublicKeyReturns the public components of the internal private key as byte arrays.
EncryptPublicKeyEncrypts an array of bytes using a 1024 bit RSA PKCS#1 key with v1.5 padding. The input array can be any size.
EncryptPrivateKeyEncrypts an array of bytes using a 1024 bit RSA PKCS#1 key with v1.5 padding. The input array can be any size.
DecryptPrivateKeyDecrypts an array of bytes using a 1024 bit RSA PKCS#1 key with v1.5 padding. The input array can be any size.

class CryptoPP_RSAES_PKCS15 (Native C++)

Public Methods

CreatePrivateKeyGenerates a new 1024 bit RSA private key.
SetPublicKeySets the internal public key by passing in the modulus and exponent as byte arrays.
GetPublicKeyReturns the public components of the internal private key as byte arrays.
EncryptPublicKeyEncrypts a byte array using the public key set by calling SetPublicKey.
EncryptPrivateKeyEncrypts a byte array using the private key generated by calling CreatePrivateKey.
DecryptPrivateKeyDecrypts a byte array using the private key generated by calling CreatePrivateKey.

Private Methods

InternalEncryptEncrypts a byte array and returns the result in a new byte array which must be deallocated by the caller.
InternalDecryptDecrypts a byte array and returns the result in a new byte array which must be deallocated by the caller.

Private Member Variables

m_pPublicKeyThe public key as a Crypto++ encryptor object.
m_pPrivateKeyThe private key generated by CreatePrivatekey as a Crypto++ decryptor object.

Tester Applications

C# Console Application Tester

In order to test the interop between .NET Cryptography services and Crypto++ we need to encrypt data in one and decrypt it in the other. This tester will test each of the combinations:

  1. Encrypt in .NET with private key and decrypt in .NET with private key
  2. Encrypt in Crypto++ with private key and decrypt in .NET with private key
  3. Encrypt in .NET with public key and decrypt in Crypto++ with private key
  4. Encrypt in Crypto++ with public key and decrypt in .NET with private key

There is also a method to create the fixed 1024 bit RSA key and export it to XML. This is useful to create a string version of the values needed, so that the test is repeatable with the same key (rather than generating a new one each time).

Things to do

  1. The purpose of this article was to demonstrate interop between .NET Cryptography and Crypto++ for the special case of RSA PKCS#1 with v1.5 padding. I have not considered any other encryption methods or encoding methods. I leave this up to the reader.
  2. The length of the keys is fixed at 1024 bits and v1.5 padding is always used. OAEP padding is available in .NET 2.0 and in Crypto++ but I have not implemented this.
  3. The key exchange is done by passing raw byte array representations of the integers between methods. A safer way of doing this is to use BER (Basic Encoding Rules) or DER (Distinguished Encoding Rules). This will ensure that the byte order is preserved. I make the assumption that the same type of platform will be used by the server and the client.

Other useful things

BER and DER encoding/decoding of Integers

A safe way of passing integers between entities is to encode them using ASN.1 – this ensures that the byte order is preserved or the sign of an integer for example. The article by Burton Kaliski [2] explains BER and DER encoded very well and I would suggest that you read it first if you need to do anything with ASN.1 encoding. Crypto++ will encode and decode integers using BER/DER. In .NET 2.0 it is possible to BER encode/decode byte buffers and normal primitive length integers, however encoding large integers is more tricky. Byte arrays are encoded as OCTET_STRINGs, so large integers in .NET need to be encoded in this way. Below is a code snippet to encode the public key components of an RSA key:

C++
// Create a 1024 bit server key with RSA PKCS#1 v1.5
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024);

// Get parameters
RSAParameters rsaParams = RSA.ExportParameters(true);

// BER Encode
byte[] pbExponentBER = System.DirectoryServices.Protocols.BerConverter.Encode(
                                               "o", rsaParams.Exponent);
byte[] pbModulusBER = System.DirectoryServices.Protocols.BerConverter.Encode(
                                               "o", rsaParams.Modulus);

Once encoded these byte arrays should be encoded as Base64 strings and transmitted to the entity needing the public key. If the recipient uses Crypto++, the integers need to be decoded as explained in the code snippet below. This makes allowance for the integer to be coded as an INTEGER or an OCTET_STRING.

C++
Integer Modulus;
Integer Exponent;

std::string strModulusBase64 = ...;
std::string strExponentBase64 = ...;

try
{

byte pbOut[127];
size_t nPeek;

//-------------------------------------------------------------------------
// BER is Basic Encoding Rules, which is a method of encoding data
// for transfer. The specification can be found at:
// http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
// and a good guide at: http://www.columbia.edu/~ariel/ssleay/layman.html.

// ------------------------------------------
//  Exponent
// ------------------------------------------

StringSource ssExp(strExponentBase64.c_str(), strExponentBase64.length(),
                   true, new Base64Decoder);

// find out whether this is a BER encoded INTEGER or OCTET STRING
nPeek = ssExp.Peek(pbOut, 127);

// check BER encoding type
if(pbOut[0] == 0x04)
{
//
// OCTET STRING
//

long nBERDataLength = 0;

// Get the length
if(pbOut[1] & 0x80)
{
  //
  // Long form length
  //

  // Get the number of octets describing the length (up to 127)
  byte nLengthOctets = pbOut[1] & 0x0F;

  // Ignore big values (these are unlikely)
  switch(nLengthOctets)
  {
  case 1:
    nBERDataLength = pbOut[2];
    break;
  case 2:
  case 3:
  case 4:
    {
      byte * pbTemp = (byte*)&nBERDataLength;
      for(int i = 0; i < nLengthOctets; i++)
        pbTemp[i] = pbOut[nLengthOctets-1-i+2];
    }
    break;
  }
}
else
{
  //
  // Short form length
  //

  nBERDataLength = pbOut[1];
}

// Decode
Exponent.BERDecodeAsOctetString(ssExp.Ref(), nBERDataLength);
}
else
{
//
// INTEGER
//
Exponent.BERDecode(ssExp.Ref());
}

// ------------------------------------------
//  Modulus
// ------------------------------------------

StringSource ssMod(strModulusBase64.c_str(), strModulusBase64.length(),
                   true, new Base64Decoder);

// find out whether this is a BER encoded INTEGER or OCTET STRING
nPeek = ssMod.Peek(pbOut, 127);

// check BER encoding type
if(pbOut[0] == 0x04)
{
//
// OCTET STRING
//

long nBERDataLength = 0;

// Get the length
if(pbOut[1] & 0x80)
{
  // Get the number of octets describing the length (up to 127)
  byte nLengthOctets = pbOut[1] & 0x0F;

  // Ignore big values (these are unlikely)
  switch(nLengthOctets)
  {
  case 1:
    nBERDataLength = pbOut[2];
    break;
  case 2:
  case 3:
  case 4:
    {
      byte * pbTemp = (byte*)&nBERDataLength;
      for(int i = 0; i < nLengthOctets; i++)
        pbTemp[i] = pbOut[nLengthOctets-1-i+2];
    }
    break;
  }
}

// Decode
Modulus.BERDecodeAsOctetString(ssMod.Ref(), nBERDataLength);
}
else
{
//
// INTEGER
//
Modulus.BERDecode(ssMod.Ref());
}

}
catch(CryptoPP::Exception e)
{
  // Error handling
}

// Recreate server's public key
mpPublicKey = new RSAES_PKCS1v15_Encryptor(Modulus, Exponent);

// Check if the key is valid
AutoSeededRandomPool rng;
bool bValid = mpPublicKey->GetKey().Validate(rng, 3);

// Return the status
return bValid;

Remember that BER encoding of integers takes their sign (positive or negative) into account. If an INTEGER is negative, it will be converted to it's two's complement form. This is not done with the OCTET_STRING. As .NET 2.0 can only handle integers of up to 64 bits, it does not encode or decode long integers. That leaves only one option, which is to use OCTET_STRINGs.

Known Issues

LoaderLock Exception

It is likely that you may have a loader lock exception thrown when the tester application exists:

LoaderLock was detected

Message: DLL 'CryptoPPDoNet.dll' is attempting managed execution inside
OS Loader lock. Do not attempt to run managed code inside a DllMain or
image initialization function since doing so can cause the application
to hang.

I suspect that this is happening because Crypto++ is destroying all of the classes allocated by its class factory just before the managed class library unloads. However, this is occurring inside a block of native code and not managed code.

I have left in the following directives at the top of the native classes but commented them out:

C++
#pragma managed(push, off)
#pragma managed(pop)

These tell the compiler what code is managed and what is unmanaged. If these are left in, errors occur when Crypto++ shuts down. As the managed DLL is running unmanaged code when the exception occurs, I have found it hard to attach to the tester process to see where the error occurs. A solution to this seems to be to treat everything as managed code, although I doubt this is strictly correct. I'm sure there is a solution to this somehow, although it probably means added things to the Crypto++ header files, which I try to avoid to maintain compatibility with the vanilla release. More on loader locks can be found in an MSDN forum post [3].

Compiling Crypto++ as Unicode

When compiling Crypto++ as Unicode, you need to force the debug output functions to use the ANSI versions rather than letting the compiler choose automatically. This is done by ensuring the following lines are changed to:

C:\src\cryptopp551\dll.cpp(119):      OutputDebugStringA("Crypto++ was not
                                      able to obtain new and delete function
                                      pointers.\n");
C:\src\cryptopp551\fipstest.cpp(310): OutputDebugStringA("Crypto++ DLL
                                      integrity check failed. Cannot open file
                                      for reading.");
C:\src\cryptopp551\fipstest.cpp(404): OutputDebugStringA("In memory integrity
                                      check failed. This may be caused by debug
                                      breakpoints or DLL relocation.\n");
C:\src\cryptopp551\fipstest.cpp(423): OutputDebugStringA((("Crypto++ DLL
                                      integrity check failed. Actual MAC is
                                      : " + hexMac) + "\n").c_str());

Base64 Encoding/Decoding with Crypto++

I had lots of trouble including base64.h from Crypto++ with either the DLL or static builds. It is interesting that it is not included in the default DLL header file, which to me means that something in it is not entirely standard. If you have to use Base64 encoding/decoding, I would suggest using the .NET Framework to do this. I have not found another workaround to get it working in Crypto++ when built with /clr enabled in C++.

In Closing

This article has shown the simple interaction between Crypto++ and .NET 2.0 cryptography services in the context of an encrypted communication channel between a client and a server.

The core concepts that have been discussed are as follows:

  1. Wrapping Crypto++ using a managed C++ class
  2. Interop between Crypto++ RSA encryption and .NET 2.0 Cryptographic services
  3. Compiling Crypto++ and statically linking it to a managed class

Hopefully I have managed to demystify a number of things which I found very confusing when starting work with Crypto++ and .NET cryptography. I find the documentation for both fairly limited, but luckily there is a good community of users out there who are very willing to share their knowledge and help out with some pretty mundane questions. If you are having trouble with Crypto++ I would suggest posting your question to the Crypto++ user's group.

Acknowledgements

Thanks to Jeff Walton for some great articles on the CodeProject to get me going with this and for answering some fairly dumb questions to start with.

My thanks also goes to Parch from the Crypto++ users group who helped me out with some linking issues I had in the beginning.

References

  1. How to: Wrap Native Class for Use by C#, Visual Studio 8.0 online help, MSDN
  2. A Layman's Guide to a Subset of ASN.1, BER, and DER, Burton S. Kaliski Jr., Nov 1993
  3. http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=227822&SiteID=1

History

  • 01 Aug 2007: Version 1.0 - First release

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)