April 14, 2003

Strong Encryption/Decryption and hash implementation in C#

Following code snippet provides methods for strong Encryption/Decryption and hash implementation (as compare to last code)

(Tip: Not better for performance)

/// <summary>
/// Strong Encryption/Decryption methods and hash implementation
/// </summary>
internal class StrongSecurity {

private const int SHA1_HASH_SIZE = 160; // Size of hash without salt. Do not change this size.
private static string HASH_ALGORITHM = "SHA1";// can be "MD5"
private static int PASSWORD_ITERATIONS = 1;
private static int KEY_SIZE = 128;

/// <summary>
/// Decrypt Input Message
/// </summary>
/// <param name="strEncryptedMessage">Encrypted Message</param>
/// <param name="btSalt">Salt</param>
/// <param name="strKey">Key</param>
/// <param name="strHashAlgorithm">Hash Algorithm</param>
/// <returns>Decrypted Message</returns>
internal static string decryptMessage(string strEncryptedMessage, byte[] btSalt,string strKey) {
PasswordDeriveBytes password = null;
RijndaelManaged symmetricKey = null;
ICryptoTransform decryptor = null;
MemoryStream memoryStream = null;
CryptoStream cryptoStream = null;
try{
// Convert strings defining encryption key characteristics into byte arrays.
byte[] btIV = btSalt;

byte[] btEncryptedDataBytes = Convert.FromBase64String(strEncryptedMessage);

//Create password using salt and key
password = new PasswordDeriveBytes(
strKey,
btSalt,
HASH_ALGORITHM,
PASSWORD_ITERATIONS);

// Use the password to generate pseudo-random bytes for the encryption
// key. Specify the size of the key in bytes (instead of bits).
byte[] keyBytes = password.GetBytes(KEY_SIZE / 8);

symmetricKey = new RijndaelManaged();

// Set encryption mode to Cipher Block Chaining (CBC)
symmetricKey.Mode = CipherMode.CBC;

// Generate encryptor from the existing key bytes and IV
decryptor = symmetricKey.CreateDecryptor(
keyBytes,
btIV);

// Memory stream to hold encrypted data.
memoryStream = new MemoryStream(btEncryptedDataBytes);

// Define cryptographic stream
cryptoStream = new CryptoStream(memoryStream,
decryptor,
CryptoStreamMode.Read);

byte[] plainTextBytes = new byte[btEncryptedDataBytes.Length];

// Start decrypting.
int decryptedByteCount = cryptoStream.Read(plainTextBytes,
0,
plainTextBytes.Length);

// Convert decrypted data into a string.
string plainText = Encoding.UTF8.GetString(plainTextBytes,
0,
decryptedByteCount);

// Return decrypted string.
return plainText;
}
finally{
password = null;
symmetricKey = null;
decryptor = null;
memoryStream.Close();
memoryStream = null;
cryptoStream.Close();
cryptoStream = null;
}
}

/// <summary>
/// Encrypts the Message
/// </summary>
/// <param name="strMessage">Message to Encrypt</param>
/// <param name="btSalt">Salt</param>
/// <param name="strKey">Key</param>
/// <param name="strHashAlgorithm">Hash Algorithm</param>
/// <returns>Encrypted Message</returns>
internal static string encryptMessage(string strMessage, byte[] btSalt, string strKey) {
PasswordDeriveBytes password = null;
RijndaelManaged symmetricKey = null;
ICryptoTransform encryptor = null;
MemoryStream memoryStream = null;
CryptoStream cryptoStream = null;

try{
// Convert strings into byte arrays.
byte[] btIV = btSalt;
byte[] btInputData = Encoding.UTF8.GetBytes(strMessage);

// Create password using salt,password phrase
password = new PasswordDeriveBytes(
strKey,
btSalt,
HASH_ALGORITHM,
PASSWORD_ITERATIONS);

// Use the password to generate pseudo-random bytes for the encryption
// key. Specify the size of the key in bytes (instead of bits).
byte[] keyBytes = password.GetBytes(KEY_SIZE / 8);

symmetricKey = new RijndaelManaged();

// Set encryption mode to Cipher Block Chaining (CBC)
symmetricKey.Mode = CipherMode.CBC;

// Generate encryptor from the existing key bytes and IV
encryptor = symmetricKey.CreateEncryptor(
keyBytes,
btIV);

// Memory stream to hold encrypted data.
memoryStream = new MemoryStream();

// Define cryptographic stream
cryptoStream = new CryptoStream(memoryStream,
encryptor,
CryptoStreamMode.Write);
// Start encrypting.
cryptoStream.Write(btInputData, 0, btInputData.Length);

// Finish encrypting.
cryptoStream.FlushFinalBlock();

// Convert our encrypted data from a memory stream into a byte array.
byte[] btEncryptedDataBytes = memoryStream.ToArray();

// Convert encrypted data into a base64-encoded string.
string strEncryptedData = Convert.ToBase64String(btEncryptedDataBytes);

// Return encrypted string.
return strEncryptedData;
}
finally{
password = null;
symmetricKey = null;
encryptor = null;
memoryStream.Close();
memoryStream = null;
cryptoStream.Close();
cryptoStream = null;
}
}

/// <summary>
/// Verifies Hash received from the client
/// </summary>
/// <param name="hashValue">Hash Value to Verify</param>
/// <param name="strKey">Server side Key to generate the hash</param>
/// <returns>If hash is correct then return the salt bytes else null</returns>
internal static byte[] VerifyHash(string strHashValue,string strKey) {
int intHashSizeInBits, intHashSizeInBytes;

// Convert base64-encoded hash value into a byte array.
byte[] btHashWithSalt = Convert.FromBase64String(strHashValue);

// Size of hash without salt.
intHashSizeInBits = SHA1_HASH_SIZE;

// Convert size of hash from bits to bytes.
intHashSizeInBytes = intHashSizeInBits / 8;

// Make sure that the specified hash value is long enough.
if (btHashWithSalt.Length < intHashSizeInBytes)
return null;

// Allocate array to hold original salt bytes retrieved from hash.
byte[] btSalt = new byte[btHashWithSalt.Length -
intHashSizeInBytes];

// Copy salt from the end of the hash to the new array.
for (int i=0; i < btSalt.Length; i++)
btSalt[i] = btHashWithSalt[intHashSizeInBytes + i];

// Compute a new hash string.
string strExpectedHashString =
computeHash(btSalt,strKey);

// If the computed hash matches the specified hash,
// the secrete key must be correct.
if (strHashValue == strExpectedHashString)
return btSalt;
else
return null;
}

/// <summary>
/// Computes hash of the key and salt
/// </summary>
/// <param name="btSalt">Salt</param>
/// <param name="strKey">Key</param>
/// <returns>Computed Hash</returns>
internal static string computeHash(byte [] btSalt, string strKey) {

// Convert plain text into a byte array.
byte[] plainTextBytes = Encoding.UTF8.GetBytes(strKey);

// Allocate array, which will hold plain text and salt.
byte[] plainTextWithSaltBytes =
new byte[plainTextBytes.Length + btSalt.Length];

// Copy plain text bytes into resulting array.
for (int i=0; i < plainTextBytes.Length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];

// Append salt bytes to the resulting array.
for (int i=0; i < btSalt.Length; i++)
plainTextWithSaltBytes[plainTextBytes.Length + i] = btSalt[i];

// Specify hashing algorithm
HashAlgorithm hash = new SHA1Managed();

// Compute hash value of our plain text with appended salt.
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);

// Create array which will hold hash and original salt bytes.
byte[] hashWithSaltBytes = new byte[hashBytes.Length +
btSalt.Length];

// Copy hash bytes into resulting array.
for (int i=0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];

// Append salt bytes to the result.
for (int i=0; i < btSalt.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = btSalt[i];

// Convert result into a base64-encoded string.
string hashValue = Convert.ToBase64String(hashWithSaltBytes);

// Return the result.
return hashValue;
}
}