Cryptography the .NET Way

rior to the .NET Framework, programming encryption was sort of an obstacle race. You first had to find the right algorithm, then set up the key, and finally struggle with the programming interface of the library. With the .NET Framework, doing encryption is no longer a cryptic task. Simple and well-designed classes let you tackle symmetric, asymmetric, and hash algorithms.

Cryptography is as ancient as the world is. Have you heard about the ingenious tricks performed by Roman commanders such as Julius Caesar and Augustus to send orders to detached troops? They scrambled their messages by mapping letters in the alphabet with another letter by a fixed number of positions. This number was the actual key to decode the message. In particular, Caesar used to shift letters by 3 positions (A becomes D and Z becomes C), whereas Augustus preferred to shift by 1. Caesar’s cipher book probably was the predecessor of today’s public key encryption algorithms. A good set of cryptographic tools is vital in many sectors of the software industry. Cryptography protects the confidentiality of the data being exchanged, but it also prevents attacks that a sniffer could plan once he or she knows internal aspects of the system. Using cryptography you can make connections safer as your code enables reliable and secure user authentication.

Cryptography itself is not that difficult to work with, but it can have an overly complex API. Since in many real-world scenarios you can’t just do without encryption, the more a software platform supports you with easy-to-use tools, the better. A good measure to evaluate such tools is the level of expertise in the cryptographic science they assume. In the .NET Framework, cryptographic services have been designed to smooth difficulties quite a bit. As a result, using cryptographic providers and encryption classes is not harder than using, say, XML readers or ADO.NET data relations.

In this article I’ll take you on a whistle-stop tour of cryptography in the .NET Framework. I’ll discuss symmetric and asymmetric algorithms as well as hashing techniques. After that, I’ll show you practical implementations of encryption and hashing on disk files and streams. Using encryption in Web applications is nearly identical to using it in Windows applications. The final part of this article discusses how the ASP.NET infrastructure makes use of encryption internally.

Cryptography Overview
Cryptography is the science (or is it better described as an art?) that encrypts information so that it looks completely different from the original?scrambled and camouflaged. Cryptography must be a two-way and lossless channel. In other words, there must be a way for a user to decrypt the encrypted information and regain the original information. Only one user should be able to perform this task?the user who holds the key used to encrypt. Generations of scientists and mathematicians and even hobbyists have worked hard to come up with effective techniques to define keys and generate algorithms.

Hash values represent an effective way to verify the integrity of the data being received over a potentially insecure channel.

Cryptography serves three main purposes: confidentiality, data integrity, and authentication. Confidentiality means that data is scrambled and hidden from ill-intentioned, or simply too curious, eyes. Data integrity prevents tampering with the data, whereas authentication consists in verifying the identity of the sender to ensure that he or she is exactly who they say that they are.

Cryptography recognizes four main categories of functions: symmetric algorithms, asymmetric algorithm, signatures, and hash algorithms.

  • Symmetric algorithms perform a transformation on data, camouflaging its real contents. In doing so, it employs a single secret key to both encrypt and decrypt data. Each individual who gets the key can decrypt any file encrypted with the same key.
  • Asymmetric algorithms use a pair of keys, known as public/private keys. Anyone can use a public key to encrypt data. Only a particular private key, though, can decrypt that content. To set up a public key encryption, you must use a pair of public and private keys that are mathematically linked. Once you obtain a pair of keys, you keep the private key for yourself and distribute the public key to anyone that needs to send data to you. The algorithm is said to be asymmetric because two different keys are involved?one to encrypt and one to decrypt.
  • A digital signature is designed to ensure that any received data originates from a specific user. A digital signature is a block of data that is unique to a party.
  • People often use hash functions to digitally sign documents. A hash function creates a fixed-length array of bytes given a block of data of any length. More importantly, the hash code generated is mathematically guaranteed to be random and unique and not particularly affine to the data. Put another way, two nearly identical streams of data generate radically different hash codes.

Cryptographic API
The .NET cryptographic classes are located in the System.Security.Cryptography namespace. Below this common root you’ll find specialized namespaces for symmetric and asymmetric algorithms, hash functions, digital signatures, and random number generators. The .NET Framework implementation of cryptography builds on top of the unmanaged implementation of Microsoft CryptoAPI even though there are areas in it that are purely made of managed code.

The object model for the cryptographic API is layered and includes a first abstract level in which classes of algorithm are defined: SymmetricAlgorithm, AsymmetricAlgorithm, and HashAlgorithm. Each class contains a bunch of still abstract but more specific subclasses. Each subclass identifies a particular algorithm such as RC2, DES, and SHA1 (as well as others). Finally, the full implementation of cryptographic functions is available within provider classes that you actually use in .NET applications.

