A .NET Cryptography Primer, Part Four

(You can find the previous parts here:  part 1 (background), part 2 (hashes), and part 3 (Symmetric encryption))

Background

This last part in this primer series covers asymmetric encryption, also known as public/private key encryption.  A “public” key, freely distributed, is paired with a “private” key, which is kept secret.  The public key is used to encrypt messages that, without massive computational power, only the paired private key can decrypt.  This is only secure one way–from public key to private key–so two-way encrypted communications usually involve both sender and receiver exchanging public keys in order to encrypt transmissions both ways.

Shared secret (symmetric) encryption is vulnerable to eavesdroppers, decompilers and those with sufficient computational power, each of whom have a relatively simple path to discovering what that secret is.  If that shared secret is sniffed as it is transmitted over the network, or recovered from source code, or cracked using a rainbow table, your data is now open to everyone.  Asymmetric cryptography allows for the easy storage and transmission of the public key without fear of it falling into the wrong hands, as determining the private key is practically impossible.  I’m no expert, but I believe if you started to brute-force crack a 4096 bit key today, you’d finish roughly five seconds after the universe collapses back into a singularity.

Another common use for asymmetric algorithms is to generate signatures.  This is useful for ensuring that data transmitted in cleartext isn’t tampered with.  For a given message, you can use your private key to generate a signature.  This is transmitted in cleartext along with the message.  On the receiving end, you can use the public key to verify that the signature was generated by the private key and that it matches the set of bytes.  This ensures that the message is from a trusted source (the owner of the private key) and that the message hasn’t been tampered with in a “man in the middle” style attack.

Implementations

There are two different types of asymmetric algorithms available to .NET coders.  They are RSA and DSA (Digital Signature Algorithm).  Without going into details, the algorithms are essentially the same.  The difference is that DSA can only be used to generate signatures and is legal to export, whereas RSA can be used to perform encryption as well and cannot be legally exported to some countries.  Of course, that is a huge over-generalization; if you’re interested in learning more you can always Google it.  For my examples, I’ll be using RSA as it can do both and is more commonly used.

Like other cryptographic algorithms in the .NET framework, the RSA implementation is implemented as a provider, the RSACryptoServiceProvider, which extends an abstract base class for all algorithms of this type:  System.Security.Cryptography.RSA.  By using the static Create() method on this base class, you can get the default or a specifically named implementation of the provider.  In my examples I’ll be new-ing up instances, as the constructor for the RSACryptoServiceProvider takes a CspParameters object that can be used to configure the provider.  For more information on the different types of RSA CSPs available for Microsoft developers and how to specify which to use, go here.

A word or two thousand about Key Containers

The security of asymmetric algorithms depends on the private key remaining private.  Imagine a software product that uses RSA signatures for a license key.  You compile the public key into the software and use it to verify licenses.  The company generates these a license by taking the “Licensed to” information from a user and generating a signature.  If the private key is stolen or lost, every boxed copy of the software is now trash.  The source code has to be recompiled with a new public key.  And the next software update will require users to re-register their copies of the software.  This is commonly known as “a nightmare.”

Unlike shared secret keys (aka passwords), public/private key pairs are complex structures that cannot be memorized.  Because of this, Microsoft stores these keys in what is called a “Key Container“.  This is implemented as a file on the hard drive and is secured by ACLs.  Key Containers can be scoped to the machine (look in Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\ MachineKeys) or to the user (Documents and Settings\[USER ACCOUNT]\Application Data\Microsoft\Crypto\RSA).  Machine level keys are accessible by the system and those with configured access rights (by default the principal that created them).  User-scoped containers are, oddly enough, accessible by the system, the user that created them, AND administrators.  Because of this, they can optionally be encrypted with a password.  Another important aspect about user-scoped key containers is that they are stored within the user’s profile, which means they are lost when the user account is deleted.  You can have more than one key container of either type, each being identified by its name.

User-scoped Key Containers

User scoped key containers, by default, are only available to the system, admins, and the user that created them.  You can also configure the provider to allow users to apply a password to the container to make them more secure.  I’ll be using the machine store for simplicity later, but to scope the container to the user is simple:

<span style="color: green;">// create a configuration object for the provider</span>

<span style="color: #2b91af;">CspParameters</span><span style="color: navy;">parms</span><span style="color: fuchsia;">=</span><span style="font-weight: bold; color: #2f2fff;">new</span><span style="color: #2b91af;">CspParameters</span><span>();</span>

<span style="color: green;">// set the flag to use a user protected key</span>

