Cryptography Basics


Rather than present a general exposition of cryptography, this section is meant to familiarize you with basic techniques required to deal with .NET security and protecting your Web Services through encryption. The three building blocks you need are hashing algorithms, secret key encryption, and an understanding of the Public Key Cryptographic System (PKCS).

Hashing algorithms digest long sequences of data into short footprints, the most popular being 64-bit hash keys. The two most popular hashing algorithms are SHA (Secure Hash Algorithm) and MD5 (Message-Digest algorithm 5). These hash keys are used for signing digital documents; in other words, the hash is generated and encrypted using a private key.

Secret key encryption is commonly used to protect data through passwords and pass phrases (long phrases that would be difficult to guess). Secret key encryption is suitable for situations where the encrypted data needs to be accessed by the same person who protected it.

Public Key Cryptography is most widely used in protecting the data through encryption. It is also used for digital signatures. Public Key Cryptography is based on asymmetric keys, which means you always have a pair of keys. One is known to all and is called the public key. The other key of the pair is kept secret and is known only to the owner. This is called the private key. If you use the public key to encrypt data, it can only be decrypted using the corresponding private key of the key pair, and vice versa.

The public key is known to all, so anyone can decrypt the information. However, the private key is known only to the owner, so this process acts as a digital signature. In other words, if the public key decrypts the message, then you know that the sender was the owner of the private key. As suggested earlier, rather than encrypt the whole document using the private key, a hash algorithm is used to digest the data into a compact form, which is then encrypted using the private key. The result of this process is called the digital signature of the digital document.

If the data is encrypted using the public key, then it can then only be decrypted by the corresponding private key, which means that only the owner of the private key will be able to read the unencrypted data. This can be used for encryption purposes. The cryptographic namespace of the .NET Framework is System.Security.Cryptography.

Hash Algorithms

Hash algorithms are also called one-way functions because of their mathematical property of nonre-versibility. The hash algorithms reduce large binary strings into a fixed-length binary byte array. This fixed-length binary array is used for computing digital signatures, as explained earlier.

To verify a piece of information, the hash is recomputed and compared against a previously computed hash value. If both values match, the data has not been altered. The cryptographic hashing algorithms map a large stream of binary data to a much shorter fixed length, so it is theoretically possible for two different documents to have the same hash key.

Although, in theory, it is possible for two documents to have the same MD5 hash key and a different check sum, it is computationally impossible to create a forged document having the same hash key as the original hash value. Take the case of a virus attack on an executable code. In the late 1980s, as a protective measure against accidental or malicious damage to the code integrity, the most sophisticated technique was to create a check sum or a CRC (cyclic redundancy check).

Tip 

Virus makers drew cunning designs to create viruses that added padding code to the victim’s files so that the check sum and CRC remained unchanged in spite of the infection. However, using MD5 hash values, this kind of stealth attack is rendered unfeasible.

Windows Meta Files (WMF) still use check sums in the file header. For example, the .NET Framework class System.Drawing.Imaging.WmfPlaceableFileHeader has a read/write property of type short called Checksum. However, due to ease of computation, this check sum is used as a cheap mode of protection against accidental damage, rather than against malicious attacks.

Here is a simple program to calculate a check sum:

  ' Cryptography/Checksum.vb Imports System Imports System.IO Module Module1 

This is the entry point for the program. Here, you check to see whether you’ve received the correct argument from the command line to run the program, and stop the program if you haven’t:

  Public Sub Main(ByVal CmdArgs() As String)     If (CmdArgs.Length <> 1) Then         Console.WriteLine("Usage: Checksum <filename>")         End     End If 

First, you open the file for which the check sum is to be computed:

  Dim fs As FileStream = File.OpenRead(CmdArgs(0)) 

You then compute the check sum, close the file, and then output the result to the screen:

      Dim sum As Short = compute(fs)     fs.Close()     Console.WriteLine(sum) End Sub 

The following method computes the check sum:

      Function compute(ByVal strm As Stream)         Dim sum As Long = 0         Dim by As Integer         strm.Position = 0         by = strm.ReadByte         While (by <> -1)             sum = (((by Mod &HFF) + sum) Mod &HFFFF)             by = strm.ReadByte         End While         Return CType((sum Mod &HFFFF), Short)     End Function End Module 

Compile this program with the following in the VB compiler (or build and run your application if you are using Visual Studio:

 vbc Checksum.vb

Run it with the following:

 Checksum <filename>

Due to their unsafe nature, check sums and CRCs are sometimes deemed poor cousins of cryptographic hash algorithms. Next you will look into classes provided by the .NET Framework to cater to cryptographic-grade algorithms.

Cryptographic Hash Algorithms

The abstract class System.Security.Cryptography.HashAlgorithm represents the concept of cryp-tographic hash algorithms within the .NET Framework. The framework provides eight classes that extend the HashAlgorithm abstract class:

  • MD5CryptoServiceProvider (extends abstract class MD5)

  • RIPEMD160Managed (extends abstract class RIPEMD160)

  • SHA1CryptoServiceProvider (extends abstract class SHA1)

  • SHA256Managed (extends abstract class SHA256)

  • SHA384Managed (extends abstract class SHA384)

  • SHA512Managed (extends abstract class SHA512)

  • HMACSHA1 (extends abstract class KeyedHashAlgorithm)

  • MACTripleDES (extends abstract class KeyedHashAlgorithm)

The last two classes belong to a class of algorithm called keyed hash algorithms. The keyed hashes extend the concept of the cryptographic hash with the use of a shared secret key. This is used for computing the hash of data transported over an unsecured channel.

The following is an example of computing a hash value of a file:

  ' Cryptography/TestKeyHash.vb Imports System Imports System.IO Imports System.Security.Cryptography Imports System.Text Imports System.Runtime.Serialization.Formatters Module Module1     Public Sub Main(ByVal CmdArgs() As String)         If (CmdArgs.Length <> 1) Then             Console.WriteLine("Usage: TestKeyHash <filename>")             End         End If 

The following snippet creates the object instance of the .NET SDK Framework class with a salt (a random secret to confuse a snooper):

  Dim key() As Byte = Encoding.ASCII.GetBytes("My Secret Key".ToCharArray()) Dim hmac As HMACSHA1 = New HMACSHA1(key) Dim fs As FileStream = File.OpenRead(CmdArgs(0)) 

The next four lines compute the hash, convert the binary hash into a printable Base64 format, close the file, and then print the Base64 encoded string as the result of hashing to the screen:

          Dim hash() As Byte = hmac.ComputeHash(fs)         Dim b64 As String = Convert.ToBase64String(hash)         fs.Close()         Console.WriteLine(b64)     End Sub End Module 

The code can be compiled at the command line using the following:

 vbc TestKeyHash.vb

To execute the code, use the following command at the console prompt:

 TestKeyHash TestKeyHash.vb

This should produce a hashed output:

 IOEj/D0rOxjEqCD8qHoYm+yWw6I=

The previous example uses an instance of the HMACSHA1 class. The output displayed is a Base64 encoding of the binary hash result value. Base64 encoding is widely used in MIME and XML file formats to represent binary data. To recover the binary data from a Base64-encoded string, you could use the following code fragment:

  Dim orig() As Byte = Convert.FromBase64String(b64) 

The XML parser, however, does this automatically, as you will see in later examples.

SHA

Secure Hash Algorithm (SHA) is a block cipher that operates on a block size of 64 bits. However, the subsequent enhancements of this algorithm have bigger key values, thus increasing the value range and therefore enhancing the cryptographic utility. Note that the bigger the key value sizes, the longer it takes to compute the hash. Moreover, for relatively smaller data files, smaller hash values are more secure. To put it another way, the hash algorithm’s block size should be less than or equal to the size of the data itself.

The hash size for the SHA1 algorithm is 160 bits. The following code shows how to use it, and is similar to the HMACSHA1 code discussed previously:

 ' Cryptography/TestSHA1.vb Imports System Imports System.IO Imports System.Security.Cryptography Imports System.Text Imports System.Runtime.Serialization.Formatters Module Module1     Public Sub Main(ByVal CmdArgs() As String)         If (CmdArgs.Length <> 1) Then              Console.WriteLine("Usage: TestSHA1 <filename>")             End         End If         Dim fs As FileStream = File.OpenRead(CmdArgs(0))         Dim sha As SHA1 = New SHA1CryptoServiceProvider()         Dim hash() As Byte = sha.ComputeHash(fs)         Dim b64 As String = Convert.ToBase64String(hash)         fs.Close()         Console.WriteLine(b64)     End Sub End Module

The .NET Framework provides bigger key size algorithms as well - namely, SHA256, SHA384, and SHA512. The numbers at the end of the name indicate the block size.

The class SHA256Managed extends the abstract class SHA256, which in turn extends the abstract class HashAlgorithm. The Forms Authentication module of ASP.NET security (System.Web.Security.FormsAuthenticationModule) uses SHA1 as one of its valid formats to store and compare user passwords.

MD5

MD5 stands for Message-Digest algorithm 5. It is a cryptographic, one-way hash algorithm. The MD5 algorithm competes well with SHA. MD5 is an improved version of MD4, devised by Ronald Rivest of RSA fame. In fact, FIPS PUB 180-1 states that SHA-1 is based on similar principles to MD4. The salient features of this class of algorithms are as follows:

  • It is computationally unfeasible to forge an MD5 hash digest.

  • MD5 is not based on any mathematical assumption such as the difficulty of factoring large binary integers.

  • MD5 is computationally cheap, and therefore suitable for low latency requirements.

  • It is relatively simple to implement.

MD5 is the de facto standard for hash digest computation, due to the popularity of RSA. The .NET Framework provides an implementation of this algorithm through the class MD5CryptoServiceProvider in the System.Security.Cryptography namespace. This class extends the MD5 abstract class, which in turn extends the abstract class HashAlgorithm. This class shares a common base class with SHA1, so the examples previously discussed can be modified easily to accommodate it:

 Dim fs As FileStream = File.OpenRead(CmdArgs(0)) Dim md5 As MD5 = New MD5CryptoServiceProvider() Dim hash() As Byte = md5.ComputeHash(fs) Dim b64 As String = Convert.ToBase64String(hash) fs.Close() Console.WriteLine(b64)

RIPEMD-160

Based on MD5, RIPEMD-160 started as a project in Europe called RIPE (RACE Integrity Primitives Evaluation Message Digest) in 1996. By 1997, the design of RIPEMD-160 was finalized. RIPEMD-160 is a 160-bit hash algorithm and is meant to be a replacement for MD4 and MD5.

The .NET Framework 2.0 introduced the RIPEMD160 class to work with this iteration of encryption techniques. The following code demonstrates the use of this class:

 Dim fs As FileStream = File.OpenRead(CmdArgs(0)) Dim myRIPEMD As New RIPEMD160Managed() Dim hash() As Byte = myRIPEMD.ComputeHash(fs) Dim b64 As String = Convert.ToBase64String(hash) fs.Close() Console.WriteLine(b64)

Secret Key Encryption

Secret key encryption is widely used to encrypt data files using passwords. The simplest technique is to seed a random number using a password, and then encrypt the files with an XOR operation using this random number generator.

The .NET Framework represents the secret key by an abstract base class SymmetricAlgorithm. Four concrete implementations of different secret key algorithms are provided by default:

  • DESCryptoServiceProvider (extends abstract class DES)

  • RC2CryptoServiceProvider (extends abstract class RC2)

  • RijndaelManaged (extends abstract class Rijndael)

  • TripleDESCryptoServiceProvider (extends abstract class TripleDES)

Let’s explore the SymmetricAlgorithm design. As indicated by the following example code, two separate methods are provided to access encryption and decryption. Here is a console application program that encrypts and decrypts a file, given a secret key:

  ' Cryptography/SymEnc.vb Imports System.Security.Cryptography Imports System.IO Imports System.Text Imports System Module Module1    Public Sub Main(ByVal CmdArgs() As String)        If (CmdArgs.Length <> 4) Then            UsageAndExit()        End If 

The following computes the index of the algorithm that we’ll use:

  Dim algoIndex As Integer = CmdArgs(0) If (algoIndex < 0 Or algoIndex >= algo.Length) Then    UsageAndExit() End If 

We open the input and output files (the filename represented by CmdArgs(3) is the output file, and CmdArgs(2) is the input file):

  Dim fin As FileStream = File.OpenRead(CmdArgs(2)) Dim fout As FileStream = File.OpenWrite(CmdArgs(3)) 

We create the symmetric algorithm instance using the .NET Framework class SymmetricAlgorithm. This uses the algorithm name indexed by the CmdArgs(0) parameter. After this, we set the key parameters and display them onscreen for information:

  Dim sa As SymmetricAlgorithm = _     SymmetricAlgorithm.Create(algo(algoIndex)) sa.IV = Convert.FromBase64String(b64IVs(algoIndex)) sa.Key = Convert.FromBase64String(b64Keys(algoIndex)) Console.WriteLine("Key " + CType(sa.Key.Length, String)) Console.WriteLine("IV " + CType(sa.IV.Length, String)) Console.WriteLine("KeySize: " + CType(sa.KeySize, String)) Console.WriteLine("BlockSize: " + CType(sa.BlockSize, String)) Console.WriteLine("Padding: " + CType(sa.Padding, String)) 

At this point, we check which operation is required, and execute the appropriate static method:

      If (CmdArgs(1).ToUpper().StartsWith("E")) Then         Encrypt(sa, fin, fout)     Else         Decrypt(sa, fin, fout)     End If End Sub 

Here is where the encryption itself takes place:

  Public Sub Encrypt(ByVal sa As SymmetricAlgorithm, _                    ByVal fin As Stream, _                    ByVal fout As Stream)     Dim trans As ICryptoTransform = sa.CreateEncryptor()     Dim buf() As Byte = New Byte(2048) {}     Dim cs As CryptoStream = _         New CryptoStream(fout, trans, CryptoStreamMode.Write)     Dim Len As Integer     fin.Position = 0     Len = fin.Read(buf, 0, buf.Length)     While (Len > 0)         cs.Write(buf, 0, Len)         Len = fin.Read(buf, 0, buf.Length)     End While     cs.Close()     fin.Close() End Sub  

Here’s the decryption method:

  Public Sub Decrypt(ByVal sa As SymmetricAlgorithm, _                    ByVal fin As Stream, _                    ByVal fout As Stream)     Dim trans As ICryptoTransform = sa.CreateDecryptor()     Dim buf() As Byte = New Byte(2048) {}     Dim cs As CryptoStream = _         New CryptoStream(fin, trans, CryptoStreamMode.Read)     Dim Len As Integer     Len = cs.Read(buf, 0, buf.Length)     While (Len > 0)         fout.Write(buf, 0, Len)         Len = cs.Read(buf, 0, buf.Length)     End While     fin.Close()     fout.Close() End Sub 

This next method prints usage information:

  Public Sub UsageAndExit()     Console.Write("Usage SymEnc <algo index> <D|E> <in> <out> ")     Console.WriteLine("D =decrypt, E=Encrypt")     For i As Integer = 0 To (algo.Length - 1)         Console.WriteLine("Algo index: {0} {1}", i, algo(i))     Next i     End End Sub 

The static parameters used for object creation are indexed by CmdArgs(0). How you arrive at these magic numbers is explained shortly:

  Dim algo() As String = {"DES", "RC2", "Rijndael", "TripleDES"} Dim b64Keys() As String = {"YE32PGCJ/g0=", _         "vct+rJ09WuUcR61yfxniTQ==", _         "PHDPqfwE3z25f2UYjwwfwg4XSqxvl8WYmy+2h8t6AUg=", _         "Q1/lWoraddTH3IXAQUJGDSYDQcYYuOpm"}     Dim b64IVs() As String = {"onQX8hdHeWQ=", _         "jgetiyz+pIc=", _         "pd5mgMMfDI2Gxm/SKl5I8A==", _         "6jpFrUh8FF4="} End Module 

After compilation, this program can encrypt and decrypt using all four of the symmetric key implementations provided by the .NET Framework. The secret keys and their initialization vectors (IVs) have been generated by a simple source code generator, examined shortly.

The following commands encrypt and decrypt files using the DES algorithm. The first command takes a text file, 1.txt, and uses the DES algorithm to create an encrypted file called 2.bin. The next command decrypts this file and stores it in 3.bin:

 SymEnc 0 E 1.txt 2.bin SymEnc 0 D 2.bin 3.bin

The first parameter of the SymEnc program is an index to the string array, which determines the algorithm to be used:

 Dim algo() As String = {"DES", "RC2", "Rijndael", "TripleDES"}

The string defining the algorithm is passed as a parameter to the static Create() method of the abstract class SymmetricAlgorithm. This class has an abstract factory design pattern:

 Dim sa As SymmetricAlgorithm = SymmetricAlgorithm.Create(algo(algoIndex))

To encrypt, we get an instance of the ICryptoTransform interface by calling the CreateEncryptor() method of the SymmetricAlgorithm class extender:

 Dim trans As ICryptoTransform = sa.CreateEncryptor()

Similarly, for decryption, we get an instance of the ICryptoTransform interface by calling the CreateDecryptor() method of the SymmetricAlgorithm class instance:

 Dim trans As ICryptoTransform = sa.CreateDecryptor()

We use the class CryptoStream for both encryption and decryption, but the parameters to the constructor differ. Encryption uses the following code:

 Dim cs As CryptoStream = New CryptoStream(fout, trans, CryptoStreamMode.Write)

Similarly, decryption uses this code:

 Dim cs As CryptoStream = New CryptoStream(fin, trans, CryptoStreamMode.Read)

We call the Read() and Write() methods of the CryptoStream for decryption and encryption, respectively. For generating the keys, we use a simple code generator, as follows:

  ' Cryptography/SymKey.vb Imports System.Security.Cryptography Imports System.Text Imports System.IO Imports System Imports Microsoft.VisualBasic.ControlChars Module Module1     Public Sub Main(ByVal CmdArgs() As String)         Dim keyz As StringBuilder = New StringBuilder         Dim ivz As StringBuilder = New StringBuilder         keyz.Append("Dim b64Keys() As String = { _" + VbCrLf)         ivz.Append(VbCrLf + "Dim b64IVs() As String = { _" + VbCrLf ) 

The algorithm names for symmetric keys used by .NET SDK are given the correct index values here:

 Dim algo() As String = {"DES", "RC2", "Rijndael", "TripleDES"}

For each of the algorithms, we generate the keys and IV:

  Dim comma As String = ", _" + VbCrLf For i As Integer = 0 To 3    Dim sa As SymmetricAlgorithm = SymmetricAlgorithm.Create(algo(i))        sa.GenerateIV()    sa.GenerateKey()    Dim Key As String    Dim IV As String    Key = Convert.ToBase64String(sa.Key)    IV = Convert.ToBase64String(sa.IV)    keyz.AppendFormat(tab + """" + Key + """" + comma)    ivz.AppendFormat(tab + """" + IV + """" + comma)    If i = 2 Then comma = " " Next i 

Here, we print or emit the source code:

          keyz.Append("}")         ivz.Append("}")         Console.WriteLine(keyz.ToString())         Console.WriteLine(ivz.ToString())     End Sub End Module 

The preceding program creates a random key and an initializing vector for each algorithm. This output can be inserted directly into the SymEnc.vb program. The simplest way to do this is to type the following:

 SymKey > keys.txt

This redirects the information into a file called keys.txt, which you can then use to cut and paste the values into your program. You use the StringBuilder class along with the control character crlf (carriage return and line feed) to format the text so that it can be inserted directly into your program. You then convert the binary data into Base64 encoding using the public instance method ToBase64String() of the class Convert. Kerberos, the popular network authentication protocol supported by Windows Server 2003, Windows 2000, and all of the UNIX flavors, uses secret key encryption for implementing security.

PKCS

The Public Key Cryptographic System is a type of asymmetric key encryption. This system uses two keys, one private and the other public. The public key is widely distributed, whereas the private key is kept secret. One cannot derive or deduce the private key by knowing the public key, so the public key can be safely distributed.

The keys are different, yet complementary. That is, if you encrypt data using the public key, then only the owner of the private key can decipher it, and vice versa. This forms the basis of PKCS encryption.

If the private key holder encrypts a piece of data using his or her private key, any person having access to the public key can decrypt it. The public key, as the name suggests, is available publicly. This property of the PKCS is exploited along with a hashing algorithm, such as SHA or MD5, to provide a verifiable digital signature process.

The abstract class System.Security.Cryptography.AsymmetricAlgorithm represents this concept in the .NET Framework. Two concrete implementations of this class are provided by default:

  • DSACryptoServiceProvider, which extends the abstract class DSA

  • RSACryptoServiceProvider, which extends the abstract class RSA

DSA (Digital Signature Algorithm) was specified by NIST (National Institute of Standards and Technology) in January 2000. The original DSA standard, however, was issued by NIST way back in August 1991. DSA cannot be used for encryption and is good only for digital signature. Digital signature is discussed in more detail in the next subsection.

RSA algorithms can also be used for encryption as well as digital signatures. RSA is the de facto standard and has much wider acceptance than DSA. RSA is a tiny bit faster than DSA as well.

The RSA algorithm is named after its three inventors: Rivest, Shamir, and Adleman. It was patented in the United States, but the patent expired in September 2000. RSA can be used for both digital signature and data encryption. It is based on the assumption that large numbers are extremely difficult to factor. The use of RSA for digital signatures is approved within the FIPS PUB 186-2 and is defined in the ANSI X9.31 standard document.

To gain some practical insights into RSA implementation of the .NET Framework, consider the following code (for this to compile, you also have to make a reference to the System.Security DLL in your project):

  ' Cryptography/TestRSAKey.vb Imports System.Security.Cryptography.Xml Module Module1     Sub Main()         Dim RSA As RSAKeyValue = New RSAKeyValue         Dim str As String = RSA.Key.ToXmlString(True)         System.Console.WriteLine(str)     End Sub End Module 

This code creates a pair of private and public keys and prints it out at the command line in XML format. To compile the preceding code, simply open a console session, run corvar.bat (if necessary), set the .NET SDK paths, and compile the program by typing the following command:

 TestRSAKey.vb

This should produce a file called TestRSAKey.exe. Execute this program and redirect the output to a file such as key.xml:

 TestRSAKey > key.xml

The file key.xml contains all the private and public members of the generated RSA key object. You can open this XML file in Internet Explorer 5.5 or later. If you do so, you will notice that the private member variables are also stored in this file. The binary data representing the large integers is encoded in Base64 format.

The program listed above uses an RSAKeyValue instance to generate a new key pair. The class RSAKeyValue is contained in the System.Security.Cryptography.Xml namespace. This namespace can be thought of as the XML face of the .NET cryptographic framework. It contains a specialized, lightweight implementation of XML for the purpose of cryptography, and the model allows XML objects to be signed with a digital signature.

The System.Security.Cryptography.Xml namespace classes depend upon the classes contained in the System.Security.Cryptography namespace for the actual implementation of cryptographic algorithms.

The key.xml file, generated by redirecting the output of the Visual Basic test program TestRSAKey, contains both private and public keys. However, you need to keep the private key secret while making the public key widely available. Therefore, you need to separate out the public key from the key pair. Here is the program to do it:

  ' Cryptography/TestGetPubKey.vb Imports System.Text Imports System.Security.Cryptography Imports System.IO Imports System Module Module1     Public Sub Main(ByVal CmdArgs() As String)         If (CmdArgs.Length <> 1) Then             Console.WriteLine("Usage: TestGetPubKey <key pair xml>")             End         End If         Dim xstr As String = File2String(CmdArgs(0)) 

The following code creates an instance of the RSA implementation and reinitializes the internal variables through the XML formatted string:

      Dim rsa As RSACryptoServiceProvider = New RSACryptoServiceProvider()     rsa.FromXmlString(xstr)     Dim x As String = rsa.ToXmlString(False)     Console.WriteLine(x) End Sub Public Function File2String(ByVal fname As String)     Dim finfo As FileInfo = New FileInfo(fname)     Dim buf() As Byte = New Byte(finfo.Length) {}     Dim fs As FileStream = File.OpenRead(fname)     fs.Read(buf, 0, buf.Length)         Return (New ASCIIEncoding).GetString(buf)     End Function End Module 

This program is logically similar to TestRSAKey.vb except that it has to read the key file and pass a different parameter in the ToXmlString() method.

The cryptography classes use a lightweight XML implementation, thus avoiding the elaborate ritual of parsing the fully formed generic XML data containing serialized objects. This has another advantage, speed, because it bypasses the DOM parsers. To compile the previous code, type the following:

 vbc /r:System.Security.dll TestGetPubKey.vb

This should produce the file TestGetPubKey.exe. Run this file, giving key.xml as the name of the input file, and redirect the program’s output to pub.xml. This file contains an XML formatted public key. The binary data, basically binary large integers, are Base64 encoded. You may recall that key.xml contains both the public and private key pairs, and was generated by redirecting the output of TestRSAKey.exe. The following line redirects key.xml’s public key to pub.xml:

 TestGetPubKey key.xml > pub.xml

The following program tests the encrypt and decrypt feature of the RSA algorithm:

  ' Cryptography/TestCrypt.vb Imports System Imports System.IO Imports System.Security.Cryptography Imports System.Text Module Module1     Public Sub Main(ByVal CmdArgs() As String)         If (CmdArgs.Length <> 4) Then             Console.WriteLine("Usage: TestCrypt <key xml> <E|D> <in> <out>")             Console.WriteLine(" E= Encrypt, D= Decrypt (needs private key)")             End         End If 

Here, you read the public or private key into memory:

  Dim xstr As String = File2String(CmdArgs(0)) 

You create an instance of an RSA cryptography service provider and initialize the parameters based on the XML lightweight filename passed in CmdArgs(0):

  Dim RSA As New RSACryptoServiceProvider() RSA.FromXmlString(xstr) 

Display the key filename:

  Console.WriteLine("Key File: " + CmdArgs(0)) Dim op As String= "Encrypted" 

Read the input file and store it into a byte array:

  Dim info As FileInfo = New FileInfo(CmdArgs(2)) Dim inbuflen As Integer = CType(info.Length, Integer) Dim inbuf() As Byte = New Byte(inbuflen-1) {} Dim outbuf() As Byte Dim fs As FileStream = File.OpenRead(CmdArgs(2)) fs.Read(inbuf, 0, inbuf.Length)  fs.Close() 

Either encrypt or decrypt depending on the CmdArgs(1) option:

  If (CmdArgs(1).ToUpper().StartsWith("D")) Then     op = "Decrypted"     outbuf = rsa.Decrypt(inbuf, False) Else     outbuf = rsa.Encrypt(inbuf, False) End If 

Now, write the result in the output buffer into the file and display the result:

      fs = File.OpenWrite(CmdArgs(3))     fs.Write(outbuf, 0, outbuf.Length)     fs.Close()          Console.WriteLine(op + " input [" + CmdArgs(2) + "] to output [" _                       + CmdArgs(3) + "]") End Sub 

Here’s a helper method to read the filename passed as an argument and convert the content to a string:

      Public Function File2String(ByVal fname As String)         Dim finfo As FileInfo = New FileInfo(fname)         Dim buf() As Byte = New Byte(finfo.Length) {}         Dim fs As FileStream = File.OpenRead(fname)                  fs.Read(buf, 0, buf.Length)         fs.Close()                  Return (New ASCIIEncoding).GetString(buf)     End Function End Module 

This test program encrypts or decrypts a short file depending on the parameters supplied to it. It takes four parameters: the XML formatted private or public key file, option E or D, representing the encrypt or decrypt options, respectively, and input and output filenames.

This program can be compiled with the following command:

 vbc /r:System.Security.dll TestCrypt.vb

The preceding command produces a PE file, TestCrypt.exe. To test the encrypt and decrypt functions, create a small plain-text file called 1.txt. Recall that we also created two other files, key.xml and pub.xml. The file key.xml contains a key pair, and pub.xml contains the public key extracted from the file key.xml.

To encrypt the plain-text file plain.txt, use the following command:

 TestCrypt pub.xml E 1.txt rsa.bin

Note that we have used the public key file to encrypt it. You can type the output on the console, but this won’t make any sense because it contains binary data. You could use a binary dump utility to dump out the file’s content. If you do this, note that the total number of bytes is 128, compared to the input of 13 bytes. This is because the RSA is a block cipher algorithm and the block size equals the key size, so the output is always in multiples of the block size. You may wish to rerun the preceding examples with larger files to see the resulting encrypted file length.

Now decrypt the file to get back the original text:

 TestCrypt key.xml D rsa.bin decr.txt

The key.xml file, which also contains the private key, is used to decrypt because you use the public key to encrypt, and the private key to decrypt. In other words, anyone may send encrypted documents to you if they know your public key, but only you can decrypt such messages. The reverse is true for digital signatures, covered in the next section.

Digital Signature Basics

Digital signature is the encryption of a hash digest (for example, MD5 or SHA-1) of data using a public key. The digital signature can be verified by decrypting the hash digest and comparing it against a hash digest computed from the data by the verifier.

As noted earlier, the private key is known only to the owner, so the owner can sign a digital document by encrypting the hash computed from the document. The public key is known to all, so anyone can verify the signature by recomputing the hash and comparing it against the decrypted value, using the public key of the signer.

The .NET Framework provides DSA and RSA digital signature implementations by default. This section considers only DSA, as RSA was covered in the preceding section. Both of the implementations extend the same base class, so all programs for DSA discussed next work for RSA as well.

First, go through the same motions of producing a key pair and a public key file and then sign and verify the signature:

  ' Cryptography/GenDSAKeys.vb Imports System Imports System.Security.Cryptography Imports VB_Security.FileUtil Module Module1     Public Sub Main(ByVal CmdArgs() As String)         Dim dsa As DSACryptoServiceProvider = New DSACryptoServiceProvider()         Dim prv As String = dsa.ToXmlString(True)         Dim pub As String = dsa.ToXmlString(False)         Dim fileutil As FileUtil = New FileUtil()                  fileutil.SaveString("dsa-key.xml", prv)         fileutil.SaveString("dsa-pub.xml", pub)                  Console.WriteLine("Created dsa-key.xml and dsa-pub.xml")     End Sub End Module 

This code generates two XML-formatted files, dsa-key.xml and dsa-pub.xml, containing private and public keys, respectively. Before you can run this, however, you need to create the FileUtil class used to output the two files:

  ' Cryptography/FileUtil.vb Imports System.IO Imports System.Text Public Class FileUtil     Public Sub SaveString(ByVal fname As String, ByVal data As String)         SaveBytes(fname, (New ASCIIEncoding).GetBytes(data))     End Sub          Public Function LoadString(ByVal fname As String)         Dim buf() As Byte = LoadBytes(fname)         Return (New ASCIIEncoding).GetString(buf)     End Function          Public Function LoadBytes(ByVal fname As String)         Dim finfo As FileInfo = New FileInfo(fname)         Dim length As String = CType(finfo.Length, String)         Dim buf() As Byte = New Byte(length) {}         Dim fs As FileStream = File.OpenRead(fname)                  fs.Read(buf, 0, buf.Length)         fs.Close()                  Return buf     End Function          Public Sub SaveBytes(ByVal fname As String, ByVal data() As Byte)         Dim fs As FileStream = File.OpenWrite(fname)                  fs.SetLength(0)         fs.Write(data, 0, data.Length)         fs.Close()     End Sub End Class 

The following code signs the data:

  ' Cryptography/DSASign.vb Imports System Imports System.IO Imports System.Security.Cryptography Imports System.Text Imports VB_Security.FileUtil Module Module1     Public Sub Main(ByVal CmdArgs() As String)         If CmdArgs.Length <> 3 Then             Console.WriteLine("Usage: DSASign <key xml> <data> <sign>")             End         End If         Dim fileutil As FileUtil = New FileUtil()         Dim xkey As String = fileutil.LoadString(CmdArgs(0))         Dim fs As FileStream = File.OpenRead(CmdArgs(1)) 

The following two lines of code create the DSA provider instance and reconstruct the private key from the XML format:

  Dim dsa As DSACryptoServiceProvider = New DSACryptoServiceProvider() dsa.FromXmlString(xkey) 

The next line signs the file:

          Dim sig() As Byte = dsa.SignData(fs)         fs.Close()         fileutil.SaveString(CmdArgs(2), Convert.ToString(sig))         Console.WriteLine("Signature in {0}} file", CmdArgs(2))     End Sub End Module 

To verify the signature, you can use the following sample code:

  ' Cryptography/DSAVerify.vb Imports System Imports System.IO Imports System.Security.Cryptography Imports System.Text Imports VB_Security.FileUtil Module Module1     Public Sub Main(ByVal CmdArgs() As String)         If CmdArgs.Length <> 3 Then             Console.WriteLine("Usage: DSAVerify <key xml> <data> <sign>")             End         End If                  Dim fileutil As FileUtil = New FileUtil()         Dim xkey As String = fileutil.LoadString(CmdArgs(0))         Dim data() As Byte = fileutil.LoadBytes(CmdArgs(1))         Dim xsig As String = fileutil.LoadString(CmdArgs(2))         Dim dsa As DSACryptoServiceProvider = New DSACryptoServiceProvider()         dsa.FromXmlString(xkey)         Dim xsigAsByte() As Byte = New Byte(xsig) {}         Dim verify As Boolean         verify = dsa.VerifyData(data, xsigAsByte)         Console.WriteLine("Signature Verification is {0}", verify)     End Sub End Module 

The actual verification is done using the highlighted code fragment. The next four commands compile the source files:

 vbc /target:library FileUtil.vb vbc /r:FileUtil.dll GenDSAKeys.vb vbc /r:FileUtil.dll DSASign.vb vbc /r:FileUtil.dll DSAVerify.vb

There are many helper classes within the System.Security.Cryptography and the System .Security.Cryptography.Xml namespaces, and they provide many features to help deal with digital signatures and encryption, and, at times, provide overlapping functionality. Therefore, there is more than one way of doing the same thing.

X.509 Certificates

X.509 is a public key certificate exchange framework. A public key certificate is a digitally signed statement by the owner of a private key, trusted by the verifier (usually a certifying authority), that certifies the validity of the public key of another entity. This creates a trust relationship between two unknown entities. This is an ISO standard specified by the document ISO/IEC 9594-8. X.509 certificates are also used in SSL (Secure Sockets Layer), which is covered in the next section.

There are many certifying authority services available over the Internet. VeriSign (www.verisign.com) is the most popular. This company was also founded by the RSA trio themselves. You can run your own Certificate Authority (CA) service over an intranet using Microsoft Certificate Server.

The Microsoft .NET Framework SDK also provides tools for generating certificates for testing purposes. The following command generates a test certificate:

 makecert -n CN=Test test.cer

You can view it by double-clicking the test.cer file from Windows Explorer. The certificate is shown in Figure 12-19. From the same dialog box, you can also install this certificate on your computer by clicking the Install Certificate button.

image from book
Figure 12-19

Three classes dealing with X.509 certificates are provided in the .NET Framework in the namespace System.Security.Cryptography.X509Certificates. The following program loads and manipulates the certificate created earlier:

  ' Cryptography/LoadCert.vb Imports System Imports System.Security.Cryptography.X509Certificates Module Module1     Public Sub Main(ByVal CmdArgs() As String)         If CmdArgs.Length <> 1 Then             Console.Write("Usage loadCert <cert file>")             End         End If         Dim cert As X509Certificate = _             X509Certificate.CreateFromCertFile(CmdArgs(0))         Console.WriteLine("Hash= {0}", cert.GetCertHashString())         Console.WriteLine("Effective Date= {0}", _                            cert.GetEffectiveDateString())         Console.WriteLine("Expire Date= {0}", _                            cert.GetExpirationDateString())         Console.WriteLine("Issued By= {0}", cert.Issuer.ToString())         Console.WriteLine("Issued To= {0}", cert.Subject.ToString())         Console.WriteLine("Algo= {0}", cert.GetKeyAlgorithm())         Console.WriteLine("Pub Key= {0}", cert.GetPublicKeyString())     End Sub End Module 

The static method loads CreateFromCertFile (the certificate file) and creates a new instance of the class X509Certificate. The next section deals with SSL, which uses X.509 certificates to establish the trust relationship.

Secure Sockets Layer

The SSL (Secure Sockets Layer) protocol provides privacy and reliability between two communicating applications over the Internet. SSL is built over the TCP layer. In January 1999, the IETF (Internet Engineering Task Force) adopted an enhanced version of SSL 3.0 called TLS, which stands for Transport Layer Security. TLS is backwardly compatible with SSL, and is defined in RFC 2246. However, the name SSL was retained due to wide acceptance of this Netscape protocol name.

SSL provides connection-oriented security and has the following four properties:

  • Connection is private and encryption is valid for the current session only.

  • Symmetric key cryptography, like DES, is used for encryption. However, the session secret key is exchanged using public key encryption.

  • Digital certificates are used to verify the identities of the communicating entities.

  • Secure hash functions, such as SHA and MD5, are used for message authentication code (MAC).

The SSL protocol sets the following goals for itself:

  • Cryptographic security - Uses symmetric key for session and public key for authentication

  • Interoperability - Interpolates OS and programming languages

  • Extensibility - Adds new protocols for encrypting data that are allowed within the SSL framework

  • Relative efficiency - Reduces computation and network activity by using caching techniques

The following is a simplified discussion of the SSL algorithm sequence. Two entities communicating using SSL protocols must have a public-private key pair, optionally with digital certificates validating their respective public keys.

At the beginning of a session, the client and server exchange information to authenticate each other. This ritual of authentication is called the handshake protocol. During this, a session ID, the compression method, and the cipher suite to be used are negotiated. If the certificates exist, they are then exchanged. Although certificates are optional, either the client or the server may refuse to continue with the connection and end the session in the absence of a certificate.

After receiving each other’s public keys, a set of secret keys based on a randomly generated number is exchanged by encrypting it with each other’s public keys. After this, the application data exchange can commence. The application data is encrypted using a secret key, and a signed hash of the data is sent to verify data integrity.

Microsoft implements the SSL client in the .NET Framework classes. However, the server-side SSL can be used by deploying your service through the IIS Web server. The following code fragment can be used to access SSL protected Web servers from the .NET platform:

  Dim req As WebRequest = WebRequest.Create("https://www.reuters.com") Dim result As WebResponse = req.GetResponse() 

Note that the preceding URL starts with https, which signals the WebRequest class (part of System.Net) to use the SSL protocol. Interestingly, the same code is useful for accessing unsecured URLs as well.

The following is a program for accessing a secured URL. It takes care of the minor details, such as encoding:

  ' Cryptography/GetWeb.vb Imports System Imports System.IO Imports System.Net Imports System.Text Module Module1     Public Sub Main(ByVal CmdArgs() As String)         If CmdArgs.Length <> 1 Then             Console.WriteLine("Usage: GetWeb URL")             Console.WriteLine("Example: GetWeb https://www.reuters.com")             End         End If         Dim ms As String 

You call the Create() method (shown in a moment) with a URL and an encoding format:

      Try         ms = Create(CmdArgs(0), "utf-8")         Console.WriteLine(ms)     Catch x As Exception         Console.WriteLine(x.StackTrace)         Console.WriteLine("Bad URL: {0}", CmdArgs(0))     End Try End Sub 

Next is the Create() method. Using the .NET Framework WebRequest object, you create an HTTP secured request object and get its response stream:

  Function Create(ByVal url As String, ByVal encod As String) As String     Dim req As WebRequest = WebRequest.Create(url)     Dim result As WebResponse = req.GetResponse()     Dim ReceiveStream As Stream = result.GetResponseStream() 

Create an encoding instance from the .NET Framework object, Encoding:

  Dim enc As Encoding = System.Text.Encoding.GetEncoding(encod) 

Here, you create the stream reader:

  Dim sr As StreamReader = New StreamReader(ReceiveStream, enc) 

You read the stream fully - the entire Web page or serialized object is read into the responseString:

          Dim response As String = sr.ReadToEnd()         Return response     End Function     Dim MaxContentLength As Integer = 16384 ' 16k End Module 

The preceding console application gets a secured (SSL) protected URL and displays the content on the console. To compile the code, use the following command:

 vbc /r:System.dll GetWeb.vb




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net