An application?no matter the model, be it Windows Forms, ASP.NET, or Web services?calls into the high-level API exposed by the classes. These classes in turn define a wrapper object to access the cryptographic service provider (CSP) implementation of the particular algorithm chosen.

From a design point of view, the CSP is the component that encrypts and decrypts. It is a separate component from the provider class that exposes the algorithm to the end user application. In general, a CSP is a server capable of performing a standard set of tasks related to cryptography. Each provider class?the class that .NET applications work with to do cryptographic tasks?relies on the CSP to physically perform the task. The CSP is hidden to the programmer in the sense that a developer uses a higher level programming interface. The CSP can be a Win32 unmanaged library or a managed class. If it is a Win32 component, it is one defined within the CryptoAPI library. In the .NET Framework, most classes use unmanaged Win32 providers defined by the CryptoAPI framework.

In Table 1 I’ve listed the cryptographic classes for symmetric algorithms. Table 2 and Table 3 list the classes for asymmetric and hash algorithms, respectively. As you can see, the classes with the Managed suffix are based on managed code; all the other classes call back into the unmanaged CryptoAPI service providers.

Table 1: Classes available for symmetric encryption. All classes inherit from SymmetricAlgorithm

Algorithm

Cryptographic class

Description

DES

DESCryptoServiceProvider

Wrapper class to access the standard CSP for the Data Encryption Standard (DES) algorithm

RC2

RC2CryptoServiceProvider

Wrapper class to access the standard CSP for the RC2 algorithm

Rijndael

RijndaelManaged

Wrapper class to access the standard CSP for the Rijndael algorithm. The CSP is made of managed code.

TripleDES

TripleDESCryptoServiceProvider

Wrapper class to access the standard CSP for the Triple DES algorithm

Table 2: Classes available for asymmetric encryption. All classes inherit from AsymmetricAlgorithm.

Algorithm

Cryptographic class

Description

DSA

DSACryptoServiceProvider

Wrapper class to access the standard CSP for the Digital Signature Algorithm (DSA) algorithm.

RSA

RSACryptoServiceProvider

Wrapper class to access the standard CSP for the RSA algorithm.

Table 3: Classes available for hash functions.

Algorithm

Cryptographic class

Description

MD5

MD5CryptoServiceProvider

Computes the MD5 hash for the input data using the implementation provided by the CSP.

SHA1

SHA1CryptoServiceProvider, SHA1Managed

The classes compute the SHA1 hash for the input data using the implementation provided by the CSP. The former class uses unmanaged code; the latter is based on managed code.

SHA256

SHA256Managed

Computes the SHA256 hash for the input data using managed code.

SHA384

SHA384Managed

Computes the SHA384 hash for the input data using managed code.

SHA512

SHA512Managed

Computes the SHA512 hash for the input data using managed code.

Encrypt and Decrypt Data
Let me walk you through a few practical examples of how to use cryptography in .NET applications. I’ll show you private and public key algorithms as well as hashing. At this point you may wonder when to use symmetric, private key algorithms instead of asymmetric, public key algorithms. I know of one golden rule that may help you to decide. Symmetric encryption is ideal to encrypt large amounts of data, and subsequently is designed to work on streams. Microsoft provides the CryptoStream class (more on this shortly), which works on top of an existing Stream class and automatically encrypts its contents. The CryptoStream class is the recommended and most effective way to work with symmetric cryptography from within managed applications.

.NET Framework classes define a wrapper object to access the cryptographic service provider for particular algorithm chosen.

In contrast, asymmetric encryption is recommended for small amounts of data and is typically used to encrypt keys. Let’s begin with public key encryption and consider the RSA service provider.

First create an instance of the service provider.

   RSACryptoServiceProvider rsa;   rsa = new RSACryptoServiceProvider();

Next, configure it by importing a parameter object. The parameter class is named RSAParameters and you load it using the method ImportParameters.

   RSAParameters info;   info = new RSAParameters();   info.Modulus = PublicKey;   info.Exponent = Exponent;   RSA.ImportParameters(info);

The necessary parameters?modulus and exponent?have to do with the internal cryptographic system used by the algorithm. The RSA algorithm works by taking two large primes (say P and Q) and computes their product. This value is known as the modulus. Next, it takes a number, say E, less than the modulus and relatively prime to (P-1)*(Q-1). Another number, say D, is found such that its product by E is divisible by (P-1)*(Q-1).

