Encryption Basics

Rather than present an exposition of cryptography, this section is meant to familiarize you with basic techniques required to deal with .NET security and protect your Web services through encryption. There are four different categories of cryptography: encoding, hashing, and symmetric and asymmetric encryption.

First you will review each of these four different cryptographic categories. The first is encoding, which, as you may already know, if you are at all familiar with encryption, doesn't actually protect information. The most common encodings are things like UTF8, UTF7, and Base64 encoding. These encodings are typically used to take information that might interact with a container and hide the special characters. Thus, if you want to embed binary data within an XML file and want to ensure that the binary data won't interfere with the XML, you can Base64 the data, and it can safely be placed within an XML file.

Encoding is quite common for passing hidden or state data in Web pages, MIME, and XML file formats. For example, in ASP.NET, ViewState is an encoded block of information about the state of an ASP.NET page. However, keep in mind that encoded data, while not immediately humanly readable, uses a public algorithm to create its string. Encoding algorithms are designed to be quickly and easily reversed, and without any form of implied privacy. This means that anyone can reverse the encoded data, so for ASP.NET, ViewState does not protect the data which has been encoded; it just allows for transport of that data. To reiterate, encoding does not protect information.

The next item in the list of cryptography categories is hashing. Hashing algorithms digest sequences of data, creating a “random” output for the input string. A hash has a private key that can be varied by each application using the hash. Using a different key ensures you get different random string representations. While changing a single character will result in an entirely different result, the key to a hash is that there is no way to decrypt the original string from that result. In fact, hashing algorithms are specifically designed to not support the decryption of data once it has been hashed. At the same time, a hash always produces the same result for a given input string.

In terms of degree of security, hash keys are generally judged by the size of the encryption key, with larger keys (512-bit) providing greater security than shorter (128-bit) keys. Two popular hashing algorithms are SHA (Secure Hash Algorithm) and MD5 (Message-Digest algorithm 5). These hash keys are used for everything from saving passwords to signing digital documents; in other words, the hash is generated and encrypted using a private key.

Hashing works for passwords and pass phrases (longer authentication strings, which are far more difficult to guess) by never actually decrypting the password value. In order to validate your protected data, you reenter that data, which is then hashed, and the original hash is compared to the hashing of the newly entered text. If these two hashed values match, then the same text was entered. If the hashed values don't match, it means that the correct password or other information was not entered. In this way the original password can be protected not only from outsiders, but also from insiders who might want to impersonate another user.

Hashing algorithms, unlike other forms of encryption, are meant to be nonreversible. This is an important part of the security they provide. Note that in most cases, complex algorithms can be developed to reverse a hash, the most common being the creation of a dictionary of hashed values. However, the point of a hash is to create a “random” string based on input and ensure that the “random” element is repeatable for the same string. Thus, each password attempt is hashed, and the result is compared to the stored hash value for that user's password or pass phrase; matches mean success, and there is no relationship to “how close” the entered text is to the correct text, because the hashed value is “random” for any given set of characters.

Symmetric encryption is commonly used to protect data such as private messages or data that will be retrieved. Symmetric key encryption is suitable for situations where the encrypted data needs to be accessed by someone in the same organization as the one who protected it. In this scenario, a key might be embedded within an application or stored as part of some device that the organization members control. It is important to keep the key private, as the same key is used to both encrypt and decrypt the data. Private keys work well as long as only those people who are authorized to view the protected data have them. It breaks down when attempting to interchange private data with the world at large. For that you need one key used by outsiders and a different key used by insiders.

Asymmetric public key encryption is most widely used in protecting the data that may be shared with an outside group. It is also used for digital signatures. Public key encryption is based on asymmetric keys, which means you always have a pair of keys. One key 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.

Because the public key is known to all, anyone can decrypt information protected by the private key. 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. It is important to remember that when data is protected using the public key, only the holder of the private key can decrypt it; another holder of the public key will be unable to decrypt the protected information.

In some cases an entire set of data is encrypted—for example, HTTPS does this. Similarly, asymmetric encryption is also used for digital signatures. Rather than encrypt the whole document using the private key, a public key and an agreed-upon hash algorithm describing the data is used to “sign” the document. The signature is attached to the document, and the receiver then decrypts it using the private key. The result of the decryption is compared with rerunning the same hash on the key document characteristics that were agreed upon for the hash; if the results match, then the document is considered authentic. The result of this process is a digital signature associated with the digital document. This process works bidirectionally, so a document can be signed with the private key and the signature can be checked with the public key.

Because the holder of the private key will be able to read the data, it is very important that when you create a key pair, the private key must be protected and never shared.

Hash Algorithms

Hash algorithms are also called one-way functions because of their mathematical property of nonreversibility. The hash algorithms reduce large strings into a fixed-length binary byte array.

To verify a piece of information, the hash is recomputed and compared against a previously computed hash value. If both values match, then the newly provided data is correct. Cryptographic hashing algorithms map strings of data to a fixed-length result. Thus, two strings of different length will have a hash of the same size.

Although it is theoretically possible for two documents to have the same MD5 hash result, it is computationally impossible to create a meaningful forged document having the same hash key as the original hash value.

Cryptographic Hash Algorithms

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

1. MD5CryptoServiceProvider (extends abstract class MD5)
2. RIPEMD160Managed (extends abstract class RIPEMD160)
3. SHA1CryptoServiceProvider (extends abstract class SHA1)
4. SHA256Managed (extends abstract class SHA256)
5. SHA384Managed (extends abstract class SHA384)
6. SHA512Managed (extends abstract class SHA512)
7. HMACSHA1 (extends abstract class KeyedHashAlgorithm)
8. MACTripleDES (extends abstract class KeyedHashAlgorithm)

The last two classes belong to a class of algorithm called keyed hash algorithms. 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.

To demonstrate this, a hashing example is available as part of the code download. The TestHashKey.vb file is part of the ProVB2012_Security solution. This class can be called using the following line of code in the click event handler that is part of the main window's code-behind:

TextBoxResult.Text = TestHashKey.Main("....TestHashKey.vb")

Calling the shared method Main using this line of code from the Button_Click_1 event handler will run the example code shown in Listing 18.2, telling it to encrypt a copy of the source file TestHashKey.vb:

Listing 18.2 : Class TestHashKey—TestHashKey.vb

'TestHashKey.vb
Imports System
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text
        
Public Class TestHashKey
    Public Shared Function Main(ByVal pathToFileToProtect As String) As String
        Dim key() As Byte = Encoding.ASCII.GetBytes("My Secret Key".ToCharArray())
        Dim hmac As HMACSHA1 = New HMACSHA1(key)
        Dim fs As FileStream = File.OpenRead(pathToFileToProtect)
        Dim hash() As Byte = hmac.ComputeHash(fs)
        Dim b64 As String = Convert.ToBase64String(hash)
        fs.Close()
        Return b64
    End Function
End Class

Figure 18.9 Displaying the hash results

18.8

The preceding snippet creates the object instance of the .NET SDK Framework class with a salt (a random secret to confuse a snooper). The next four lines compute the hash, encode the binary hash into a printable Base64 format, close the file, and then return the Base64 encoded string. Running this will result in the hashed output shown in 18-9.

The previous example uses an instance of the HMACSHA1 class. The output displayed is a Base64 encoding of the binary hash result value. As noted earlier, 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 shown in later examples.

SHA

Secure Hash Algorithm (SHA) is a block cipher that operates on a block size of 64 bits. However, 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. Similar to the HMACSHA1 code discussed previously, the code in Listing 18.3 shows an example of using this algorithm:

Listing 18.3 : Class TestSHA1—TestSHA1.vb

'TestSHA1.vb
Imports System
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text
        
Public Class TestSHA1
    Public Shared Function Main(ByVal pathToFileToProtect As String) As String
        
        Dim fs As FileStream = File.OpenRead(pathToFileToProtect)
        Dim sha As SHA1 = New SHA1CryptoServiceProvider
        Dim hash() As Byte = sha.ComputeHash(fs)
        Dim b64 As String = Convert.ToBase64String(hash)
        fs.Close()
        Return b64
    End Function
End Class

The .NET Framework provides larger 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.Forms AuthenticationModule) uses SHA1 as one of its valid formats to store and compare user passwords.

MD5

Message-Digest algorithm 5 (MD5) 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 Rivest, Shamir and Adleman (RSA) fame. In fact, FIPS PUB 180-1 states that SHA-1 is based on principles similar 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 was 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 easily replicated by updating the SHA1 source to reference the MD5CryptoServiceProvider instead of the SHA1 provider.

Dim md5 As MD5 = New MD5CryptoServiceProvider()
Dim hash() As Byte = md5.ComputeHash(fs)

RIPEMD-160

Based on MD5, RIPEMD-160 started as a project in Europe called the RIPE (RACE Integrity Primitives Evaluation) project Message Digest in 1996. By 1997, the design of RIPEMD-160 was finalized. RIPEMD-160 is a 160-bit hash algorithm that 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. As you should recognize from the preceding MD5 example, switching to this provider is also easily accomplished:

Dim myRIPEMD As New RIPEMD160Managed()
Dim hash() As Byte = myRIPEMD.ComputeHash(fs)

Symmetric Key Encryption

Symmetric 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 provides an abstract base class SymmetricAlgorithm. Five concrete implementations of different symmetric key algorithms are provided by default:

1. AesCryptoServiceProvider (extends abstract class Aes)
2. DESCryptoServiceProvider (extends abstract class DES)
3. RC2CryptoServiceProvider (extends abstract class RC2)
4. RijndaelManaged (extends abstract class Rijndael)
5. 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. You can run a copy of symmetric encryption using the sample code. Uncomment the following line of code in the ButtonTest _Click event handler in MainWindow.xaml.vb. An example of this call is shown here:

SymEnc.Main(TextBoxResult, 0, "....SymEnc.vb", "DESencrypted.txt", True)

Listing 18.4 illustrates the code that encrypts and decrypts a file, given a secret key:

Listing 18.4 : Class SymEnc—SymEnc.vb

'SymEnc.vb
Imports System.Security.Cryptography
Imports System.IO
Imports System.Text
Imports System
        
Public Class SymEnc
    Private Shared algo() As String = {"DES", "RC2", "Rijndael", "TripleDES"}
    Private Shared b64Keys() As String = {"YE32PGCJ/g0=", _
    "vct+rJ09WuUcR61yfxniTQ==", _
    "PHDPqfwE3z25f2UYjwwfwg4XSqxvl8WYmy+2h8t6AUg=", _
    "Q1/lWoraddTH3IXAQUJGDSYDQcYYuOpm"}
    Private Shared b64IVs() As String = {"onQX8hdHeWQ=", _
    "jgetiyz+pIc=", _
    "pd5mgMMfDI2Gxm/SKl5I8A==", _
    "6jpFrUh8FF4="}
        
    Public Shared Sub Main(ByVal textBox As TextBox, ByVal algoIndex As Integer,
                    ByVal inputFile As String, ByVal outputFile As String,
                    ByVal encryptFile As Boolean)
        
        Dim fin As FileStream = File.OpenRead(inputFile)
        Dim fout As FileStream = File.OpenWrite(outputFile)
        Dim sa As SymmetricAlgorithm = SymmetricAlgorithm.Create(algo(algoIndex))
        sa.IV = Convert.FromBase64String(b64IVs(algoIndex))
        sa.Key = Convert.FromBase64String(b64Keys(algoIndex))
        textBox.Text = "Key length: " & CType(sa.Key.Length, String) &
                     Environment.NewLine
        textBox.Text &= "Initial Vector length: " & CType(sa.IV.Length, String) &
                     Environment.NewLine
        textBox.Text &= "KeySize: " & CType(sa.KeySize, String) &
                     Environment.NewLine
        textBox.Text &= "BlockSize: " & CType(sa.BlockSize, String) &
                     Environment.NewLine
        textBox.Text &= "Padding: " & CType(sa.Padding, String) &
                     Environment.NewLine
        If (encryptFile) Then
            Encrypt(sa, fin, fout)
        Else
            Decrypt(sa, fin, fout)
        End If
    End Sub

The parameters to Main provide the Textbox where the output will be displayed and the index from the array algo, which is the name of the algorithm to be used. It then looks for the input and output files, and finally a Boolean indicating whether the input should be encrypted or decrypted.

Within the code, first the action is to open the input and output files. The code then creates an instance of the selected algorithm and converts the initial vector and key strings for use by the algorithm. Symmetric algorithms essentially rely on two secret values: one called the key; the other, the initial vector, both of which are used to encrypt and decrypt the data. Both private values are required for either encryption or decryption.

The code then outputs some generic information related to the encryption being used and then checks which operation is required, executing the appropriate static method to encrypt or decrypt the file.

To encrypt, the code gets an instance of the ICryptoTransform interface by calling the CreateEncryptor method of the SymmetricAlgorithm class extender. The encryption itself is done in the following method:

     Private Shared 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(fin.Length) {}
         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

For decryption, the code gets an instance of the ICryptoTransform interface by calling the CreateDecryptor method of the SymmetricAlgorithm class instance. To test this you can uncomment the line of code which follows the method call to encrypt the file using the method Main and matches the following line:

SymEnc.Main(TextBoxResult, 0, "DESencrypted.txt", "DESdecrypted.txt", False)

The following snippet provides the decryption method:

     Private Shared 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(fin.Length) {}
         Dim cs As CryptoStream = _
         New CryptoStream(fin, trans, CryptoStreamMode.Read)
         Dim Len As Integer
         Len = cs.Read(buf, 0, buf.Length - 1)
         While (Len > 0)
             fout.Write(buf, 0, Len)
             Len = cs.Read(buf, 0, buf.Length)
         End While
         fin.Close()
         fout.Close()
     End Sub

The class CryptoStream is used for both encryption and decryption. You'll find it listed both in the Decrypt method shown in the preceding code snippet and also in the earlier code snippet that showed the Encrypt method. Notice, however, that depending on if you are encrypting or decrypting, the parameters to the constructor for the CryptoStream differ.

You'll also notice if you review the code in SymEnc.vb, that this code supports testing of encryption and decryption using any of the four symmetric key implementations provided by the .NET Framework. The second parameter to Sub Main is an index indicating which algorithm to use. The secret keys and associated initialization vectors (IVs) were generated by a simple source code generator, examined shortly.

If you haven't done so yet, you should run the application and verify the contents of the DESencrypted.txt and DESdecrypted.txt files. If the new methods run to completion, the screen display should look similar to what is shown in Figure 18.10.

Figure 18.10 Symmetric encryption characteristics as output

18.10

To generate the keys, a simple code generator is available in the file SymKey.vb. You can reference it by adding a call to the button click event as shown in the following line.

SymKey.SymKeyMain(TextBoxResult)

The code to generate the key and vectors is shown in Listing 18.5.

Listing 18.5 : Class SymKey—SymKey.vb

'SymKey.vb
Imports System.Security.Cryptography
Imports System.Text
Imports System.IO
Imports System
Imports Microsoft.VisualBasic.ControlChars
        
Public Class SymKey
    Public Shared Sub SymKeyMain(ByVal textBox As TextBox)
        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)
        Dim algo() As String = {"DES", "RC2", "Rijndael", "TripleDES"}
        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(algo(i) & vbTab & ": """ & Key & """" & vbCrLf)
            ivz.AppendFormat(algo(i) & vbTab & ": """ & IV & """" & vbCrLf)
        Next i
        keyz.Append("}")
        ivz.Append("}")
        textBox.Text = keyz.ToString() & vbCrLf & ivz.ToString()
    End Sub
End Class

The preceding program creates a random key and an initializing vector for each algorithm. When run, the result are displayed as shown in Figure 18.11. You can use the output from this call to create unique and thus secure keys that can be copied into the SymEnc.vb program.

Figure 18.11 Generating random encryption key values

18.11

PKCS

The Public Key Cryptographic System (PKCS) 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 with 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. Four concrete implementations of this class are provided by default:

1. DSACryptoServiceProvider, which extends the abstract class DSA
2. ECDiffieHellmanCngCryptoServiceProvider, which extends the ECDiffieHellmanCng abstract class
3. ECDsaCngCryptoServiceProvider, which extends the abstract class ECDsaCng
4. RSACryptoServiceProvider, which extends the abstract class RSA

The Digital Signature Algorithm (DSA) was specified by the National Institute of Standards and Technology (NIST) in January 2000. The original DSA standard, however, was issued by NIST much earlier, 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 section.

Similarly, the ECDsa algorithm is also an elliptic curve algorithm, in this case combined with the Digital Signature Algorithm. This is then enhanced with a Cryptographic Next Generation algorithm.

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.

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.

Digital Signature Example

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 both implementations extend the same base class, so all programs for DSA discussed here work for RSA as well.

First, you need to produce a key pair. To do this, you'll need the the following method which has been added to the ProVB2012_Security main window. This can be called once from the Button_Click_1 event handler to generate the necessary files in your application's folder.

     Private Sub GenDSAKeys()
         Dim dsa As Security.Cryptography.DSACryptoServiceProvider =
             New Security.Cryptography.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) End Sub

This method generates two XML-formatted files, dsa-key.xml and dsa-pub.xml, containing private and public keys, respectively. This code is dependent on an additional class, FileUtil, that is available in the project to wrap some of the common file I/O operations. This file is shown in Listing 18.6.

Listing 18.6 : Class FileUtil—FileUtil.vb

'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
    Public Function LoadSig(ByVal fname As String)
        Dim fs As FileStream = File.OpenRead(fname)
        ' Need to omit the trailing null from the end of the 0 based buffer.
        Dim buf() As Byte = New Byte(39) {}
        fs.Read(buf, 0, buf.Length)
        fs.Close()
        Return buf
    End Function
End Class

To create the signature for a data file, call the method SignMain in the class DSASign from the Button_Click_1 event handler. Listing 18.7 shows the code that signs the data:

Listing 18.7 : Class DSASign with Sub SignMain—DSASign.vb

'DSASign.vb
Imports System
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text
        
Public Class DSASign
    Public Shared Sub SignMain()
        
        Dim fileutil As FileUtil = New FileUtil
        Dim xkey As String = fileutil.LoadString("dsa-key.xml")
        Dim fs As FileStream = File.OpenRead("....FileUtil.vb")
        Dim data(fs.Length) As Byte
        fs.Read(data, 0, fs.Length)
        Dim dsa As DSACryptoServiceProvider = New DSACryptoServiceProvider
        dsa.FromXmlString(xkey)
        Dim sig() As Byte = dsa.SignData(data)
        fs.Close()
        fileutil.SaveBytes("FileUtilSignature.txt", sig)
    End Sub
End Class

The two lines of code that reference the DSACryptoServiceProvider and dsa.FromXmlString method actually create the DSA provider instance and reconstruct the private key from the XML format. Next, the file is signed using the call to dsa.SignData while passing the file stream to be signed to this method. The FileStream is then cleaned up and the resulting signature is saved into the output file.

Now that you have a data file and a signature, the next step is to verify the signature. The class DSAVerify can be leveraged to verify that the signature file created is in fact valid. Listing 18.8 illustrates the contents of this class.

Listing 18.8 : Class DSAVerify with Function VerifyMain—DSAVerify.vb

'DSAVerify.vb
Imports System
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text
        
Public Class DSAVerify

    Public Shared Function VerifyMain() As String
        
        Dim fileutil As FileUtil = New FileUtil
        Dim xkey As String = fileutil.LoadString("dsa-key.xml")
        Dim fs As FileStream = File.OpenRead("....FileUtil.vb")
        Dim data(fs.Length) As Byte
        fs.Read(data, 0, fs.Length)
        Dim xsig() As Byte = fileutil.LoadSig("FileUtilSignature.txt")
        Dim dsa As DSACryptoServiceProvider = New DSACryptoServiceProvider
        dsa.FromXmlString(xkey)
        Dim verify As Boolean = dsa.VerifyData(data, xsig)
        Return String.Format("Signature Verification is {0}", verify)
    End Function
End Class

During testing you may want to ensure that both of these methods are enabled at the same time. This will ensure that you are encrypting and decrypting with the same keys. When working correctly, your display should look similar to what is shown in Figure 18.12.

Figure 18.12 Validating the signature for a certificate

18.12

There are many helper classes in the System.Security .Cryptography and System.Security.Cryptography.Xml namespaces. These classes provide numerous features to help deal with digital signatures and encryption. They also provide overlapping functionality, so 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. X.509 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.

Many certifying authority services are available over the Internet. VeriSign (www.verisign.com) is one of the most popular, and was founded by the RSA trio themselves. Other providers may cost less, but if you intend to make your certificate public, you'll want to investigate if they are default providers within the Windows operating system. Alternatively, at the low-cost end, and during development, you can run your own Certificate Authority (CA) service over an intranet using Microsoft Certificate Services.

The Microsoft .NET Framework SDK also provides tools for generating certificates for testing purposes. Using the Developer Command Prompt for VS2012, the following command generates a test certificate:

makecert -n CN=ProVB test.cer

The certificate is with the code at the solution directory level.

Three classes dealing with X.509 certificates are provided in the .NET Framework in the namespace System.Security.Cryptography.X509Certificates. The code in Listing 18.9 loads and manipulates the certificate created using makecert. Note you'll need to adjust the path to that certificate from the sample code.

Listing 18.9 : Class CertLoad with Sub CertMain—CertLoad.vb

’ CertLoad.vb
Imports System
Imports System.Security.Cryptography.X509Certificates
        
Public Class CertLoad
    Public Shared Sub CertMain(ByVal certFilePath As String, 
                               ByVal textbox As TextBox)
        
        Dim cert As X509Certificate = _
        X509Certificate.CreateFromCertFile(certFilePath)
        textbox.Text = "Hash = " & cert.GetCertHashString() & Environment.NewLine
        textbox.Text &= "Effective Date = " &
            cert.GetEffectiveDateString() & Environment.NewLine
        textbox.Text &= "Expire Date = " &
            cert.GetExpirationDateString() & Environment.NewLine
        textbox.Text &= "Issued By = " & cert.Issuer & Environment.NewLine
        textbox.Text &= "Issued To = " & cert.Subject & Environment.NewLine
        textbox.Text &= "Algorithm = " & cert.GetKeyAlgorithm() &
                         Environment.NewLine
        textbox.Text &= "Pub Key = " & cert.GetPublicKeyString() &
                         Environment.NewLine
    End Sub
End Class

The static method loads CreateFromCertFile (the certificate file) and creates a new instance of the class X509Certificate. When working correctly, the results are displayed in ProVB_Security as shown in Figure 18.13. The next section deals with Secure Sockets Layer (SSL), which uses X.509 certificates to establish the trust relationship.

Figure 18.13 Displaying the contents of a certificate file

18.13

Secure Sockets Layer

The Secure Sockets Layer (SSL) protocol provides privacy and reliability between two communicating applications over the Internet. SSL is built over the TCP layer. In January 1999, the Internet Engineering Task Force (IETF) adopted an enhanced version of SSL 3.0 called Transport Layer Security (TLS). 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. This section provides a simplified overview of the SSL algorithm sequence. SSL provides connection-oriented security via the following four properties:

1. Connection is private and encryption is valid for the current session only.
2. Symmetric key cryptography, like DES, is used for encryption. However, the session symmetric key is exchanged using public key encryption.
3. Digital certificates are used to verify the identities of the communicating entities.
4. Secure hash functions, such as SHA and MD5, are used for message authentication code (MAC).

The SSL protocol provides the following features:

  • Cryptographic security—Using a symmetric key for session data-encryption, and a public key for authentication
  • Interoperability—Interpolates OS and programming languages
  • Extensibility—Adds new data-encryption protocols that are allowed within the SSL framework
  • Relative efficiency—Reduces computation and network activity by using caching techniques

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 handshake, a session ID, the compression method, and the cipher suite to be used are negotiated. If the certificates exist, then they are 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 them 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 snippet demonstrates a method for accessing a secured URL. It takes care of minor details, such as encoding, and allows you to directly receive the results of a web request.

’ Cryptography/GetWeb.vb
Imports System
Imports System.IO
Imports System.Net
Imports System.Text
        
Public Class GetWeb
    Dim MaxContentLength As Integer = 16384 ' 16k
        
    Public Shared Function QueryURL(ByVal url As String) As String
        Dim req As WebRequest = WebRequest.Create(url)
        Dim result As WebResponse = req.GetResponse()
        Dim ReceiveStream As Stream = result.GetResponseStream()
        Dim enc As Encoding = System.Text.Encoding.GetEncoding("utf-8")
        Dim sr As StreamReader = New StreamReader(ReceiveStream, enc)
        Dim response As String = sr.ReadToEnd()
        Return response
    End Function
        
End Class

Using this method from the ProVB_Security application allows you to retrieve the information associated with the selected Web page. In this case, you can pass the URL www.amazon.com to the method from the ButtonTest click event handler. The resulting display should be similar to what is shown in Figure 18.14.

Figure 18.14 Output from a programmatic HTTP request

18.14
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset