Using the Cryptography APIs in .NETby Wei-Meng Lee
The .NET framework contains a number of cryptography services that allow you to incorporate security services into your .NET applications. These libraries are located under the
System.Security.Cryptography namespace and provide various functions such as encryption and decryption of data, as well as other operations such as hashing and random-number generation. In this article, I will show you how to use some of the common security APIs to make your .NET applications more secure.
The first, most common security function that you will perform is hashing. Consider the situation where you need to build a function to authenticate users before they can use your application. You would require the user to supply a login credential, most commonly containing a user name and a password. This login information would need to be persisted to a database. And it is not uncommon for developers to store the passwords of users directly on a database. This is a big security risk, because hackers who had a chance to glance at the users' database would be able to obtain the passwords of your users. A better approach is to store the hash values of the users' passwords, instead of the users' passwords themselves. A hashing algorithm has the following properties:
- It maps a string of an arbitrary length to small binary values of a fixed length, known as a hash value.
- The hash value of a string is unique, and small changes in the original string will produce a different hash value.
- It is improbable to find two different strings that produce the same hash value.
- It is impossible to use the hash value to find the original string.
Instead of storing the passwords of your users verbatim in the database, you should store their hash values. When the user logs in to your application, the password provided is compared with the hash values stored in the database. In this way, even if hackers actually stole the user's database, it does not expose the actual password. One downside to storing the hash values of users' passwords is that in the event that a user loses his password, there is no way of retrieving it. In this case, you'd need to generate a new password for the user and request that he change it immediately. But this inconvenience is a small price to pay for the security of your application.
There are many hashing algorithms available in .NET, but the most commonly used are the SHA1 and MD5 implementations. Let's take a look at how they work in .NET.
Using Visual Studio 2005, create a new Console application project using Visual Basic. Import the following namespaces:
Imports System.Text.Encoding Imports System.Security.Cryptography Imports System.IO Define the following subroutine: Private Sub Hashing_SHA1() '---ask the user to enter a password--- Console.Write("Please enter a password: ") Dim password As String = Console.ReadLine() '---hash the password--- Dim data() As Byte = ASCII.GetBytes(password) Dim passwordHash() As Byte Dim sha As New SHA1CryptoServiceProvider() passwordHash = sha.ComputeHash(data) '---ask the user to enter the same password again--- Console.Write("Please enter password again: ") password = Console.ReadLine() '---hash the second password and compare it with the first--- data = System.Text.Encoding.ASCII.GetBytes(password) If ASCII.GetString(passwordHash) = _ ASCII.GetString(sha.ComputeHash(data)) Then Console.WriteLine("Same password") Else Console.WriteLine("Incorrect password") End If End Sub
In this subroutine, you first ask the user to enter a password, after which you will hash it using the SHA1 implementation. You then ask the user to enter the same password again. To verify whether the second password matches the first, you hash the second password and then compare the two hash values. Note that for the SHA1 implementation, the hash value generated is 160 bits in length (the byte array
passwordHash has 20 members [8 bits x 20 = 160 bits]). In my example, I converted the hash values into strings and performed a comparison. You could also convert them to Base64 encoding and then perform a comparison. Alternatively, you can also compare the two hash values using their byte arrays, comparing byte by byte. As soon as one byte is different, you can conclude that the two hash values are not the same.
To test the subroutine, simply call the
Hashing_SHA1() subroutine in
Sub Main() Hashing_SHA1() Console.Read() End Sub
Figure 1 shows the subroutine in action.
Figure 1. Using Hashing for authentication
You can also use the MD5 implementation to perform hashing, as the following subroutine shows:
Private Sub Hashing_MD5() '---ask the user to enter a password--- Console.Write("Please enter a password: ") Dim password As String = Console.ReadLine() '---hash the password--- Dim data() As Byte = ASCII.GetBytes(password) Dim passwordHash() As Byte Dim md5 As New MD5CryptoServiceProvider() passwordHash = md5.ComputeHash(data) '---ask the user to enter the same password again--- Console.Write("Please enter password again: ") password = Console.ReadLine() '---hash the second password and compare it with the first--- data = ASCII.GetBytes(password) If ASCII.GetString(passwordHash) = _ ASCII.GetString(md5.ComputeHash(data)) Then Console.WriteLine("Same password") Else Console.WriteLine("Incorrect password") End If End Sub
The main difference is that the hash value for MD5 is 128 bits in length.