The values E and D are called the public and the private exponents. The modulus and the public exponent form the public key for the RSA algorithm. You only need to pass these two parameters to encrypt some data. Similarly, you form the private key using the modulus and the private exponent. At this point, to encrypt and decrypt you no longer need the two large prime numbers from which everything originated. The letters used are not coincidental as they appear in the original paper of the authors and also name the various members of the RSAParameters class. The RSA algorithm was invented by Ronald L. Rivest, Adi Shamir, and Leonard Adleman in 1977.

Once you configure the parameters you only need to call the Encrypt and Decrypt methods. To encrypt you pass the input data as an array of bytes and get an array of scrambled bytes. The Decrypt method takes an array of scrambled bytes and returns output?the decrypted value?as an array of bytes.

CryptoStream Class
Private-key algorithms are also called block ciphers because they are used to encrypt one block of data at a time. The size of the block depends on the particular algorithm, which is normally made of a few bytes. RC2, DES, and TripleDES use 8 bytes, while Rijndael varies from a default of 16 to 24 or sometimes 32 bytes. The key determines how to cipher each block. Note that unless you take special measures, if you try to encrypt two identical blocks of text, the algorithm’s output will be identical. This opens up a potential hole in the security of the algorithm that may leave your data open to attackers. For this reason, a second, not necessarily private value is used?the initialization vector (IV). By using the value of the vector, the algorithm chains together the various ciphered blocks in such a way that the next block is encrypted using the key modified by the output of the previous blocks. The IV determines how the key for the next block is created based on the previous ciphered blocks.

To encrypt using a private-key algorithm, you first make an instance of the provider class. In the code snippet below I use the Rijndael algorithm, one of the few implemented in pure managed code.

   RijndaelManaged crypto;   crypto = new RijndaelManaged();   byte[] Key = {...};   byte[] IV = {...};   ICryptoTransform trans;   trans = crypto.CreateEncryptor(Key, IV);

Once the key and the initialization vector have been defined (both sides must agree on these values, know them, and keep them secret), you pack the parameters in a ICryptoTransform object. This object is in charge of performing the actual encryption or decryption. The CreateEncryptor and CreateDecryptor methods just return an instance properly configured for the action to accomplish.

To complete the operation, you can then either call the TransformBlock method or, better yet, use the CryptoStream class.

The class defines a Stream object that links data streams to cryptographic transformations. You initialize CryptoStream passing another stream object that acts as the data provider. The input stream can be any stream that is, in turn, linked to some physical data. For example, it can be a file stream or a network stream. Internally, the class transparently performs any tasks that are needed to encrypt and decrypt.

   FileStream file;   file = new FileStream(fileName, FileMode.Open);   CryptoStreamMode mode = CryptoStreamMode.Write;   CryptoStream enc;   enc = new CryptoStream(file, trans, mode);

The CryptoStream class constructor takes three arguments:

  • The input Stream as an instance of any Stream-based class,
  • The ICryptoTransform object, which performs the decryption and encryption. You obtain the crypto transformer directly from the class that wraps the cryptographic service provider.
  • The working mode. It is a value picked from the CryptoStreamMode enumeration?Read or Write. In read mode the class decrypts, and in write mode it encrypts.

Using CryptoStream has a couple of advantages:

  • First, you don’t need to know much about the algorithm being used under the hood. You must know the agreed key and vector. You must create a decryptor or an encryptor object and pass it to the stream class. The hard work with cipher blocks and output chaining?which is indeed necessary?takes place within the .NET class.
  • Second, in the .NET Framework you can build a pile of Stream objects and have each work on top of the other. In the code snippet above you read the content of a file with the FileStream class and, by simply linking the stream to the cryptographic stream, you read or write its encrypted contents! No other effort is required on your end. You use the instance of the CryptoStream class as you would do with any other Stream class, including the original FileStream class. Whenever you call Read the text is automatically and transparently decrypted; whenever you call Write the text is silently encrypted and used with the configured key and initialization vector.

If you have an application that manages disk files through streams, with an extra step you can have it use encryption too. If you don’t have such an application, but plan to write a similar one, the pair FileStream and CryptoStream is extremely powerful. CryptoStream inherits from Stream so it features all the I/O methods of streams.

Listing 1 shows a complete application that creates an encrypted text file and decrypts it. The example makes use of the Rijndael algorithm and the CryptoStream class.

Hash Algorithms
Hash functions are a fundamental piece in cryptography today. They map arbitrary binary data to small arrays of a fixed length called hash values. Hash functions are commonly used with digital signatures and for verifying data integrity.

Hash functions exploit some mathematical principles by means of which small changes to the data result in completely different and unpredictable hash values. Here’s an example of how to compute a hash value.

   byte[] input = new byte[100];   // Fill the input buffer somehow   byte[] output;    SHA1CryptoServiceProvider sha;   sha = new SHA1CryptoServiceProvider();    output = sha.ComputeHash(input);

The code necessary to generate a hash value doesn’t change much if you use other hash algorithms. You simply change the name of the class.

Hash values represent an effective way to verify the integrity of the data being received over a potentially insecure channel. For example, you can attach a hash value to a string of text you’re sending. Your code calculates the hash value based on the contents that you want to send. When data comes back you can compare the hash value of received data to the original hash value. If the two values match, then the data is intact; otherwise, the necessary conclusion is that something tampered with your data. ASP.NET widely exploits just this mechanism.

Encryption in ASP.NET
The ASP.NET Framework uses different forms of encryption in various contexts. In particular, the view state of Web pages is normally hashed to detect tampering. In addition, when ASP.NET impersonates a fixed account, the user ID and password of the chosen identity can be encrypted and stored in the system registry. ASP.NET automatically encrypts the contents of the cookie it uses for forms authentication. Let’s review these three aspects of how ASP.NET uses encryption in more detail.

You use the instance of the CryptoStream class as you would do with any other Stream class.

The view state of a page is the serialized state of the page’s child controls. The information is stored in a hidden field and carried back and forth between the Web server and the browser. When the page posts back, the view state is retrieved from the hidden field and used to restore the state from which the original page originated. Even from this concise description, a fact emerges clearly: The view state is a potential vehicle of infection for the Web server. What if a malicious user modifies the contents of the view state? The view state is Base64 encoded but even that is weak protection. So ASP.NET digitally signs the serialized content of the view state by appending a hash code computed on it. This digital signature is a 20-byte string which is then Base64 encoded with the remainder of the view state. Such a form of protection effectively prevents tampering but can’t guarantee confidentiality. To provide for that you should use HTTP over secure, but encrypted, sockets (HTTPS).

By default, ASP.NET works with impersonation turned off and all the pages run under the ASP.NET worker process account. To impersonate a particular user, though, you must indicate their user ID and password within a configuration file?typically, web.config. A configuration file is made of clear (unencrypted) text and resides within the application’s Web space?an area of disk space more exposed to the risk of malicious attacks. Can you store sensitive information such as user ID and password in an encrypted form? Starting with ASP.NET 1.1, this is possible thanks to the services of the aspnet_setreg utility. The utility employs the Win32 API function CryptProtectData to encrypt the input information. It writes the binary text to the registry to a path decided by the code. In the web.config file only the registry path is written. To recover the credentials of the user to impersonate, an attacker must be able to execute the Win32 API CryptUnprotectData with the same logon credential as the encrypter and on the same Web server machine.

ASP.NET bases its forms authentication on an encrypted cookie that it creates once the user’s credentials are recognized by the HTTP runtime. Forms authentication works by redirecting unauthenticated users to a login page. After the credentials have been entered, ASP.NET validates the credentials and creates the cookie with a default timeout of 30 minutes. Next, ASP.NET redirects the request back to the original page. Next time, when IIS processes the request, it finds the cookie with the user’s credentials and makes the request pass the security checkpoint. The cookie encryption is requested in the section of the web.config file and it uses the DES algorithm to encrypt the authentication ticket contained in the cookie. Note that the modified cookie is still subject to plain text modifications. In this particular context, encryption is really effective only if accompanied with hash validation. When the forms authentication protection settings require both encryption and validation, the ticket is encrypted and a hash is computed on it. ASP.NET appends the hash value to the cookie and uses it to verify that the original ticket has not been tampered with along the way.

Summary
Communication over networks is too often susceptible to unwanted reading or unauthorized spying. Cryptography?the art of data camouflage?is the technique that saves from such troubled waters. Cryptography has many facets.

You can use cryptography to encrypt and decrypt disk files as well as dynamically and transparently scramble data traveling over network channels. In doing so, you make potentially unsecure channels inherently more secure, thus providing data integrity and authentication.

The .NET Framework’s cryptographic classes manage many details of cryptography for you so you don’t have to be an algorithm expert or a mathematician. Some of .NET’s cryptographic classes are simply wrappers for the Win32 CryptoAPI set of libraries, while others are made of pure managed code. You can encrypt data using symmetric and asymmetric algorithms. For symmetric algorithms in particular?probably the most common case in end user applications?.NET provides an extremely handy facility called the CryptoStream class.

Encryption is not just exposed as a programmable feature?the .NET Framework uses it internally. ASP.NET, a key subset of the .NET Framework, exposes good cryptographic tools for you to use.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: