Chaining Algorithms

Now we know how to take a stream and either encrypt or
decrypt the information contained within using a symmetric algorithm.
That's pretty handy, but there's a
problem. The output from the encryption
process is binary and not printable.
What if we need to save the encrypted bytes in some kind of a text
medium, like an XML file? An easy way to
do this is to Base64 encode the data.

Luckily, the crypto framework provides a pair of
ICryptoTransform implementations that will transform to and from a Base64
encoding. The classes are
ToBase64Transform and FromBase64Transform.
To use them, we will take advantage of CryptoStream chaining.
Here are the replacement methods for the code
above:

You pass the first CryptoStream into the constructor of the
second. When you write into the outer
CryptoStream (crypt), the write will be passed onto the underlying stream,
toBase64. toBase64 then does its work
and passes the results onto the underlying FileStream. Pretty nifty!
You can continue to chain algorithms (or any other action, really) as
much as you see fit. This model is
incredibly useful and extensible, and was one of the better decisions made by
the crypto framework team.

Hashing

Hashing uses a non-reversible algorithm, such as MD5 or
SHA1, to create a unique short sequence of bytes from a larger sequence of
bytes. Hashing is useful for verifying
that the contents of a message or file has not changed since creation.

The primary interface to HashAlgorithm is the ComputeHash
method. It's overloaded to take either a
Stream or a byte[]. Here's a sample:

The code is quite a bit simpler than the encryption/decryption code. Also notice the
alternate way to create a base64-encoded string from a byte[],
Convert.ToBase64String(). Alternatively,
instead of base64 encoding the hash code, you could easily put the byte[] into
a SQL Server field of type binary. To
use other algorithms, just change the string passed to HashAlgorithm.Create().
If you're going to use a KeyedHashAlgorithm,
you can just use new on the actual implementation object. That way, you can pass the key into the
constructor instead of having to set it after creation by a factory method.

Finally, if you use hashing to store passwords, be sure to use a salt. A salt is a small sequence of bytes prefixed
or appended to the password before hashing. For example, instead of just hashing the password "my-password", you
would hash "12345" + "my-password." Having a salt helps to prevent dictionary attacks against your password
database, should anyone obtain a copy of it. There's a class called PasswordDeriveBytes in the crypto framework that
looks interesting, but unfortunately, it appears to only be for generating keys
for the symmetric algorithms.

Public Key Crypto

So far, we've covered private key encryption and
hashing. The last area to cover is
public-key-based cryptography, which is primarily used for key exchange and
message signing. Public key encryption
uses two keys. Any information encrypted
with one key can be decrypted with the other.
The two keys are known as the public key and the private key. The private key must be kept safe and secret, while the public key can be distributed to anyone who wants it, through
a number of mediums. This makes public
key encryption ideal for encrypting traffic between two parties without having
to set up a secret key beforehand. The
downside is that is very slow.

The framework supports two algorithms, RSA and DSA. RSA can be used for key exchange
or to sign messages, while DSA can only be used to sign.
First, let's check out the code to sign a message:

Again, this is pretty quick code for something that's really
quite complex. The crypto team did a
great job wrapping up support for signature generation. Basically, we create an
AsymmetricSignatureFormatter to create a signature and an AsymmetricSignatureDeformatter
to verify a signature. One very
important thing: we must call SetHashAlgorithm() before calling
CreateSignature
or VerifySignature,
and you must set the hash algorithm to the same type used to create the
hash. Notice that I'm using SHA1 both to
create the hash and for the signature.
Again, to switch algorithms, just instantiate a different formatter.

The second interesting thing you can do with public key crypto
is key exchange. With key exchange, I
can create a key, sign it with my private key, encrypt it with my partner's
public key, and then send it along. My
partner can decrypt it with their private key, and verify the signature with my
public key. After that's done, we've
agreed on a secret key. This concept is
codified with the AsymmetricKeyExchangeFormatter and AsymmetricKeyExchangeDeformatter.
As of this writing, only RSA-based key exchanges
are implemented. Here's a sample that
exchanges a key between two parties:

This example can be a bit confusing at first. Remember that during a key exchange, we have
two parties; for instance, Alice and Bob. If Alice wants to send Bob something secret,
she has to encrypt the information with Bob's public key. In the sample code, we're
pretending first to be Alice, then Bob. Alice
only has access to Bob's public key, so she loads it into an RSA algorithm
object and creates the key exchange. Bob, who has both the public and private keys, can then crack the key
exchange and extract the secret. It
amazes me how little code this ends up needing for implementation. Obviously, you'll want a slightly stronger
implementation of CreateSecret().

That's It!

Well, that's really it for
System.Security.Cryptography. In this
rather lengthy article, we've covered symmetric, asymmetric, and one-way
algorithms. We've also covered how to
use them together and exposed a couple of quirks.
I hope this article has been as enlightening for you to read as it was
for me to research and write.

Ben Lowery
is a developer at FactSet Research Systems, where he works on all things great and small.