A NET Cryptography Primer, Part One

This post assumes the reader understands some of the basics of cryptography.  If you don't know the basics, I strongly suggest you read the following first:  cryptographic hashing, asymmetric (public/private key) and symmetric (shared secret) cryptographic algorithms, and Cryptography on MSDN.

Managed vs. Unmanaged

The .NET platform provides a suite of classes in the System.Security.Cryptography namespace that provide for industry standard encryption.  These classes are split into two main groups:  managed and unmanaged class wrappers. 

Managed encryption classes are pure .NET implementations of cryptographic algorithms.  All the code is managed.  They are typically identifiable by the word "Managed" appended to the end of the algorithm class name.  Unmanaged class wrappers are essentially proxies that provide the same interfaces as the managed algorithm classes but they call into CAPI, or the CryptoAPI provided by Windows (advapi32.DLL).  Supposedly, there is no difference between them, and can be used interchangeably.  In fact, cryptographic classes implement the provider model; the specific implementation of a particular algorithm can be changed via configuration files and your code, assuming you use the static Create() method to instantiate an algorithm, would be none the wiser.

The .NET platform also has a number of classes designed to encrypt, sign, and perform other cryptographic functions specifically on XML files.  This post doesn't cover this part of the framework.

Encoding

One of the first things you encounter when using cryptography in .NET is that, while most of the data you wish to encrypt/decrypt/hash are strings, all of the cryptography classes work on arrays of bytes.  This means you are going to have to convert your strings into arrays of bytes to encrypt or hash them, and then convert the resulting encrypted arrays of bytes into strings to make them easily storable, transmittable, and understandable to humans.  It is very important to understand how encoding works; a weak understanding here can result in hard to root out bugs that will drive you wacky down the road.

A side note; I'm not saying that you have to convert your encrypted data into a string; its just the most common scenario.  You could always store the byte arrays in your database as a blob or write them to a binary file.

From String

The most common encoding task in cryptography is to prepare string data for encryption.  Before a string can be encrypted it must be converted into an array of bytes.  This is relatively simple, as strings in .NET are Unicode and the .NET platform provides the correct encoder for the current system through the public static property Encoding.Default.  Simply call the GetBytes() method of this class, passing in your string to convert it to an array of bytes. 

From and Array of Bytes

Converting from an array of encrypted bytes to a string is much more complex.  Bytes can store numbers 0 to 255, so to convert a byte to string you must first chunk the byte array into equal sized portions, then convert these chunks into a fixed number of characters.  This sounds more complicated than necessary; It would seem that, since a byte is a number between 0 and 255, why not just represent it by the number itself?  That is doable, but answer me this:  What is the byte array length for the number-encoded string:  "123"?  It could be three ({1,2,3}), or two ({12,3} or {1,23}), or one ({123}).  There is no way to decode this string into the original byte array.

In order to be able to both encode a byte array into a string and be able to decode it back into the original byte array, each byte must be represented by a fixed number of characters.  It would be simple to encode the above example into the string "001023" and figure out that the original byte array was {1,23}.  However, you notice that we have gone from two bytes, 1 and 23, to six characters, each of which is (up to) 32 bytes.  The encoded string now takes up 192 bytes of memory.  Not exactly efficient.  And when you get to Usenet file sizes (4gb DVD rip -> 4294967296 bytes, encoded at 96 bytes per byte -> 384 gb!!!) you can see where this encoding starts to break down.

The next better alternative to this 3:1 encoding is to do 1:1 encoding.  This means, for each byte 0 to 255, pick a single character to represent that number.  ASCII seems a natural winner for this one, as the extended code page has 255 characters–one for every number!  But ASCII contains control characters, such as tab, newline and beep which make handling ASCII encoded strings troublesome to type or print, and you're still not very efficient.  You can use Encoding.ASCII to convert to and from byte arrays.  Alternatively, you can use one of the Unicode versions of the Encoding class (UTF-7, UTF-8, UTF-32, and Default) to encode the byte array into a string of Unicode characters.  Oddly enough, although UTF-32 is supposedly the most inefficient of the Unicode encodings, in .NET it is almost the most efficient, compressing a 255 byte long array into just 64 Unicode characters at just 5 bytes over ASCII.

Other common encodings are hex (00 – FF) and Base64.  The .NET platform doesn't have a hex conversion class, for some reason, so you have to roll your own, or use these methods:

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;summary&gt;</span>

<span style="color: gray">///</span><span style="color: green"> Creates a hex string from byte array.</span>

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;/summary&gt;</span>

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;param name=&quot;data&quot;&gt;</span><span style="color: green">The byte data.</span><span style="color: gray">&lt;/param&gt;</span>

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;returns&gt;</span><span style="color: green">A hex string (base16 encoded)</span>

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;/returns&gt;</span>

<span style="font-weight: bold; color: #2f2fff">public</span><span> </span><span style="font-weight: bold; color: #2f2fff">static</span><span> </span><span style="font-weight: bold; color: #2f2fff">string</span><span> </span><span style="color: navy">HexFromByteArray</span><span>(</span><span style="font-weight: bold; color: #2f2fff">byte</span><span>[] </span><span style="color: navy">data</span><span>)</span>

<span>{</span>

<span>&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #2f2fff">return</span><span> </span><span style="color: #2b91af">BitConverter</span>

<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: fuchsia">.</span><span style="color: navy">ToString</span><span>(</span><span style="color: navy">data</span><span>)</span>

<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: fuchsia">.</span><span style="color: navy">Replace</span><span>(</span><span style="background: #f3f8f3; color: green">&quot;-&quot;</span><span>, </span><span style="background: #f3f8f3; color: green">&quot;&quot;</span><span>);</span>

<span>}</span>

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;summary&gt;</span>

<span style="color: gray">///</span><span style="color: green"> Creates a byte array from a hex (base16) </span>

<span style="color: gray">///</span><span style="color: green"> encoded string</span>

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;/summary&gt;</span>

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;param name=&quot;data&quot;&gt;</span><span style="color: green">A hex string (base16 </span>

<span style="color: gray">///</span><span style="color: green"> encoded).</span><span style="color: gray">&lt;/param&gt;</span>

<span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;returns&gt;</span><span style="color: green">A byte array</span><span style="color: gray">&lt;/returns&gt;</span>

<span style="font-weight: bold; color: #2f2fff">public</span><span> </span><span style="font-weight: bold; color: #2f2fff">static</span><span> </span><span style="font-weight: bold; color: #2f2fff">byte</span><span>[] </span><span style="color: navy">HexToByteArray</span><span>(</span><span style="font-weight: bold; color: #2f2fff">string</span><span> </span><span style="color: navy">data</span><span>)</span>

<span>{</span>

<span>&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #2f2fff">byte</span><span>[] </span><span style="color: navy">result</span><span> </span><span style="color: fuchsia">=</span><span> </span><span style="font-weight: bold; color: #2f2fff">new</span><span> </span><span style="font-weight: bold; color: #2f2fff">byte</span><span>[</span><span style="color: navy">data</span><span style="color: fuchsia">.</span><span style="color: navy">Length</span><span> </span><span style="color: fuchsia">/</span><span> </span><span style="color: purple">2</span><span>];</span>

<span>&nbsp;&nbsp;&nbsp; </span><span style="font-weight: bold; color: #2f2fff">for</span><span> (</span><span style="font-weight: bold; color: #2f2fff">int</span><span> </span><span style="color: navy">i</span><span> </span><span style="color: fuchsia">=</span><span> </span><span style="color: purple">0</span><span>; </span><span style="color: navy">i</span><span> </span><span style="color: fuchsia">&lt;</span><span> </span><span style="color: navy">result</span><span style="color: fuchsia">.</span><span style="color: navy">Length</span><span>; </span><span style="color: navy">i</span><span style="color: fuchsia">++</span><span>)</span>

<span>&nbsp;&nbsp;&nbsp; {</span>

<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: navy">result</span><span>[</span><span style="color: navy">i</span><span>] </span><span style="color: fuchsia">=</span><span> </span><span style="font-weight: bold; color: #2f2fff">byte</span><span style="color: fuchsia">.</span><span style="color: navy">Parse</span><span>(</span>

<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: navy">data</span><span style="color: fuchsia">.</span><span style="color: navy">Substring</span><span>(</span><span style="color: navy">i</span><span> </span><span style="color: fuchsia">*</span><span> </span><span style="color: purple">2</span><span>, </span><span style="color: purple">2</span><span>), </span>

<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #2b91af">NumberStyles</span><span style="color: fuchsia">.</span><span style="color: navy">HexNumber</span><span>);</span>

<span>&nbsp;&nbsp;&nbsp; }</span>

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

<span>}</span>

 

Hex and Base64 encodes are much easier on the eyes than ASCII or Unicode encoded arrays, but they are much more inefficient.  Hex encodes bytes at a 2:1 ratio; Base64 is slightly more efficient.  Both Base64 and Hex encodings are very useful when you need to print out encrypted data or when you need users to enter encrypted data into an application, as both encoding schemes consist solely of characters that can be printed or typed.

Summary

Encryption in .NET requires you to convert strings into byte arrays and vise versa.  It is therefore important that you understand this process and the different ways it is done in order to prevent hard to debug flaws from creeping into your code.  The encoding you choose should also meet your needs for efficiency and usability.

Part Two Preview

Part two will delve into hash algorithms and include sample code for hashing passwords.  Sweet!

kick it on DotNetKicks.com

2 Responses to A NET Cryptography Primer, Part One

  1. raj says:

    excellent series on security… very clear and well written keep up the good work – raj

Comments are closed.