So far, you have seen the principles of cryptography and how they are achieved through the use of hashing, encryption, and signing algorithms. In this section, you'll walk through a sample that applies these algorithms and illustrates how the .NET Framework can be used to securely pass data between two parties.
Begin with a new Visual Basic Windows Forms application and divide the form into two vertical columns. You can do this using a TableLayoutPanel with docked Panel controls. Into each of the two vertical columns place a button, btnCreateAsymmetricKey1 and btnCreateAsymmetricKey2 respectively, which will be used to generate the asymmetric keys. Also add two textboxes to each column, which will be used to display the private and public keys. The textboxes in the left column should be named TxtPublicKey1 and TxtPrivateKey1, and the textboxes in the right column should be named TxtPublicKey2 and TxtPrivateKey2. The result should be something similar to Figure 28-1. For reference, add a name label to each of the vertical panels.
Double-clicking each of the buttons will create event handlers into which you need to add code to generate an asymmetric key pair. In this case use the RSACryptoServiceProvider class, which is an implementation of the RSA algorithm. Creating a new instance of this class automatically generates a new key pair that can be exported via the ToXmlString method, as shown in the following code. This method takes a Boolean parameter that determines whether the private key information should be exported:
Imports System Imports System.IO Imports System.Security.Cryptography Imports System.Net.Sockets Imports System.Text Public Class Form1 #Region "Step 1 - Creating Asymmetric Keys" Private Sub BtnCreateAsymmetricKey1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnCreateAsymmetricKey1.Click CreateAsymmetricKey(Me.TxtPrivateKey1, Me.TxtPublicKey1) End Sub Private Sub BtnCreateAsymmetricKey2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnCreateAsymmetricKey2.Click CreateAsymmetricKey(Me.TxtPrivateKey2, Me.TxtPublicKey2) End Sub Private Sub CreateAsymmetricKey(ByVal txtPrivate As TextBox, _ ByVal txtPublic As TextBox) Dim RSA As New RSACryptoServiceProvider() txtPrivate.Text = RSA.ToXmlString(True) txtPublic.Text = RSA.ToXmlString(False) End Sub #End Region End Class
In the preceding example you can see that a number of namespaces have been imported, which makes it much easier to work with the cryptography classes. When this application is run and the buttons are invoked, two new key pairs are created and displayed in the appropriate textboxes. Examining the text from one of the private key textboxes, you can see that it is an XML block broken up into a number of sections that represent the different components required by the RSA algorithm:
<RSAKeyValue> <Modulus>uUWTj5Ub+x+LN5xE63y8zLQf4JXNU0WAADsShaBK+jF/cDGd Xc9VFcuDvRIX0oKLdUslpH cRcFh3VLi7djU+oRKAZUfs+75mMCCnoybPEHWWCsRHoIk8s4BAZuJ7KCQ O+Jb9DxYQbeeCI9bYm2yYWtHRvq7PJha5sbMvxkLOI1M=</Modulus> <Exponent>AQAB</Exponent> <P>79tcNXbc02ZVowH9qOuv3vrj6F009BSLdfSBtX6y8sosIAsLUfVqH+ UEPKQbZO/gLDAyf3U65Qkj 5QZE03CFeQ==</P>
<Q>xb28iwn6BPHqCaDPhxtea6p/OnYNTtJ8f/3Y/zHEl0Mc0aBjtY3Ci1 ggnkUGvM4j/+BRTBwUOPKG NP9DUE94Kw==</Q> <DP>0IkkYytjlLyNSfsKIho/vxrcmYKn7moKUlRxjW2JgcM6l+ViQzCew vonM93uH1TazzBcRyqSON0 4gv9vSXGz6Q==</DP> <DQ>j3bFICsw1f2dyzZ82o0kyAB/Ji8YIKPd6A6ILT4yX3w1oHE5ZjNff jGGGM4DwV/eBnr9ALcuhNK QREsez1mY2Q==</DQ> <InverseQ>hS1ygkBiiYWyE7DjFgO1eOFhFQxOaL1vPoqlAxw0YepbSQA DBGmP8IB1ygzJjP3dmMEvQ Zhwsbs6MAfPIe/gYQ==</InverseQ> <D>r4WC7pxNDfQsaFrb0F00YJqlOJezFhjZ014jhgT+A1mxahEXDTDHYw aToCPr/bs/c7flyZIkK1Mk elcpAiwfT8ssNgx2H97zhcHkcvCBO8yCgc0r+cSYlRNKLa+UPwsoXcc5N XGT0SHQG+GCVl7bywrtrWRryaWOIpSwuHmjZYE=</D> </RSAKeyValue>
In actual fact, this block shows both the public- and private-key components, which you can see if you look at the corresponding public-key textbox:
<RSAKeyValue> <Modulus>uUWTj5Ub+x+LN5xE63y8zLQf4JXNU0WAADsShaBK+jF/cDGd Xc9VFcuDvRIX0oKLdUslpH cRcFh3VLi7djU+oRKAZUfs+75mMCCnoybPEHWWCsRHoIk8s4BAZuJ7KCQ O+Jb9DxYQbeeCI9bYm2yYWtHRvq7PJha5sbMvxkLOI1M=</Modulus> <Exponent>AQAB</Exponent> </RSAKeyValue>
As you will learn later, this public key can be distributed so that it can be used to encrypt and sign information. Of course, the private key should be kept in a secure location.
In the example, only David is going to create a symmetric key (that will be shared with Julie after being encrypted and signed using a combination of their asymmetric keys). A more secure approach would be for both parties to generate symmetric keys and for them to be shared and combined into a single key.
Before adding code to generate the symmetric key, expand the dialog so the key can be displayed. Figure 28-2 shows two textboxes, named TxtSymmetricIV and TxtSymmetricKey, that will contain the IV (Initialization Vector) and the Key. The data being encrypted is broken down into a series of individually encrypted input blocks. If two adjacent blocks are identical, the process of encrypting a stream of data using a simple key would result in two identical blocks in the encrypted output. Combined with the knowledge of the input data, this can be used to recover the key. A solution to this problem is to use the previous input block as a seed for the encryption of the current block. Of course, at the beginning of the data there is no previous block, and it is here that the initialization vector is used. This vector can be as important as the key itself, so it should also be kept secure.
Add a new button named BtnCreateSymmetric to the form, and label it Create Symmetric Key. In the event handler for this button, you need to create an instance of the TripleDESCryptoServiceProvider class, which is the default implementation of the TripleDES algorithm. Create a new instance of the class and then call the GenerateIV and GenerateKey methods to randomly generate a new key and initialization vector. Because these are both byte arrays, convert them to a base-64 string so they can be displayed in the textbox:
Public Class Form1 #Region "Step 1 - Creating Asymmetric Keys" '... #End Region #Region "Step 2 - Creating Symmetric Keys" Private Sub BtnCreateSymmetric_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles BtnCreateSymmetric.Click Dim TDES As New TripleDESCryptoServiceProvider() TDES.GenerateIV() TDES.GenerateKey() Me.TxtSymmetricIV.Text = Convert.ToBase64String(TDES.IV) Me.TxtSymmetricKey.Text = Convert.ToBase64String(TDES.Key) End Sub #End Region End Class
Now that we have the symmetric key, we need to encrypt it using Julie's public key and generate a hash value that can be signed using David's private key. The encrypted key and signature can then be transmitted securely to Julie. Three TextBox controls named TxtEncryptedKey, TxtHashValue, and TxtSymmetricSignature, as well as a button named BtnEncryptKey, have been added to the dialog in Figure 28-3, so you can create and display the encrypted key, the hash value, and the signature.
As we discussed earlier, this step involves three actions: encrypting the symmetric key, generating a hash value, and generating a signature. Encrypting the symmetric key is again done using an instance of the RSACryptoServiceProvider class, which is initialized using Julie's public key. It is then used to encrypt both the initialization vector and the key into appropriate byte arrays. Because you want to create only a single hash and signature, these two byte arrays are combined into a single array, which is prepended with the lengths of the two arrays. This is done so the arrays can be separated before being decrypted.
The single-byte array created as part of encrypting the symmetric key is used to generate the hash value with the SHA1Managed algorithm. This hash value is then signed again using an instance of the RSACryptoServiceProvider, initialized this time with David's private key. An instance of the RSAPKCS1SignatureFormatter class is also required to generate the signature from the hash value:
Public Class Form1 #Region "Step 1 & 2" '... #End Region #Region "Step 3 - Encrypt, Hash and Sign Symmetric Key" Private Sub BtnEncryptKey_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles BtnEncryptKey.Click EncryptSymmetricKey() Me.TxtHashValue.Text = Convert.ToBase64String _ (CreateSymmetricKeyHash(Me.TxtEncryptedKey.Text)) SignSymmetricKeyHash() End Sub
Private Sub EncryptSymmetricKey() Dim iv, key As Byte() Dim encryptedIV, encryptedkey As Byte() iv = Convert.FromBase64String(Me.TxtSymmetricIV.Text) key = Convert.FromBase64String(Me.TxtSymmetricKey.Text) 'Load the RSACryptoServiceProvider class using 'only the public key Dim RSA As New RSACryptoServiceProvider() RSA.FromXmlString(Me.TxtPublicKey1.Text) 'Encrypt the Symmetric Key encryptedIV = RSA.Encrypt(iv, False) encryptedkey = RSA.Encrypt(key, False) 'Create a single byte array containing both the IV and Key 'so that we only need to encrypt and distribute a single value Dim keyOutput(2 * 4 - 1 + encryptedIV.Length + encryptedkey.Length) As Byte Array.Copy(BitConverter.GetBytes(encryptedIV.Length), 0,keyOutput, 0, 4) Array.Copy(BitConverter.GetBytes(encryptedkey.Length), 0, keyOutput, 4, 4) Array.Copy(encryptedIV, 0, keyOutput, 8, encryptedIV.Length) Array.Copy(encryptedkey, 0, keyOutput, 8 + encryptedIV.Length, _ encryptedkey.Length) Me.TxtEncryptedKey.Text = Convert.ToBase64String(keyOutput) End Sub Private Function CreateSymmetricKeyHash(ByVal inputString As String) As Byte() 'Retrieve the bytes for this string Dim UE As New UnicodeEncoding() Dim MessageBytes As Byte() = UE.GetBytes(inputString) 'Use the SHA1Managed provider to hash the input string Dim SHhash As New SHA1Managed() Return SHhash.ComputeHash(MessageBytes) End Function Private Sub SignSymmetricKeyHash() 'The value to hold the signed value. Dim SignedHashValue() As Byte 'Load the RSACryptoServiceProvider using the 'private key as we will be signing Dim RSA As New RSACryptoServiceProvider RSA.FromXmlString(Me.TxtPrivateKey2.Text)
'Create the signature formatter and generate the signature Dim RSAFormatter As New RSAPKCS1SignatureFormatter(RSA) RSAFormatter.SetHashAlgorithm("SHA1") SignedHashValue = RSAFormatter.CreateSignature _ (Convert.FromBase64String(Me.TxtHashValue.Text)) Me.TxtSymmetricSignature.Text = Convert.ToBase64String(SignedHashValue) End Sub #End Region End Class
At this stage, the encrypted key and signature are ready to be transferred from David to Julie.
To simulate the encrypted key and signature being transferred, create additional controls on Julie's side of the dialog. Shown in Figure 28-4, the "Retrieve Key" button will retrieve the key, signature, and public key from David and populate the appropriate textboxes. In a real application, information could potentially be e-mailed, exported as a file and copied, or sent via a socket connection to a remote application. Essentially, it doesn't matter how the key and signature are transferred, as they are encrypted to prevent any unauthorized person from accessing the information.
Because the key and signature might have been sent via an unsecured channel, it is necessary to validate that the sender is who this person claims to be. You can do this by validating the signature using the public key from the sender. Figure 28-4 shows what the form will look like if the "Validate Key" button is pressed and the signature received is successfully validated against the public key from the sender.
The code to validate the received signature is very similar to that used to create the signature. A hash value is created from the encrypted key. Using the same algorithm that was used to create the received signature, a new signature is created. Finally, the two signatures are compared via the VerifySignature method, and the background color is adjusted accordingly. To build this part of the form, add a button named BtnRetrieveKeyInfo and a button named BtnValidate. Next, add three new TextBox controls named TxtRetrievedKey, TxtRetrievedSignature, and TxtRetrievedPublicKey. Finally, add the following button-event handlers to the code:
Public Class Form1 #Region "Step 1 - 3" '... #End Region #Region "Step 4 - Transfer and Validate Key Information" Private Sub BtnRetrieveKeyInfo_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles BtnRetrieveKeyInfo.Click Me.TxtRetrievedKey.Text = Me.TxtEncryptedKey.Text Me.TxtRetrievedSignature.Text = Me.TxtSymmetricSignature.Text Me.TxtRetrievedPublicKey.Text = Me.TxtPublicKey2.Text End Sub Private Sub BtnValidate_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles BtnValidate.Click 'Create the expected hash from the retrieved public key Dim HashValue, SignedHashValue As Byte() HashValue = CreateSymmetricKeyHash(Me.TxtRetrievedKey.Text) 'Generate the expected signature Dim RSA As New RSACryptoServiceProvider() RSA.FromXmlString(Me.TxtRetrievedPublicKey.Text) Dim RSADeformatter As New RSAPKCS1SignatureDeformatter(RSA) RSADeformatter.SetHashAlgorithm("SHA1") SignedHashValue = Convert.FromBase64String(Me.TxtRetrievedSignature.Text) 'Validate against received signature If RSADeformatter.VerifySignature(HashValue, SignedHashValue) Then Me.TxtRetrievedKey.BackColor = Color.Green Else Me.TxtRetrievedKey.BackColor = Color.Red End If End Sub #End Region End Class
Now that you have received and validated the encrypted key, the last remaining step before you can use the symmetric key to exchange data is to decrypt the key.
Decrypting the symmetric key will return the initialization vector and the key required to use the symmetric key. In Figure 28-5, the dialog has been updated to include the appropriate textboxes to display the decrypted values. These should match the initialization vector and key that were originally created by David. The button has been named BtnDecryptKeyInformation, and the two textboxes TxtDecryptedIV and TxtDecryptedKey.
To decrypt the symmetric key, reverse the process for encrypting the symmetric key. Start by breaking up the single encrypted byte array into the iv and key byte arrays. To decrypt the key, you again need to create an instance of the RSACryptoServiceProvider class using Julie's private key. Because the data was encrypted using Julie's public key, the corresponding private key needs to be used to decrypt the data. This instance is then used to decrypt the initialization vector and the key:
Public Class Form1 #Region "Step 1 - 4" '... #End Region #Region "Step 5 - Decrypt Symmetric key" Private Sub BtnDecryptKeyInformation_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles BtnDecryptKeyInformation.Click Dim iv, key As Byte() 'Retrieve the iv and key arrays from the single array Dim keyOutput As Byte() = Convert.FromBase64String(Me.TxtRetrievedKey.Text) ReDim iv(BitConverter.ToInt32(keyOutput, 0) - 1) ReDim key(BitConverter.ToInt32(keyOutput, 4) - 1) Array.Copy(keyOutput, 8, iv, 0, iv.Length) Array.Copy(keyOutput, 8 + iv.Length, key, 0, key.Length) 'Load the RSACryptoServiceProvider class using Julie's private key
Dim RSA As New RSACryptoServiceProvider() RSA.FromXmlString(Me.TxtPrivateKey1.Text) 'Decrypt the symmetric key and IV. Me.TxtDecryptedIV.Text = Convert.ToBase64String(RSA.Decrypt(iv, False)) Me.TxtDecryptedKey.Text = Convert.ToBase64String(RSA.Decrypt(key, False)) End Sub #End Region End Class
Both Julie and David have access to the symmetric key, which they can now use to transmit secure data. In Figure 28-6, the dialog has been updated one last time to include three new textboxes and a send button on each side of the form. Text can be entered in the first textbox. Pressing the send button will encrypt the text and place the encrypted data in the second textbox. The third textbox will be used to receive information from the other party. The button on the left is called btnSendAToB, and the associated textboxes are TxtMessageA, TxtMessageAEncrypted, and TxtReceivedMessageFromB. The corresponding button on the right is called BtnSendBToA, and the associated textboxes are TxtMessageB, TxtMessageBEncrypted, and TxtReceivedMessageFromA.
In the following code, the symmetric key is used to encrypt the text entered in the first textbox, placing the encrypted output in the second textbox. You will notice from the code that the process by which the data is encrypted is different from the process you used with an asymmetric algorithm. Asymmetric algorithms are useful for encrypting short amounts of data, which means that they are typically used for keys and pass phrases. On the other hand, symmetric algorithms can chain data together, enabling large amounts of data to be encrypted. For this reason, they are suitable for a streaming model. During encryption or decryption, the input data can come from any stream, be it a file, the network, or an in-memory stream. Here is the code:
Public Class Form1 #Region "Step 1 - 5" '... #End Region #Region "Step 6 - Sending a Message" Private Sub btnSendAToB_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnSendAToB.Click Me.TxtMessageAEncrypted.Text = EncryptData(Me.TxtMessageA.Text, _ Me.TxtDecryptedIV.Text, _ Me.TxtDecryptedKey.Text) End Sub Private Sub BtnSendBToA_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles BtnSendBToA.Click Me.TxtMessageBEncrypted.Text = EncryptData(Me.TxtMessageB.Text, _ Me.TxtSymmetricIV.Text, _ Me.TxtSymmetricKey.Text) End Sub Private Function EncryptData(ByVal data As String, ByVal iv As String, _ ByVal key As String) As String Dim KeyBytes As Byte() = Convert.FromBase64String(key) Dim IVBytes As Byte() = Convert.FromBase64String(iv) 'Create the output stream Dim strm As New IO.MemoryStream 'Create the TripleDES class to do the encryption Dim Triple As New TripleDESCryptoServiceProvider() 'Create a CryptoStream with the output stream and encryption algorithm Dim CryptStream As New CryptoStream(strm, _ Triple.CreateEncryptor(KeyBytes, IVBytes), _
CryptoStreamMode.Write) 'Write the text to be encrypted Dim SWriter As New StreamWriter(CryptStream) SWriter.WriteLine(data) SWriter.Close() Return Convert.ToBase64String(strm.ToArray) End Function #End Region End Class
To encrypt the text message to be sent, create another instance of the TripleDESCryptoServiceProvider, which is the same provider you used to create the symmetric key. This, combined with the memory output stream, is used to create the CryptoStream. A StreamWriter is used to provide an interface for writing the data to the stream. The content of the memory stream is the encrypted data.
The final stage in this application is for the encrypted data to be transmitted and decrypted. To wire this up, trap the TextChanged event for the encrypted data textboxes. When this event is triggered, the encrypted data will be copied to the receiving side and decrypted, as shown in Figure 28-7. This simulates the information being sent over any unsecured channel.
Decryption of the encrypted data happens the same way as encryption. An instance of the TripleDESCryptoServiceProvider is used in conjunction with the memory stream, based on the encrypted data, to create the CryptoStream. Via a StreamReader, the decrypted data can be read from the stream:
Public Class Form1 #Region "Step 1 - 6" '... #End Region #Region "Step 7 - Receiving a Message" Private Sub TxtMessageAEncrypted_TextChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles TxtMessageAEncrypted.TextChanged Me.TxtReceivedMessageFromA.Text = DecryptData( _ Me.TxtMessageAEncrypted.Text, _ Me.TxtSymmetricIV.Text, _ Me.TxtSymmetricKey.Text) End Sub Private Sub TxtMessageBEncrypted_TextChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles TxtMessageBEncrypted.TextChanged Me.TxtReceivedMessageFromB.Text = DecryptData( _ Me.TxtMessageBEncrypted.Text, _ Me.TxtDecryptedIV.Text, _ Me.TxtDecryptedKey.Text) End Sub Private Function DecryptData(ByVal data As String, ByVal iv As String, _ ByVal key As String) As String Dim KeyBytes As Byte() = Convert.FromBase64String(key) Dim IVBytes As Byte() = Convert.FromBase64String(iv) 'Create the input stream from the encrypted data Dim strm As New IO.MemoryStream(Convert.FromBase64String(data)) 'Create the TripleDES class to do the decryption Dim Triple As New TripleDESCryptoServiceProvider() 'Create a CryptoStream with the input stream and decryption algorithm Dim CryptStream As New CryptoStream(strm, _ Triple.CreateDecryptor(KeyBytes, IVBytes), _
CryptoStreamMode.Read) 'Read the stream. Dim SReader As New StreamReader(CryptStream) Return SReader.ReadToEnd End Function #End Region End Class
As demonstrated in this example, you can use asymmetric keys to authenticate the communicating parties and securely exchange a symmetric key. This ensures non-repudiation, as only the authenticated parties have access to the key, and the information is securely encrypted to achieve confidentiality and data integrity. Using a combination of algorithms, you have protected your data and achieved the goals of cryptography.
18.220.237.24