Introduction
While going through lot of articles on MSDN and on other sites, I wasn't able to get enough information in one go. So, I thought to write up an article which will cover up all the cryptography and that too with an example.
Description
The .NET Framework provides the following classes that implement:
- Secret key encryption algorithms: Private-key algorithms are relatively fast and can be used to encrypt and decrypt large streams of data. Private-key algorithms are known as block ciphers because they encrypt data one block at a time. A block cipher will encrypt the same input block into the same output block based on the algorithm and key. If anything were known about the structure of the data, patterns could be detected and the key could possibly be reverse engineered. To combat this, the classes in the .NET Framework use a process known as chaining where information from the previous block is used in encrypting the current block. This helps prevent the key from being discovered. It requires an initialization vector (IV) be given to encrypt the first block of data.
- DESCryptoServiceProvider - Data Encryption Standard (DES) algorithm encrypts and decrypts data in 64-bit blocks, using a 64-bit key. Even though the key is 64-bit, the effective key strength is only 56-bits. There are hardware devices advanced enough that they can search all possible DES keys in a reasonable amount of time. This makes the DES algorithm breakable, and the algorithm is considered somewhat obsolete.
- RC2CryptoServiceProvider - RC2 is a variable key-size block cipher. The key size can vary from 8-bit up to 64-bits for the key. It was specifically designed as a more secure replacement to DES. The processing speed is two to three times faster than DES. However, the RC2CryptoServiceProvider available in the .NET Framework is limited to 8 characters, or a 64-bit key. The 8-character limitation makes it susceptible to the same brute force attack as DES.
- RijndaelManaged - Rijndael algorithm, one of the Advanced Encryption Standard (AES) algorithms, was designed as a replacement for the DES algorithms. The key strength is stronger than DES and was designed to out perform DES. The key can vary in length from 128, 192, to 256 bits in length.
- TripleDESCryptoServiceProvider - TripleDES algorithm uses three successive iterations of the DES algorithm. The algorithm uses either two or three keys. Just as the DES algorithm, the key size is 64-bit per key with an effective key strength of 56-bit per key. The TripleDES algorithm was designed to fix the shortcomings of the DES algorithm, but the three iterations result in a processing speed three times slower than DES alone.
Algo Name |
Algo Class (Abstract) |
Valid Key Size (bit) |
Default Key Size (Bit) |
Default Implementation Class |
DES |
DES |
64 |
64 |
DESCryptoServiceProvider |
TripleDES |
TripleDES |
128, 192 |
192 |
TripleDESCryptoServiceProvider |
RC2 |
RC2 |
40-128 |
128 |
RC2CryptoServiceProvider |
RijnDael |
RijnDael |
128, 192, 256 |
256 |
RijnDaelManaged |
- Public-key encryption algorithms: Public-key encryption has a much larger keyspace, or range of possible values for the key, and is therefore less susceptible to exhaustive attacks that try every possible key. A public key is easy to distribute because it does not have to be secured. Public-key algorithms can be used to create digital signatures to verify the identity of the sender of data. However, public-key algorithms are extremely slow (compared to secret-key algorithms) and are not designed to encrypt large amounts of data. Public-key algorithms are useful only for transferring very small amounts of data. Typically, public-key encryption is used to encrypt a key and IV to be used by a secret-key algorithm. After the key and IV are transferred, then secret-key encryption is used for the remainder of the session.
- Digital signature algorithms (Hash): The .NET Framework provides support for the following hash algorithms.
- HMACSHA1 - An HMAC can be used to determine whether a message sent over an insecure channel has been tampered with, provided that the sender and receiver share a secret key. The sender computes the HMAC for the original data and sends both the original data and HMAC as a single message. The receiver recomputes the HMAC on the received message and checks that the computed HMAC matches the transmitted HMAC.
Any change to the data or the HMAC will result in a mismatch, because knowledge of the secret key is required to change the message and reproduce the correct HMAC. Therefore, if the codes match, the message is authenticated.
HMACSHA1 accepts keys of any size, and produces a hash sequence of length 20 bytes.
- MACTripleDES - A MAC can be used to determine whether a message sent over an insecure channel has been tampered with, provided that the sender and receiver share a secret key. The sender computes the MAC for the original data and sends both as a single message. The receiver recomputes the MAC on the received message, and checks that the computed MAC matches the transmitted MAC.
Any change to the data or the MAC will result in a mismatch, because knowledge of the secret key is required to change the message and reproduce the correct MAC. Therefore, if the codes match, the message is authenticated.
MACTripleDES uses a key of length 8, 16 or 24 bytes, and produces a hash sequence of length 8 bytes.
- MD5CryptoServiceProvider
- SHA1Managed - The hash is used as a unique value of fixed size representing a large amount of data. Hashes of two sets of data should match if and only if the corresponding data also matches. Small changes to the data result in large unpredictable changes in the hash.
This is a purely managed implementation of SHA1 that does not wrap CAPI.
The hash size for the SHA1Managed algorithm is 160 bits.
- SHA256Managed - The hash size for the SHA256Managed algorithm is 256 bits.
- SHA384Managed - The hash size for the SHA384Managed algorithm is 384 bits.
This is an abstract class. The only implementation of this class is SHA384Managed.
- SHA512Managed - The hash size for the SHA512Managed algorithm is 512 bits.
Till now we have studied a lot about the Cryptography and the support given in .NET Framework 1.1. Let's take up a example for Encryption/Decryption of text.
Following is the Code of Encryption with �Rijndael� Algorithm. This is a class library developed in C#. You can use this code as a plug-in and use it for Encryption, Decryption and Hashing.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Diagnostics;
namespace JackTheGreat.Encryption.Cryptography
{
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Diagnostics;
namespace JackTheGreat.Encryption.Cryptography
{
public class CryptoLib
{
public System.Security.Cryptography.SymmetricAlgorithm mCryptProv;
public System.Diagnostics.EventLog objEventLog;
#region "Encrypt Function"
public string Encrypt(string EncryptProvider, string EncryptStr,
int BlckSize, int KySize, string Key, string IV)
{
if(EncryptProvider.Length<=0)
{
return("Pass an Algo to use to Encrypt.");
}
try
{
mCryptProv = SymmetricAlgorithm.Create(EncryptProvider);
mCryptProv.Mode = CipherMode.CBC;
}
catch(Exception ee)
{
return("Exception : " + ee.Message);
}
try
{
mCryptProv.BlockSize = BlckSize;
mCryptProv.KeySize = KySize;
}
catch
{
string mStr;
mStr = mCryptProv.BlockSize.ToString();
mStr += "Invalid Block or Key Size.Valid Sizes are : ";
mStr += "Block Size = " +
mCryptProv.LegalBlockSizes[0].MinSize.ToString() +
" - " + mCryptProv.LegalBlockSizes[0].MaxSize.ToString() +
" With Increment of " +
mCryptProv.LegalBlockSizes[0].SkipSize.ToString();
mStr += "Key Size = " +
mCryptProv.LegalKeySizes[0].MinSize.ToString() +
" - " + mCryptProv.LegalKeySizes[0].MaxSize.ToString() +
" With Increment of " +
mCryptProv.LegalKeySizes[0].SkipSize.ToString();
return(mStr);
}
try
{
byte[] thisKey = stringtoByte(Key,KySize);
byte[] resultKey = HashOfByteArray(thisKey);
int loopTill=0;
if(BlckSize==128)loopTill=16;
if(BlckSize==192)loopTill=24;
if(BlckSize==256)loopTill=32;
byte[] BlockKey = new byte[loopTill];
for(int loop=0; loop<loopTill; loop++ )
BlockKey[loop] = resultKey[loop];
byte[] thisIV = stringtoByte(IV,BlckSize);
byte[] resultIV = HashOfByteArray(thisIV);
byte[] BlockIV = new byte[loopTill];
for( int loop=0; loop<loopTill; loop++ )
BlockIV[loop] = resultIV[loop];
ICryptoTransform mEncryptor =
mCryptProv.CreateEncryptor(BlockKey, BlockIV);
System.IO.MemoryStream mMemStr = new MemoryStream();
CryptoStream mCryptStr= new CryptoStream(mMemStr,
mEncryptor, CryptoStreamMode.Write);
byte[] bInput = stringtoByte(EncryptStr,512);
mCryptStr.Write(bInput, 0, bInput.Length);
mCryptStr.FlushFinalBlock();
byte[] bOutput = mMemStr.GetBuffer();
int MemLength = System.Convert.ToInt32(mMemStr.Length);
mCryptStr.Close();
mMemStr.Close();
mEncryptor.Dispose();
return Convert.ToBase64String(bOutput,0,MemLength);
}
catch(System.Security.Cryptography.CryptographicException ee)
{
objEventLog = new System.Diagnostics.EventLog();
objEventLog.Source = "SMP-CryptoGraphy";
objEventLog.WriteEntry("Exception Occured in Encryption Process." +
" Error is = " +
ee.Message ,System.Diagnostics.EventLogEntryType.Error);
objEventLog.Dispose();
return("");
}
}
#endregion
#region "Decrypt Function"
public string Decrypt(string EncryptProvider, string EncryptStr,
int BlckSize, int KySize, string Key, string IV)
{
if(EncryptProvider.Length<=0)
{
return("Pass an Algo to use to Encrypt.");
}
try
{
mCryptProv = SymmetricAlgorithm.Create(EncryptProvider);
mCryptProv.Mode = CipherMode.CBC;
}
catch(Exception ee)
{
return("Exception-1: " + ee.Message);
}
try
{
mCryptProv.BlockSize = BlckSize;
mCryptProv.KeySize = KySize;
}
catch
{
string mStr;
mStr = mCryptProv.BlockSize.ToString();
mStr += "Invalid Block or Key Size.Valid Sizes are : ";
mStr += "Block Size = " +
mCryptProv.LegalBlockSizes[0].MinSize.ToString() +
" - " + mCryptProv.LegalBlockSizes[0].MaxSize.ToString() +
" With Increment of " +
mCryptProv.LegalBlockSizes[0].SkipSize.ToString();
mStr += "Key Size = " +
mCryptProv.LegalKeySizes[0].MinSize.ToString() +
" - " + mCryptProv.LegalKeySizes[0].MaxSize.ToString() +
" With Increment of " +
mCryptProv.LegalKeySizes[0].SkipSize.ToString();
return(mStr);
}
try
{
byte[] thisKey = stringtoByte(Key,KySize);
byte[] resultKey = HashOfByteArray(thisKey);
int loopTill=0;
if(BlckSize==128)loopTill=16;
if(BlckSize==192)loopTill=24;
if(BlckSize==256)loopTill=32;
byte[] BlockKey = new byte[loopTill];
for(int loop=0; loop<loopTill; loop++ )
BlockKey[loop] = resultKey[loop];
byte[] thisIV = stringtoByte(IV,BlckSize);
byte[] resultIV = HashOfByteArray(thisIV);
byte[] BlockIV = new byte[loopTill];
for( int loop=0; loop<loopTill; loop++ )
BlockIV[loop] = resultIV[loop];
ICryptoTransform mDecrypt =
mCryptProv.CreateDecryptor(thisKey, thisIV);
byte[] bInput = System.Convert.FromBase64String(EncryptStr);
System.IO.MemoryStream ms =
new System.IO.MemoryStream(bInput, 0, bInput.Length);
CryptoStream mCSReader =
new CryptoStream(ms, mDecrypt, CryptoStreamMode.Read);
System.IO.StreamReader sr = new System.IO.StreamReader(mCSReader);
mCSReader.Close();
ms.Close();
mDecrypt.Dispose();
return sr.ReadToEnd();
}
catch(System.Exception ee)
{
return("Exception in processing " + ee.Message);
}
}
#endregion
#region "Hashing of a BYTE Array by using SHA512Managed -" +
" return value is a Byte Array"
private byte[] HashOfByteArray(byte[] ByteArray)
{
SHA512Managed sha512Key = new SHA512Managed();
Sha512Key.ComputeHash(ByteArray);
return(sha512Key.Hash);
}
#endregion
#region "Hashing of a String by using SHA512Managed - " +
"return value is a Base64 string"
public string HashOfString(string PlainText)
{
try
{
int thisSize = PlainText.Length*8;
int temp;
if(PlainText.Length<1)
{
return("Please enter a Valid String to Hash. String passed is " +
PlainText.ToString());
}
byte[] thisStr = new byte[thisSize];
int lastBound = PlainText.Length;
if(lastBound > thisSize)lastBound = thisSize;
for(temp = 0;temp<=lastBound - 1;temp++)
{
thisStr[temp] = Convert.ToByte(PlainText[temp]);
}
SHA512Managed sha512Key = new SHA512Managed();
Sha512Key.ComputeHash(thisStr);
return(Convert.ToBase64String(sha512Key.Hash));
}
catch(System.Security.Cryptography.CryptographicException ee)
{
return("Exception Occured in Hashing Process. Error is = " +
ee.Message);
}
}
#endregion
#region "String to a byte array conversion -" +
" return value is a BYTE array"
private byte[] stringtoByte(string textdata,int keySize)
{
int thisSize = (keySize / 8);
int temp;
byte[] returnByteArray = new byte[thisSize];
int lastBound = textdata.Length;
if(lastBound > thisSize)lastBound = thisSize;
for(temp = 0;temp<=lastBound - 1;temp++)
{
returnByteArray[temp] = Convert.ToByte(textdata[temp]);
}
return(returnByteArray);
}
#endregion
}
}
public class CryptoLib
{
public System.Security.Cryptography.SymmetricAlgorithm mCryptProv;
public System.Diagnostics.EventLog objEventLog;
#region "Encrypt Function"
public string Encrypt(string EncryptProvider, string EncryptStr,
int BlckSize, int KySize, string Key, string IV)
{
if(EncryptProvider.Length<=0)
{
return("Pass an Algo to use to Encrypt.");
}
try
{
mCryptProv = SymmetricAlgorithm.Create(EncryptProvider);
mCryptProv.Mode = CipherMode.CBC;
}
catch(Exception ee)
{
return("Exception : " + ee.Message);
}
try
{
mCryptProv.BlockSize = BlckSize;
mCryptProv.KeySize = KySize;
}
catch
{
string mStr;
mStr = mCryptProv.BlockSize.ToString();
mStr += "Invalid Block or Key Size.Valid Sizes are : ";
mStr += "Block Size = " +
mCryptProv.LegalBlockSizes[0].MinSize.ToString() +
" - " + mCryptProv.LegalBlockSizes[0].MaxSize.ToString() +
" With Increment of " +
mCryptProv.LegalBlockSizes[0].SkipSize.ToString();
mStr += "Key Size = " +
mCryptProv.LegalKeySizes[0].MinSize.ToString() +
" - " + mCryptProv.LegalKeySizes[0].MaxSize.ToString() +
" With Increment of " +
mCryptProv.LegalKeySizes[0].SkipSize.ToString();
return(mStr);
}
try
{
byte[] thisKey = stringtoByte(Key,KySize);
byte[] resultKey = HashOfByteArray(thisKey);
int loopTill=0;
if(BlckSize==128)loopTill=16;
if(BlckSize==192)loopTill=24;
if(BlckSize==256)loopTill=32;
byte[] BlockKey = new byte[loopTill];
for(int loop=0; loop<loopTill; loop++ )
BlockKey[loop] = resultKey[loop];
byte[] thisIV = stringtoByte(IV,BlckSize);
byte[] resultIV = HashOfByteArray(thisIV);
byte[] BlockIV = new byte[loopTill];
for( int loop=0; loop<loopTill; loop++ )
BlockIV[loop] = resultIV[loop];
ICryptoTransform mEncryptor =
mCryptProv.CreateEncryptor(BlockKey, BlockIV);
System.IO.MemoryStream mMemStr = new MemoryStream();
CryptoStream mCryptStr=
new CryptoStream(mMemStr, mEncryptor, CryptoStreamMode.Write);
byte[] bInput = stringtoByte(EncryptStr,512);
mCryptStr.Write(bInput, 0, bInput.Length);
mCryptStr.FlushFinalBlock();
byte[] bOutput = mMemStr.GetBuffer();
int MemLength = System.Convert.ToInt32(mMemStr.Length);
mCryptStr.Close();
mMemStr.Close();
mEncryptor.Dispose();
return Convert.ToBase64String(bOutput,0,MemLength);
}
catch(System.Security.Cryptography.CryptographicException ee)
{
objEventLog = new System.Diagnostics.EventLog();
objEventLog.Source = "SMP-CryptoGraphy";
objEventLog.WriteEntry("Exception Occured in Encryption " +
"Process. Error is = " + ee.Message,
System.Diagnostics.EventLogEntryType.Error);
objEventLog.Dispose();
return("");
}
}
#endregion
#region "Decrypt Function"
public string Decrypt(string EncryptProvider, string EncryptStr,
int BlckSize, int KySize, string Key, string IV)
{
if(EncryptProvider.Length<=0)
{
return("Pass an Algo to use to Encrypt.");
}
try
{
mCryptProv = SymmetricAlgorithm.Create(EncryptProvider);
mCryptProv.Mode = CipherMode.CBC;
}
catch(Exception ee)
{
return("Exception-1: " + ee.Message);
}
try
{
mCryptProv.BlockSize = BlckSize;
mCryptProv.KeySize = KySize;
}
catch
{
string mStr;
mStr = mCryptProv.BlockSize.ToString();
mStr += "Invalid Block or Key Size.Valid Sizes are : ";
mStr += "Block Size = " +
mCryptProv.LegalBlockSizes[0].MinSize.ToString() +
" - " + mCryptProv.LegalBlockSizes[0].MaxSize.ToString() +
" With Increment of " +
mCryptProv.LegalBlockSizes[0].SkipSize.ToString();
mStr += "Key Size = " +
mCryptProv.LegalKeySizes[0].MinSize.ToString() +
" - " + mCryptProv.LegalKeySizes[0].MaxSize.ToString() +
" With Increment of " +
mCryptProv.LegalKeySizes[0].SkipSize.ToString();
return(mStr);
}
try
{
byte[] thisKey = stringtoByte(Key,KySize);
byte[] resultKey = HashOfByteArray(thisKey);
int loopTill=0;
if(BlckSize==128)loopTill=16;
if(BlckSize==192)loopTill=24;
if(BlckSize==256)loopTill=32;
byte[] BlockKey = new byte[loopTill];
for(int loop=0; loop<loopTill; loop++ )
BlockKey[loop] = resultKey[loop];
byte[] thisIV = stringtoByte(IV,BlckSize);
byte[] resultIV = HashOfByteArray(thisIV);
byte[] BlockIV = new byte[loopTill];
for( int loop=0; loop<loopTill; loop++ )
BlockIV[loop] = resultIV[loop];
ICryptoTransform mDecrypt =
mCryptProv.CreateDecryptor(thisKey, thisIV);
byte[] bInput = System.Convert.FromBase64String(EncryptStr);
System.IO.MemoryStream ms =
new System.IO.MemoryStream(bInput, 0, bInput.Length);
CryptoStream mCSReader =
new CryptoStream(ms, mDecrypt, CryptoStreamMode.Read);
System.IO.StreamReader sr = new System.IO.StreamReader(mCSReader);
mCSReader.Close();
ms.Close();
mDecrypt.Dispose();
return sr.ReadToEnd();
}
catch(System.Exception ee)
{
return("Exception in processing " + ee.Message);
}
}
#endregion
#region "Hashing of a BYTE Array by using SHA512Managed -" +
" return value is a Byte Array"
private byte[] HashOfByteArray(byte[] ByteArray)
{
SHA512Managed sha512Key = new SHA512Managed();
Sha512Key.ComputeHash(ByteArray);
return(sha512Key.Hash);
}
#endregion
#region "Hashing of a String by using SHA512Managed - " +
"return value is a Base64 string"
public string HashOfString(string PlainText)
{
try
{
int thisSize = PlainText.Length*8;
int temp;
if(PlainText.Length<1)
{
return("Please enter a Valid String to Hash. String passed is " +
PlainText.ToString());
}
byte[] thisStr = new byte[thisSize];
int lastBound = PlainText.Length;
if(lastBound > thisSize)lastBound = thisSize;
for(temp = 0;temp<=lastBound - 1;temp++)
{
thisStr[temp] = Convert.ToByte(PlainText[temp]);
}
SHA512Managed sha512Key = new SHA512Managed();
Sha512Key.ComputeHash(thisStr);
return(Convert.ToBase64String(sha512Key.Hash));
}
catch(System.Security.Cryptography.CryptographicException ee)
{
return("Exception Occured in Hashing Process. Error is = " +
ee.Message);
}
}
#endregion
#region "String to a byte array conversion - " +
"return value is a BYTE array"
private byte[] stringtoByte(string textdata,int keySize)
{
int thisSize = (keySize / 8);
int temp;
byte[] returnByteArray = new byte[thisSize];
int lastBound = textdata.Length;
if(lastBound > thisSize)lastBound = thisSize;
for(temp = 0;temp<=lastBound - 1;temp++)
{
returnByteArray[temp] = Convert.ToByte(textdata[temp]);
}
return(returnByteArray);
}
#endregion
}
}