<span style="color: navy;">parms</span><span style="color: fuchsia;">.</span><span style="color: navy;">Flags</span><span style="color: fuchsia;">=</span><span style="color: #a927b4;">CspProviderFlags</span><span style="color: fuchsia;">.</span><span style="color: navy;">UseUserProtectedKey</span><span>;</span>

<span style="color: green;">// name the key container </span>

<span style="color: navy;">parms</span><span style="color: fuchsia;">.</span><span style="color: navy;">KeyContainerName</span><span style="color: fuchsia;">=</span><span style="background: #f3f8f3; color: green;">"foo"</span><span>;</span>

<span style="color: green;">// Instantiate the provider</span>

<span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">rsa</span>

<span style="color: fuchsia;">=</span><span style="font-weight: bold; color: #2f2fff;">new</span><span style="color: #2b91af;">RSACryptoServiceProvider</span><span>(</span><span style="color: navy;">parms</span><span>);</span>

The provider creates a new public/private key pair for you the first time you try to do something that requires it (encrypt, sign, export).  When a new key is created, a dialog is presented to the user that allows the user to set the security level of the key container to high and apply a password to it.  The user can set the security level to High to password protect the key pair.

 

From this point on, any operation that attempts to access this key will result in a dialog being shown requesting the user’s password:

I’ll refrain from the obvious statement of how this could easily be spoofed by a malicious application on the user’s system.  If you are thinking about using this as an additional security layer, I’d suggest looking into using the KeyPassword property of the CspParameters object to set this password from within your application’s UI.

Creating the RSA Provider

This method shows how to create the default implementation of the RSA crypto provider that uses the machine key store.  If containerName is new, a new key pair is created prior to using or accessing it.  Every example from here on will use this method to create the provider.

<span style="font-weight: bold; color: #2f2fff;">private</span><span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">CreateRSA</span><span>(</span>

<span style="font-weight: bold; color: #2f2fff;">string</span><span style="color: navy;">containerName</span><span>)</span>

<span>{</span>

<span>    </span><span style="color: #2b91af;">CspParameters</span><span style="color: navy;">parms</span><span style="color: fuchsia;">=</span><span style="font-weight: bold; color: #2f2fff;">new</span><span style="color: #2b91af;">CspParameters</span><span>();</span>

<span>    </span><span style="color: navy;">parms</span><span style="color: fuchsia;">.</span><span style="color: navy;">KeyContainerName</span><span style="color: fuchsia;">=</span><span style="color: navy;">containerName</span><span>;</span>

<span>    </span><span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">rsa</span><span style="color: fuchsia;">=</span>

<span style="font-weight: bold; color: #2f2fff;">new</span><span style="color: #2b91af;">RSACryptoServiceProvider</span><span>(</span><span style="color: navy;">parms</span><span>);</span>

<span>    </span><span style="font-weight: bold; color: #2f2fff;">return</span><span style="color: navy;">rsa</span><span>;</span>

<span>}</span>

Deleting a key from a Key Container

Deleting a key from a key container is a little tricky, as the actual deletion takes place in the set method of the PersistKeyInCsp property, which isn’t intuitive.

<span style="font-weight: bold; color: #2f2fff;">private</span><span style="font-weight: bold; color: #2f2fff;">void</span><span style="color: navy;">DeleteKeyInContainer</span>

<span>(</span><span style="font-weight: bold; color: #2f2fff;">string</span><span style="color: navy;">containerName</span><span>)</span>

<span>{</span>

<span>    </span><span style="color: green;">// get a provider configured to the container</span>

<span>    </span><span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">=</span>

<span style="color: navy;">CreateRSA</span><span>(</span><span style="color: navy;">containerName</span><span>);</span>

<span>    </span><span style="color: green;">// Deletes the key entry from the container</span>

<span>    </span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">.</span><span style="color: navy;">PersistKeyInCsp</span><span style="color: fuchsia;">=</span><span style="font-weight: bold; color: #2f2fff;">false</span><span>;</span>

<span>    </span><span style="color: green;">// clears the key from the provider</span>

<span>    </span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">.</span><span style="color: navy;">Clear</span><span>();</span>

<span>}</span>

Importing and exporting keys

Key pairs can be imported and exported from a key container.  A key pair is actually a complex structure containing the different components the algorithm needs.  This can be exported as a blob (compatible with unmanaged implementations), as an XML string, and as a strut (RSAParameters):

<span style="font-weight: bold; color: #2f2fff;">private</span><span style="font-weight: bold; color: #2f2fff;">void</span><span style="color: navy;">ExportImportKeys</span><span>(</span><span style="font-weight: bold; color: #2f2fff;">string</span><span style="color: navy;">containerName</span><span>)</span>

<span>{</span>

<span>    </span><span style="color: green;">// get a provider configured to the container</span>

<span>    </span><span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">=</span>

<span>        </span><span style="color: navy;">CreateRSA</span><span>(</span><span style="color: navy;">containerName</span><span>);</span>

<span>    </span><span style="color: green;">// export the key pair 3 different ways</span>

<span>    </span><span style="font-weight: bold; color: #2f2fff;">byte</span><span>[] </span><span style="color: navy;">keyBlob</span><span style="color: fuchsia;">=</span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">.</span><span style="color: navy;">ExportCspBlob</span><span>(</span><span style="font-weight: bold; color: #2f2fff;">true</span><span>);</span>

<span>    </span><span style="font-weight: bold; color: #2f2fff;">string</span><span style="color: navy;">keyXml</span><span style="color: fuchsia;">=</span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">.</span><span style="color: navy;">ToXmlString</span><span>(</span><span style="font-weight: bold; color: #2f2fff;">true</span><span>);</span>

<span>    </span><span style="color: #761dbe;">RSAParameters</span><span style="color: navy;">keyParms</span><span style="color: fuchsia;">=</span>

<span style="color: navy;">rcsp</span><span style="color: fuchsia;">.</span><span style="color: navy;">ExportParameters</span><span>(</span><span style="font-weight: bold; color: #2f2fff;">true</span><span>);</span>

<span>    </span><span style="color: green;">// and import them back in</span>

<span>    </span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">.</span><span style="color: navy;">ImportCspBlob</span><span>(</span><span style="color: navy;">keyBlob</span><span>);</span>

<span>    </span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">.</span><span style="color: navy;">FromXmlString</span><span>(</span><span style="color: navy;">keyXml</span><span>);</span>

<span>    </span><span style="color: navy;">rcsp</span><span style="color: fuchsia;">.</span><span style="color: navy;">ImportParameters</span><span>(</span><span style="color: navy;">keyParms</span><span>);</span>

<span>}</span>

In these export methods, you must pass in a boolean that indicates whether you wish to export the private key as well as the public key (true to export both).  Note that when you bring a private key out of the Key Container you are greatly reducing the security of your application.  If you intend to distribute the public key, you will have to call one of these methods passing in false.  You do not have to use a different import method when importing a public key.

Encryption and decryption

Remember, you must encrypt with the public key, and decrypt with the private key.  This seems a little backwards, but it is how the algorithm works.  The following code creates an encryptor and decryptor pointing to different key containers (I use a randomly generated Guid for the name).  The public key is extracted from the decryptor and loaded into the encryptor, and a byte array is encrypted and decrypted.  The code after the CollectionAssert demonstrates that a public key cannot be used to decrypt.

<span style="color: green;">// create an encryptor and decryptor</span>

<span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">decryptor</span><span style="color: fuchsia;">=</span>

<span>    </span><span style="color: navy;">CreateRSA</span><span>(</span><span style="color: #761dbe;">Guid</span><span style="color: fuchsia;">.</span><span style="color: navy;">NewGuid</span><span>()</span><span style="color: fuchsia;">.</span><span style="color: navy;">ToString</span><span>());</span>

<span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">encryptor</span><span style="color: fuchsia;">=</span>

<span>    </span><span style="color: navy;">CreateRSA</span><span>(</span><span style="color: #761dbe;">Guid</span><span style="color: fuchsia;">.</span><span style="color: navy;">NewGuid</span><span>()</span><span style="color: fuchsia;">.</span><span style="color: navy;">ToString</span><span>());</span>


<span style="color: green;">// Export the public key from the decryptor</span>

<span style="font-weight: bold; color: #2f2fff;">string</span><span style="color: navy;">key</span><span style="color: fuchsia;">=</span><span style="color: navy;">decryptor</span><span style="color: fuchsia;">.</span><span style="color: navy;">ToXmlString</span><span>(</span><span style="font-weight: bold; color: #2f2fff;">false</span><span>);</span>


<span style="color: green;">// Load the public key into the encryptor</span>

<span style="color: navy;">encryptor</span><span style="color: fuchsia;">.</span><span style="color: navy;">FromXmlString</span><span>(</span><span style="color: navy;">key</span><span>);</span>


<span style="font-weight: bold; color: #2f2fff;">byte</span><span>[] </span><span style="color: navy;">expected</span><span style="color: fuchsia;">=</span><span style="font-weight: bold; color: #2f2fff;">new</span><span style="font-weight: bold; color: #2f2fff;">byte</span><span>[] { </span><span style="color: purple;">1</span><span>, </span><span style="color: purple;">2</span><span>, </span><span style="color: purple;">3</span><span> };</span>


<span style="color: green;">// encrypt with the public key</span>

<span style="font-weight: bold; color: #2f2fff;">byte</span><span>[] </span><span style="color: navy;">encrypted</span><span style="color: fuchsia;">=</span><span style="color: navy;">encryptor</span><span style="color: fuchsia;">.</span><span style="color: navy;">Encrypt</span><span>(</span>

<span>    </span><span style="color: navy;">expected</span><span>, </span><span style="font-weight: bold; color: #2f2fff;">true</span><span>);</span>


<span style="color: green;">// decrypt with the private key</span>

<span style="font-weight: bold; color: #2f2fff;">byte</span><span>[] </span><span style="color: navy;">decrypted</span><span style="color: fuchsia;">=</span><span style="color: navy;">decryptor</span><span style="color: fuchsia;">.</span><span style="color: navy;">Decrypt</span><span>(</span>

<span>    </span><span style="color: navy;">encrypted</span><span>, </span><span style="font-weight: bold; color: #2f2fff;">true</span><span>);</span>


<span style="color: #2b91af;">CollectionAssert</span><span style="color: fuchsia;">.</span><span style="color: navy;">AreEqual</span><span>(</span><span style="color: navy;">expected</span><span>,</span><span style="color: navy;">decrypted</span><span>);</span>


<span style="color: green;">// the following will fail</span>

<span style="color: navy;">encryptor</span><span style="color: fuchsia;">.</span><span style="color: navy;">Decrypt</span><span>(</span><span style="color: navy;">encrypted</span><span>, </span><span style="font-weight: bold; color: #2f2fff;">true</span><span>);</span>

Creating and Verifying Signatures

A signature is a cryptographic hash created by a private key that can be verified by its public key.  The public key can be compiled into code and be used to verify signatures that are generated by, say, a trusted employee at your company.  The following code demonstrates generating a signature and verifying it.  The private key is used to generate the signature and the public key is used to verify it.  The last line demonstrates that the public key cannot be used to generate a new license for the tampered data.

<span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">signer</span><span style="color: fuchsia;">=</span>

<span>    </span><span style="color: navy;">CreateRSA</span><span>(</span><span style="color: #761dbe;">Guid</span><span style="color: fuchsia;">.</span><span style="color: navy;">NewGuid</span><span>()</span><span style="color: fuchsia;">.</span><span style="color: navy;">ToString</span><span>());</span>

<span style="color: #2b91af;">RSACryptoServiceProvider</span><span style="color: navy;">verifier</span><span style="color: fuchsia;">=</span>

<span>    </span><span style="color: navy;">CreateRSA</span><span>(</span><span style="color: #761dbe;">Guid</span><span style="color: fuchsia;">.</span><span style="color: navy;">NewGuid</span><span>()</span><span style="color: fuchsia;">.</span><span style="color: navy;">ToString</span><span>());</span>


<span style="color: green;">// Export the public key from the signer</span>

<span style="font-weight: bold; color: #2f2fff;">string</span><span style="color: navy;">publicKey</span><span style="color: fuchsia;">=</span><span style="color: navy;">signer</span><span style="color: fuchsia;">.</span><span style="color: navy;">ToXmlString</span><span>(</span><span style="font-weight: bold; color: #2f2fff;">false</span><span>);</span>


<span style="color: green;">// Load the public key into the verifier</span>

<span style="color: navy;">verifier</span><span style="color: fuchsia;">.</span><span style="color: navy;">FromXmlString</span><span>(</span><span style="color: navy;">publicKey</span><span>);</span>


<span style="font-weight: bold; color: #2f2fff;">byte</span><span>[] </span><span style="color: navy;">rawData</span><span style="color: fuchsia;">=</span><span style="font-weight: bold; color: #2f2fff;">new</span><span style="font-weight: bold; color: #2f2fff;">byte</span><span>[] { </span><span style="color: purple;">1</span><span>, </span><span style="color: purple;">2</span><span>, </span><span style="color: purple;">3</span><span> };</span>


<span style="color: green;">// encrypt with the public key</span>

<span style="font-weight: bold; color: #2f2fff;">byte</span><span>[] </span><span style="color: navy;">signature</span><span style="color: fuchsia;">=</span><span style="color: navy;">signer</span><span style="color: fuchsia;">.</span><span style="color: navy;">SignData</span><span>(</span>

<span>    </span><span style="color: navy;">rawData</span><span>, </span><span style="background: #f3f8f3; color: green;">"SHA1"</span><span>);</span>


<span style="font-weight: bold; color: #2f2fff;">bool</span><span style="color: navy;">untampered</span><span style="color: fuchsia;">=</span><span style="color: navy;">verifier</span><span style="color: fuchsia;">.</span><span style="color: navy;">VerifyData</span><span>(</span>

<span>    </span><span style="color: navy;">rawData</span><span>, </span><span style="background: #f3f8f3; color: green;">"SHA1"</span><span>, </span><span style="color: navy;">signature</span><span>);</span>


<span style="color: green;">// Tamper!</span>

<span style="color: navy;">rawData</span><span>[</span><span style="color: purple;">2</span><span>] </span><span style="color: fuchsia;">=</span><span style="color: purple;">4</span><span>;</span>


<span style="font-weight: bold; color: #2f2fff;">bool</span><span style="color: navy;">tampered</span><span style="color: fuchsia;">=</span><span style="color: navy;">verifier</span><span style="color: fuchsia;">.</span><span style="color: navy;">VerifyData</span><span>(</span>

<span>    </span><span style="color: navy;">rawData</span><span>, </span><span style="background: #f3f8f3; color: green;">"SHA1"</span><span>, </span><span style="color: navy;">signature</span><span>);</span>


<span style="color: #2b91af;">Assert</span><span style="color: fuchsia;">.</span><span style="color: navy;">IsTrue</span><span>(</span><span style="color: navy;">untampered</span><span>);</span>

<span style="color: #2b91af;">Assert</span><span style="color: fuchsia;">.</span><span style="color: navy;">IsFalse</span><span>(</span><span style="color: navy;">tampered</span><span>);</span>


<span style="color: green;">// the following will fail</span>

<span style="color: navy;">signature</span><span style="color: fuchsia;">=</span><span style="color: navy;">verifier</span><span style="color: fuchsia;">.</span><span style="color: navy;">SignData</span><span>(</span>

<span>    </span><span style="color: navy;">rawData</span><span>, </span><span style="background: #f3f8f3; color: green;">"SHA1"</span><span>);</span>

Summary

Asymmetric cryptography is used to encrypt and sign data without having to rely on an insecure shared secret.  Key pairs are stored in a Key Container (a file on the hard drive) that can be scoped to the machine or to a specific user.  User-scoped containers can be secured using a password.  The public key is used to encrypt data and verify signatures.  The private key is used to decrypt data and generate signatures.

Whew!  That was a long one.  I’ve learned a bunch writing these four parts, including how much more I don’t know about the process.  I would have liked to go into more details about some of the aspects of cryptography on the .NET platform, but the more I say the more I risk being wrong.

Most likely, if you’re reading this, you care about security.  Don’t just stop here; if you need cryptography in your program, you need to keep learning.  If you use it and you don’t thoroughly understand it, you’re probably doing it wrong.  And this is one area where you have to do it right.

2 Responses to A .NET Cryptography Primer, Part Four

  • vonPryz says:

    For the note, the .Net RSA implementation does not work too well with large amounts of data. Actually it assumes you’ll feed it with only 58 byte blocks. Try something larger and you’ll get strange error message "Key not valid for use in specified state". Even MSDN sample code generates this error :-(

    Anyway, usually RSA is used to encrypt small amounts of data like symmetric keys. As symmetric crypto is way faster than asymmetric, this is perfect usage for mighty but slow encryption. I tested a small program that uses DES, 3DES and RSA to en/decrypt 58 char long a string. Then I executed crypting and decrypting in a loop for 1000 times. The results, as expected, were like this:

    DES: 00:00.16
    3DES:00:00.14
    RSA: 00:56.19

    I didn’t bother with measuring AES performance. That’s another story for another a day.

  • Will says:

    @vonPryz
    Yep, RSA can’t encrypt any byte array larger than its key’s modulus (around 1024 bit). Thanks for pointing that out.

    There are a number of ways to get around this. For instance, you can chunk your data prior to encryption and stitch it back together again later (think network packets and SSL), or you can use symmetric encryption to encrypt the data, then use RSA to encrypt the key and IV. For more information about this, see
    http://msdn.microsoft.com/en-us/library/ms867080.aspx

    There are different implementations of RSA that support large key sizes. For more information on those, see
    http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/distrib/dscj_mcs_xxgl.mspx?mfr=true