: C# 2008 Programmer

Encryption and Decryption

Encryption and Decryption

Hashing is a one-way process, which means that once a value is hashed, you can't obtain its original value by reversing the process. This characteristic is particularly well suited for authentications as well as digitally signing a document.

In reality, there are many situations that require information to be performed in a two-way process. For example, to send a secret message to a recipient, you need to "scramble" it so that only the recipient can see it. This process of scrambling is known as encryption. Undoing the scrambling process to obtain the original message is known as decryption. There are two main types of encryption: symmetric and asymmetric.

Symmetric Encryption

Symmetric encryption is also sometimes known as private key encryption. You encrypt a secret message using a key that only you know. To decrypt the message, you need to use the same key. Private key encryption is effective only if the key can be kept a secret. If too many people know the key, its effectiveness is reduced, and if the key's secrecy is compromised somehow, then the message is no longer secure.

Despite the potential weakness of private key encryption, it is very easy to implement and, computationally, it does not take up too many resources.

For private key encryption (symmetric), the .NET Framework supports the DES, RC2, Rijndael, and TripleDES algorithms.

To see how symmetric encryption works, you will use the RijndaelManaged class in the following SymmetricEncryption() function. Three parameters are required the string to be encrypted, the private key, and the initialization vector (IV). The IV is a random number used in the encryption process to ensure that no two strings will give the same cipher text (the encrypted text) after the encryption process. You will need the same IV later on when decrypting the cipher text.

To perform the actual encryption, you initialize an instance of the CryptoStream class with a MemoryStream object, the cryptographic transformation to perform on the stream, and the mode of the stream (Write for encryption and Read for decryption):

static string SymmetricEncryption(string str, byte[] key, byte[] IV) {
MemoryStream ms = new MemoryStream();
try {
//---creates a new instance of the RijndaelManaged class---
RijndaelManaged RMCrypto = new RijndaelManaged();
//---creates a new instance of the CryptoStream class---
CryptoStream cryptStream = new CryptoStream(
ms, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write);
StreamWriter sWriter = new StreamWriter(cryptStream);
//---encrypting the string---
sWriter.Write(str);
sWriter.Close();
cryptStream.Close();
//---return the encrypted data as a string---
return System.Convert.ToBase64String(ms.ToArray());
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
return (String.Empty);
}
}

The encrypted string is returned as a Base64-encoded string. You can check the allowable key sizes for the RijndaelManaged class by using the following code:

KeySizes[] ks;
RijndaelManaged RMCrypto = new RijndaelManaged();
ks = RMCrypto.LegalKeySizes;
//---print out the various key sizes---
Console.WriteLine(ks[0].MaxSize); // 256
Console.WriteLine(ks[0].MinSize); // 128
Console.WriteLine(ks[0].SkipSize); // 64

The valid key sizes are: 16 bytes (128 bit), 24 bytes (128 bits + 64 bits), and 32 bytes (256 bits).

You can get the system to generate a random key and IV (which you need to supply in the current example) automatically:

//---generate key---
RMCrypto.GenerateKey();
byte[] key = RMCrypto.Key;
Console.WriteLine("Key : " + System.Convert.ToBase64String(key));
//---generate IV---
RMCrypto.GenerateIV();
byte[] IV = RMCrypto.IV;
Console.WriteLine("IV : " + System.Convert.ToBase64String(IV));

If the IV is null when it is used, the GenerateIV() method is called automatically. Valid size for the IV is 16 bytes.

To decrypt a string encrypted using the RijndaelManaged class, you can use the following SymmetricDecryption() function:

static string SymmetricDecryption(string str, byte[] key, byte[] IV) {
try {
//---converts the encrypted string into a byte array---
byte[] b = System.Convert.FromBase64String(str);
//---converts the byte array into a memory stream for decryption---
MemoryStream ms = new MemoryStream(b);
//---creates a new instance of the RijndaelManaged class---
RijndaelManaged RMCrypto = new RijndaelManaged();
//---creates a new instance of the CryptoStream class---
CryptoStream cryptStream = new CryptoStream(
ms, RMCrypto.CreateDecryptor(key, IV), CryptoStreamMode.Read);
//---decrypting the stream---
StreamReader sReader = new StreamReader(cryptStream);
//---converts the decrypted stream into a string---
String s = sReader.ReadToEnd();
sReader.Close();
return s;
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
return String.Empty;
}
}

The following code snippet shows how to use the SymmetricEncryption() and SymmetricDecryption() functions to encrypt and decrypt a string:

RijndaelManaged RMCrypto = new RijndaelManaged();
//---generate key---
RMCrypto.GenerateKey();
byte[] key = RMCrypto.Key;
Console.WriteLine("Key : " + System.Convert.ToBase64String(key));
//---generate IV---
RMCrypto.GenerateIV();
byte[] IV = RMCrypto.IV;
Console.WriteLine("IV : " + System.Convert.ToBase64String(IV));
//---encrypt the string---
string cipherText =
SymmetricEncryption("This is a test string.", key, IV);
Console.WriteLine("Ciphertext: " + cipherText);
//---decrypt the string---
Console.WriteLine("Original string: " +
SymmetricDecryption(cipherText, key, IV));

Figure 11-7 shows the output.


Figure 11-7

Asymmetric Encryption

Private key encryption requires the key used in the encryption process to be kept a secret. A more effective way to transport secret messages to your intended recipient is to use asymmetric encryption (also known as public key encryption), which involves a pair of keys involved. This pair, consisting of a private key and a public key, is related mathematically such that messages encrypted with the public key can only be decrypted with the corresponding private key. The reverse is also true; messages encrypted with the private key can only be decrypted with the public key. Let's see an example for each scenario.

Before you send a message to your friend Susan, Susan needs to generate the key pair containing the private key and the public key. Susan then freely distributes the public key to you (and all her other friends) but keeps the private key to herself. When you want to send a message to Susan, you use her public key to encrypt the message. Upon receiving the encrypted message, Susan proceeds to decrypt it with her private key. Susan is the only one who can decrypt the message because the key pair works in such a way that only messages encrypted with the public key can be decrypted with the private key. And there is no need to exchange keys, thus eliminating the risk of compromising the secrecy of the key.

Now suppose that Susan sends a message encrypted with her private key to you. To decrypt the message, you need the public key. The scenario may seem odd because the public key is not a secret; everyone knows it. But using this method guarantees that the message has not been tampered with and confirms that it indeed comes from Susan. If the message had been modified, you would not be able to decrypt it. The fact that you can decrypt the message using the public key proves that the message has not been modified.

In computing, public key cryptography is a secure way to encrypt information, but it's computationally expensive because it is time-consuming to generate the key pairs and to perform encryption and decryption. Therefore, it's generally used only for encrypting a small amount of sensitive information.

For public key (asymmetric) encryptions, the .NET Framework supports the DSA and RSA algorithms. The RSA algorithm is used in the following AsymmetricEncryption() function. This function takes in two parameters: the string to be encrypted and the public key:

static string AsymmetricEncryption(string str, string publicKey) {
try {
//---Creates a new instance of RSACryptoServiceProvider---
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//---Loads the public key---
RSA.FromXmlString(publicKey);
//---Encrypts the string---
byte[] encryptedStr =
RSA.Encrypt(ASCIIEncoding.ASCII.GetBytes(str), false);
//---Converts the encrypted byte array to string---
return System.Convert.ToBase64String(encryptedStr);
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
return String.Empty;
}
}

The encrypted string is returned as a Base64-encoded string. To decrypt a string encrypted with the public key, define the following AsymmetricDecryption() function. It takes in two parameters (the encrypted string and the private key) and returns the decrypted string.

static string AsymmetricDecryption(string str, string privateKey) {
try {
//---Creates a new instance of RSACryptoServiceProvider---
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//---Loads the private key---
RSA.FromXmlString(privateKey);
//---Decrypts the string---
byte[] DecryptedStr =
RSA.Decrypt(System.Convert.FromBase64String(str), false);
//---Converts the decrypted byte array to string---
return ASCIIEncoding.ASCII.GetString(DecryptedStr);
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
return String.Empty;
}
}

The following code snippet shows how to use the AsymmetricEncryption() and AsymmetricDecryption() functions to encrypt and decrypt a string:

string publicKey, privateKey;
RSACryptoServiceProvider RSA =
new RSACryptoServiceProvider();
//---get public key---
publicKey = RSA.ToXmlString(false);
Console.WriteLine("Public key: " + publicKey);
Console.WriteLine();
//---get private and public key---
privateKey = RSA.ToXmlString(true);
Console.WriteLine("Private key: " + privateKey);
Console.WriteLine();
//---encrypt the string---
string cipherText =
AsymmetricEncryption("C# 2008 Programmer's Reference", publicKey);
Console.WriteLine("Ciphertext: " + cipherText);
Console.WriteLine();
//---decrypt the string---
Console.WriteLine("Original string: " +
AsymmetricDecryption(cipherText, privateKey));
Console.WriteLine();

You can obtain the public and private keys generated by the RSA algorithm by using the ToXmlString() method from the RSACryptoServiceProvider class. This method takes in a Bool variable, and returns a public key if the value false is supplied. If the value true is supplied, it returns both the private and public keys.

Figure 11-8 shows the output.


Figure 11-8


: 1.081. /Cache: 3 / 1