
Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]
.pdf
838 C H A P T E R 2 4 ■ P R O F I L E S
From this point, you can access the profile details exactly as you would with the SqlProfileProvider. For example, here’s the code you need to copy the address information into a series of text boxes:
protected void cmdGet_Click(object sender, EventArgs e)
{
txtName.Text = Profile.AddressName; txtStreet.Text = Profile.AddressStreet; txtCity.Text = Profile.AddressCity; txtZip.Text = Profile.AddressZipCode; txtState.Text = Profile.AddressState; txtCountry.Text = Profile.AddressCountry;
}
Figure 24-8 shows the test page.
Figure 24-8. Testing a custom Profiles provider
Summary
In this chapter, you took a detailed look at the new profiles feature in ASP.NET 2.0. You considered how it works behind the scenes, when it makes the most sense, and how to configure its behavior. Next, you tried out profiles with a full-scale example that stores a user-specific shopping cart.
The final part of this chapter explored how to create a simple Profiles provider of your own. Using these techniques, you can overcome many of the limitations of the profiles feature (such as the way it serializes all information into a single, opaque field). The ultimate decision of whether to use profiles or a custom database component still depends on several factors, but with this ability profiles become a valid alternative.

C H A P T E R 2 5
■ ■ ■
Cryptography
Over the past four chapters, you learned how to identify users through several supported authentication mechanisms and how to implement authorization for those users in your applications. ASP.NET supports rich services such as the Membership and Roles APIs that help you implement this functionality. However, although authentication and authorization are two important factors for securing applications, you have to keep much more in mind. Therefore, .NET has a bit more functionality in store. One of the most important examples is .NET’s support for cryptography—the science of scrambling data to ensure confidentiality and adding hash codes to detect tampering.
.NET includes the rich CryptoAPI for a wide range of cryptographic tasks, such as creating hashes of different types (MD5, SHA1, and so on) and implementing the most important symmetric and asymmetric encryption algorithms. And if that’s not enough, .NET 2.0 comes with separate functions for protecting secrets on the local machine or on a per-user basis through a completely managed wrapper for the Windows data protection API (DPAPI). In this chapter, you’ll learn when to use these APIs and how to use them correctly.
Encrypting Data: Confidentiality Matters
In Chapter 20, you learned how to use hashing to protect passwords. With hashing, you store a digi- |
|
tal fingerprint of the original data, not the data itself. As a result, you have no way to reverse the |
|
hashing process to retrieve the original data. All you can do is hash new data and perform a com- |
|
parison. |
|
The hashing approach is the most secure practice for validating passwords. However, it’s not |
|
much help when you want to protect sensitive data that you need to decrypt later. For example, if |
|
you’re creating an e-commerce application, you probably want to store a user’s credit card informa- |
|
tion so it can be reused in later orders. In this scenario, your application needs to be able to retrieve |
|
the credit card details on its own. Hashing doesn’t apply. |
|
Often developers deal with this situation by storing sensitive data in clear text. They assume |
|
that because the data is kept in a secure server-side storage location, they don’t need to go to the |
|
additional work of encrypting it. However, security experts know this is not true. Without encryp- |
|
tion, a malicious user needs to gain access to the server for only a matter of minutes or even seconds |
|
to retrieve passwords or credit card numbers for every customer. Security breaches can occur |
|
because of poor administrative policies, weak administrator passwords, or other exploitable soft- |
|
ware on the server. Problems can even occur because of hardware maintenance; in fact, dozens of |
|
companies have reported selling or discarding old server hard drives without properly erasing the |
|
sensitive customer data they contained. Finally, many organizations have a privacy policy that |
|
explicitly pledges to keep customer information confidential and encrypted at all times. If a security |
|
breach occurs and the company is forced to notify users that their data is at risk because it wasn’t |
|
properly encrypted, the company can face significant embarrassment and loss of trust. To avoid |
|
these problems and ensure that data is safe, you need to encrypt sensitive information stored by |
|
your application. |
839 |

840 C H A P T E R 2 5 ■ C RY P TO G R A P H Y
The .NET Cryptography Namespace
In the System.Security.Cryptography namespace, you can find the necessary classes for encrypting and decrypting information in your application. Furthermore, you find all the fundamental classes for creating different types of hashes. If you then reference the additional assembly System.Security.dll, you even have access to more advanced security functionality such as an API for modifying Windows ACLs (the System.Security.AccessControl namespace), the DPAPI, and classes for creating key-hashed message authentication codes (HMAC). Table 25-1 shows the categories of classes.
Table 25-1. Categories of Security Classes in the System.Security.Cryptography Namespace
Category |
Description |
Encryption algorithms |
The namespace includes the most important hashing and |
|
encryption algorithms and classes for creating digital |
|
signatures. You will learn more about the details of these |
|
classes in the section “Understanding the .NET Cryptography |
|
Classes.” |
Helper classes |
If you need to create true cryptographic random numbers, |
|
you will find helper classes in the |
|
System.Security.Cryptography namespace. The helper classes |
|
are for interacting with the underlying Windows cryptography |
|
system (the CryptoAPI). |
X509 certificates |
In the namespace System.Security.Cryptography.X509- |
|
Certificates, you will find all the necessary classes for working |
|
with X509 certificates and (since .NET 2.0) classes for |
|
accessing the Windows certificate store. |
XML signature and encryption |
You can find complete support of the XML signature and |
|
encryption standards in the System.Security.Cryptography.Xml |
|
namespace. The classes in this namespace are used for |
|
encrypting and signing XML documents according to the |
|
standards published by the W3C. |
CMS/PKCS#7 |
Since .NET 2.0, the framework has managed support for |
|
creating CMS/PKCS-enveloped messages directly without |
|
unmanaged calls. (CMS stands for Cryptographic Message |
|
Syntax and PKCS stands for Public-Key Cryptography |
|
Standard.) |
|
|
In the world of the Web, X509 certificates play an important role. They establish SSL communications and perform certificate authentication. An X509 certificate is a binary standard for encapsulating keys for asymmetric encryption algorithms together with a signature of a special organization that has issued the certificate (usually such organizations are called certificate authorities).
For simple SSL connections, you don’t need access to the certificate store, but if you want to call web services or web applications in your code hosted on a different server that requires you to authenticate with an X509 certificate, your application has to read the certificate from the Windows certificate store and then add the certificate to the web request (or the web service proxy) before actually sending the request. You can read a certificate from the store and assign it to a web request as follows:

C H A P T E R 2 5 ■ C RY P TO G R A P H Y |
841 |
X509Certificate2 Certificate = null;
// Read the certificate from the store
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); store.Open(OpenFlags.ReadOnly);
try
{
//Try to find the certificate
//based on its common name X509Certificate2Collection Results =
store.Certificates.Find(
X509FindType.FindBySubjectDistinguishedName, "CN=Mario, CN=Szpuszta", false);
if (Results.Count == 0)
throw new Exception("Unable to find certificate!");
else
Certificate = Results[0];
}
finally
{
store.Close();
}
Windows supports several types of certificate stores that are called store locations. The local machine store, for example, is accessible to all applications running on the local machine with the appropriate permissions. You can create a separate store for each Windows service of a machine, and every user has a separate certificate store. Certificates are stored securely in those stores. While the local machine store is encrypted with a key managed by the local security authority of the machine, the user store is encrypted with a key stored in the user’s profile. Within a store location, Windows differentiates between stores used for different purposes. The most important stores are the Personal (“my”) store and the Trusted Root Certification Authorities. Usually, the “my” store contains all the certificates used by applications (and users if it’s a user store), while the Trusted Root Certification Authorities store contains certificates of authorities issuing certificates. VeriSign is an example of a well-known authority from which you can buy certificates. If you place a certifi-
cate into the Trusted Root Certification Authorities store, you indicate that any certificates issued by this authority are trusted by the system and therefore can be used by any application without any fear. Other certificates by default are not trusted and therefore marked with a special flag. Of course, you should use only valid certificates issued by a trusted authority for critical operations such as authenticating or setting up SSL on the server, because any other certificate could lead to a potential security risk.
In ASP.NET web applications, you have to use either the local machine store or a service account’s store (which is nothing more than the user store of the service account under which a Windows service is executed). Therefore, the code introduced previously opens the store with the flag StoreLocation.LocalMachine. The second possible flag for this option is StoreLocation.CurrentUser, which opens a current user’s or service account’s store. As the certificate is a “usage” certificate, you will read it from the personal store. You can view the certificates of a store by opening a Microsoft Management Console and then adding the Certificates snap-in, as shown in Figure 25-1.

842 C H A P T E R 2 5 ■ C RY P TO G R A P H Y
Figure 25-1. The Windows Certificates snap-in
You can create test certificates through the makecert.exe command. This command creates a certificate in the personal store of the local machine:
makecert -ss my -sr LocalMachine -n "CN=Mario, CN=Szpuszta"
You have to include the System.Security.Cryptography.X509Certificates for using the classes shown previously. As soon as you have the certificate from the store in place, you can use it when sending requests through SSL to a server that requires certificate authentication, as follows:
// Now create the web request
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(url); Request.ClientCertificates.Add(Certificate);
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse(); // ...
Another useful example of security is a class for generating cryptographically strong random numbers. This class is important for generating random key values or salt values when you want to store salted password hashes. A salted password hash is a hash created from a password and a socalled salt. A salt is a random value. This ensures that even if two users select the same passwords,

C H A P T E R 2 5 ■ C RY P TO G R A P H Y |
843 |
the results stored in the back-end store will look different, as the random salt value is hashed with the password. It also requires you to store the salt value in a separate field together with the password, because you will need it for password validation. You will learn more about salted hash values when creating a custom Membership provider in Chapter 26. For now, this shows how you can create random number values with the System.Security.Cryptography.RandomNumberGenerator class:
byte[] RandomValue = new byte[16];
RandomNumberGenerator RndGen = RandomNumberGenerator.Create(); RndGen.GetBytes(RandomValue);
ResultLabel.Text = Convert.ToBase64String(RandomValue);
For more information about the random number generator, refer to the Cryptographic Service Provider documentation of Windows, as this class is just a wrapper around the native implementation (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/seccrypto/security/ cryptographic_service_providers.asp).
Understanding the .NET Cryptography Classes
Before you can perform cryptography in .NET, you need to understand a little more about the underlying plumbing. The .NET encryption classes are divided into three layers. The first layer is a set of abstract base classes; these classes represent an encryption task. These include the following:
•AsymmetricAlgorithm: This class represents asymmetric encryption, which uses a public/ private key pair. Data encrypted with one key can be decrypted only with the other key.
•SymmetricAlgorithm: This class represents symmetric encryption, which uses a shared secret value. Data encrypted with the key can be decrypted using only the same key.
•HashAlgorithm: This class represents hash generation and verification. Hashes are also known as one-way encryption algorithms, as you can only encrypt but not decrypt data. You can use hashes to ensure that data is not tampered with.
The second level includes classes that represent a specific encryption algorithm. They derive from the encryption base classes, but they are also abstract classes. For example, the DES algorithm class, which represents the DES (Data Encryption Standard) algorithm, derives from SymmetricAlgorithm.
The third level of classes is a set of encryption implementations. Each implementation class derives from an algorithm class. This means a specific encryption algorithm such as DES could have multiple implementation classes. While some .NET Framework encryption classes are implemented entirely in managed code, most are actually thin wrappers over the CryptoAPI library. The classes that wrap the CryptoAPI functions have CryptoServiceProvider in their name (for example, DESCryptoServiceProvider), while the managed classes typically have Managed in their name (for example, RijndaelManaged). Essentially, the managed classes perform all their work in the .NET world under the supervision of the CLR, while the unmanaged classes use calls to the unmanaged CryptoAPI library. This might seem like a limitation, but it’s actually an efficient reuse of existing technology.
The CryptoAPI has never been faulted for its technology, just its awkward programming interface. Figure 25-2 shows the classes in the System.Security.Cryptography namespace. This three-layer organization allows almost unlimited extensibility. You can create a new implementation for an existing cryptography class by deriving from an existing algorithm class. For example, you could create a class that implements the DES algorithm entirely in managed code by creating a new DESManaged class and inheriting from DESCryptoServiceProvider. Similarly, you can add

844C H A P T E R 2 5 ■ C RY P TO G R A P H Y
support for a new encryption algorithm by adding an abstract algorithm class (for example, CAST128, which is similar to DES but is not provided in the framework) and a concrete implementation class (such as CAST128Managed).
Figure 25-2. The cryptographic class hierarchy
■Note The encryption classes are one of the few examples in the .NET class library where the standard naming and case rules are not followed. For example, you’ll find classes such as TripleDES and RSA rather than TripleDes and Rsa.
Symmetric Encryption Algorithms
As mentioned earlier in this chapter, the .NET Framework supports three types of encryption: symmetric, asymmetric, and one-way encryption (hashes). Symmetric algorithms always use the same key for encryption and decryption. Symmetric algorithms are fast for encryption and decryption.
Table 25-2 lists the most important symmetric algorithms supported by the .NET Framework.
Table 25-2. Symmetric Algorithms Supported by .NET
|
Abstract |
|
|
Maximum |
|
Algorithm |
Default Implementation |
Valid Key Size |
Key Size |
DES |
DES |
DESCryptoServiceProvider |
64 |
64 |
TripleDES |
TripleDES |
TripleDESCryptoServiceProvider |
128, 192 |
192 |
RC2 |
RC2 |
RC2CryptServiceProvider |
40–128 |
128 |
Rijndael |
Rijndael |
RijndaelManaged |
128, 192, 256 |
256 |
|
|
|
|
|
The strength of the encryption corresponds to the length of the key. Keep in mind that the greater the key size, the harder it is for a brute-force attack to succeed, because there are far more possible key values to test. Of course, greater symmetric key sizes also lead to larger messages and

C H A P T E R 2 5 ■ C RY P TO G R A P H Y |
845 |
slower encryption times. For most purposes, a good standard choice is Rijndael. It offers solid performance and support for large key sizes.
■Note DES, TripleDES, and RC2 are all implemented using the CryptoAPI and thus need the high encryption pack on Windows 2000. Note also that the key length for DES and TripleDES include parity bits that don’t contribute to the strength of the encryption. TripleDES with a 192-bit key uses only 168 bits, while a 128-bit key uses 112 bits. In DES, the 64-bit key uses only 56 bits. For that reason, it’s considered fairly weak, and you should use other key algorithms instead. For additional information about the relative strengths of these algorithms, consult a dedicated book or Internet resource about encryption theory, such as Bruce Schneier’s Applied Cryptography: Protocols, Algorithms, and Source Code in C, Second Edition (Wiley, 1995).
As mentioned, the big advantage of symmetric algorithms is performance. Conversely, the major problems with symmetric algorithms are as follows:
Key exchange: If you are using symmetric algorithms to exchange data between two applications hosted by different parties, you have to exchange the key in a secure way.
Brute-force attacks: If you use the symmetric key for a longer period of time, attackers might have enough time to decrypt traffic by just trying any valid combination of bits in a key. Therefore, with an increasing bit size, the strength of the key increases, as explained previously. But generally this means you should use a different key in regular intervals anyway.
Long-term key management: If you have to update keys in regular intervals, you have to exchange them in regular intervals, which might lead to additional security risks. Furthermore, you have to store the key in a secure place.
Symmetric algorithms are not enough for secure systems, and that’s why asymmetric algorithms exist.
Asymmetric Encryption
Asymmetric algorithms try to solve some of the problems of symmetric algorithms. They are based on mathematical methods that require different keys for encryption and decryption. Usually the key used for encryption is called a public key. You can give this key to anyone who wants to send encrypted information to you. On the other hand, the private key is the only key that can be used for decryption. Therefore, if you are the only one with access to the private key, you are the only person who is able to decrypt the information. This fact makes key exchange between parties definitely easier, because you don’t need to transmit the key that can decrypt sensitive data. Table 25-3 lists the asymmetric algorithms supported by the .NET Framework.
Table 25-3. Asymmetric Algorithms Supported by .NET
|
Abstract |
|
|
Maximum |
|
Algorithm |
Default Implementation |
Valid Key Size |
Key Size |
RSA |
RSA |
RSACryptoServiceProvider |
384-16384 (8 bit increments) |
1024 |
DSA |
DSA |
DSACryptoServiceProvider |
512-1024 (64 bit increments) |
1024 |
|
|
|
|
|
When you use RSA and DSA, you will recognize that only RSA supports the direct encryption and decryption of values. The DSA algorithm—as its name Digital Signature Algorithm implies— can be used only for signing information and verifying signatures.


C H A P T E R 2 5 ■ C RY P TO G R A P H Y |
847 |
The ICryptoTransform Interface
.NET uses a stream-based architecture for encryption and decryption, which makes it easy to encrypt and decrypt different types of data from different types of sources. This architecture also makes it easy to perform multiple cryptographic operations in succession, on the fly, independent of the low-level details of the actual cryptography algorithm you’re using (such as the block size).
To understand how all this works, you need to consider the core types—the ICryptoTransform interface and the CryptoStream class. The ICryptoTransform interface represents blockwise cryptographic transformation. This could be an encryption, decryption, hashing, Base64 encoding/ decoding, or formatting operation. To create an ICryptoTransform object for a given algorithm, you use the CreateEncryptor() and CreateDecryptor() methods (depending on whether you want to encrypt or decrypt data).
Here’s a code snippet that creates an ICryptoTransform for encrypting with the DES algorithm:
DES crypt = DES.Create();
ICryptoTransform transform = crypt.CreateEncryptor();
Various cryptographic tasks execute in the same way, even though the actual cryptographic function performing the transformation may be different. Every cryptographic operation requires that data be subdivided into blocks of a fixed size before it can be processed. You can use an ICryptoTransform instance directly, but in most cases you’ll take an easier approach and simply pass it to another class: the CryptoStream.
The CryptoStream Class
The CryptoStream wraps an ordinary stream and uses an ICryptoTransform to perform its work behind the scenes. The key advantage is that the CryptoStream uses buffered access, thereby allowing you to perform automatic encryption without worrying about the block size required by the algorithm. The other advantage of the CryptoStream is that, because it wraps an ordinary .NET stream-derived class, it can easily “piggyback” on another operation, such as file access (through
a FileStream), memory access (through a MemoryStream), a low-level network call (through a NetworkStream), and so on.
To create a CryptoStream, you need three pieces of information: the underlying stream, the mode, and the ICryptoTransform you want to use. For example, the following code snippet creates an ICryptoTransform using the DES algorithm implementation class and then uses it with an existing stream to create a CryptoStream:
DES crypt = DES.Create();
ICryptoTransform transform = crypt.CreateEncryptor();
CryptoStream cs = new CryptoStream(fileStream, transform,
CryptoStreamMode.Write);
// (Now you can use cs to write encrypted information to the file.)
Note that the CryptoStream can be in one of two modes: read mode or write mode, as defined by the CryptoStreamMode enumeration. In read mode, the transformation is performed as it is retrieved from the underlying stream (as shown in Figure 25-3).