Click here to Skip to main content
15,896,207 members
Articles / Programming Languages / C#

XCrypt - Encryption and decryption class wrapper

Rate me:
Please Sign up or sign in to vote.
4.86/5 (19 votes)
24 Jun 2013CPOL2 min read 56.4K   2K   77  
This is an alternative for "XCrypt - Encryption and decryption class wrapper".
/* 
  Copyright 2001-2003 Markus Hahn <markus_hahn@gmx.net>
  All rights reserved.
  See Documentation for license details.  
*/


using System;
using System.Text;
using System.Security.Cryptography;


namespace Blowfish_NET
{

	/// <summary>
	///   simple and easy to use string encryption and 
	///   decryption using the Blowfish algorithm
	/// </summary>
	/// <remarks>
	///   As a simple solution for developers who want nothing
	///   more than protect simple strings with a simple key this
	///   class provides such a functionality. 
	///   The password is hashed using the build-in SHA-1 class
	///   of the .NET framework. Additionally the random number
	///   generation for the CBC initialization vector (IV) and
	///   the BASE64 encoding and decoding is also taken from
	///   the system security services.
	/// </remarks>
	public class BlowfishSimple
	{
		BlowfishCBC m_bfc;

		UTF8Encoding m_ue;
		RNGCryptoServiceProvider m_rng;

		String m_sKeyChecksum;


		/// <summary>
		///   secure checksum of the key used
		/// </summary>
		/// <remarks> 
		///   Store this checksum somewhere to be able to check later on
		///   by calling the VerifyKey() method to see if a key matches
		///   for decryption or not.
		/// </remarks> 
		public String KeyChecksum
		{
			get
			{
				return m_sKeyChecksum;
			} 
		}


		static byte[] TransformKey
			(String sKey)
		{
			UTF8Encoding ue = new UTF8Encoding();
			return ue.GetBytes(sKey);
		}


		static byte[] CalcKeyChecksum
			(byte[] salt,
			byte[] key)
		{
			byte[] keyCombo = new byte[20 + key.Length];

			Array.Copy(salt, 0, keyCombo, 0, 20);
			Array.Copy(key, 0, keyCombo, 20, key.Length);

			HashAlgorithm sha = new SHA1CryptoServiceProvider();
			byte[] result = sha.ComputeHash(keyCombo);
			sha = null; 

			Array.Clear(keyCombo, 0, keyCombo.Length);

			return result;
		}
  

		/// <summary>
		///   to verify a key before it is used for decryption
		/// </summary>
		/// <remarks> 
		///   By passing the current key available and a key checksum
		///   retrieved during the former encryption process you can
		///   check (with a propability of the SHA-1 collision safety)
		///   that this key will decrypt the data correctly. 
		/// </remarks> 
		/// <param name="sKey"> 
		///   the key to verify 
		/// </param> 
		/// <param name="sKeyChecksum"> 
		///   the original key checksum
		/// </param> 
		/// <returns> 
		///   true: key seems to be the right one / false: no match
		/// </returns> 
		public static bool VerifyKey
			(String sKey,
 			String sKeyChecksum)
		{
			// decode what we got
			byte[] checksumCombo = Convert.FromBase64String(sKeyChecksum);
 
			// correct size?     
			if (40 != checksumCombo.Length) return false;

			// calculate the new checksum
			byte[] keyRaw = TransformKey(sKey);
			byte[] checksum = CalcKeyChecksum(checksumCombo, keyRaw);
 
			// is it equal to the existing one?
			int nI = 0;
			while (nI < checksum.Length)
			{
				if (checksum[nI] != checksumCombo[checksum.Length + nI]) break;
				nI++;
			} 
   
			return (nI == checksum.Length);
		}


		/// <summary>
		///   standard constructor
		/// </summary>
		/// <param name="sKey"> 
		///   the string which is used as the key material, internally 
		///   a UTF8 representation is used, hashed with SHA-1, thus
		///   we use a 160bit key (which does not make weak keys safe!)
		/// </param>
		public BlowfishSimple
			(String sKey)
		{
			m_ue = new UTF8Encoding();

			byte[] keyRaw = TransformKey(sKey);

			HashAlgorithm sha = new SHA1CryptoServiceProvider();
			byte[] key = sha.ComputeHash(keyRaw);
			sha = null;

			m_rng = new RNGCryptoServiceProvider();

			byte[] checksumSalt = new byte[20];
			m_rng.GetBytes(checksumSalt); 

			byte[] checksum = CalcKeyChecksum(checksumSalt, keyRaw);
			byte[] checksumCombo = new byte[checksumSalt.Length + checksum.Length];
			Array.Copy(checksumSalt, 0, checksumCombo, 0, checksumSalt.Length);
			Array.Copy(checksum, 0, checksumCombo, checksumSalt.Length, checksum.Length);

			m_sKeyChecksum = Convert.ToBase64String(checksumCombo);

			// (start with a dummy IV)    
			byte[] iv = new byte[Blowfish.BLOCKSIZE];
 
			m_bfc = new BlowfishCBC(key, iv);

			Array.Clear(keyRaw, 0, keyRaw.Length);
			Array.Clear(key, 0, key.Length);
			Array.Clear(iv, 0, iv.Length);
		}


		/// <summary>
		///   encrypts a string
		/// </summary>
		/// <remarks>
		///   for efficiency the given string will be UTF8 encoded
		///   and padded to the next block border, the CBC IV plus
		///   the encrypted data will then be BASE64 encoded and
		///   returned as an string
		/// </remarks>
		/// <param name="sPlainText"> 
		///   the string to encrypt
		/// </param> 
		/// <returns> 
		///   the encrypted string 
		/// </returns> 
		public String Encrypt
			(String sPlainText)
		{
			int nI;

			// convert string
			byte[] ueData = m_ue.GetBytes(sPlainText);

			// prepare the input and the output buffer
			int nOrigLen = ueData.Length;
			int nLen = nOrigLen;

			// (we need aligned and "pad-able" buffers) 
			int nMod = nLen % Blowfish.BLOCKSIZE;
			nLen = (nLen - nMod) + Blowfish.BLOCKSIZE;

			// allocate the input buffer
			byte[] inBuf = new byte[nLen];

			// copy the data and do the padding
			Array.Copy(ueData, 0, inBuf, 0, nOrigLen);
    
			nI = nLen - (Blowfish.BLOCKSIZE - nMod);
			while (nI < nLen) inBuf[nI++] = (byte)nMod;
   
			// allocate the output buffer
			byte[] outBuf = new byte[inBuf.Length + Blowfish.BLOCKSIZE];

			// create and set a new IV
			byte[] iv = new byte[Blowfish.BLOCKSIZE];
			m_rng.GetBytes(iv);
			m_bfc.Iv = iv;

			// do the encryption
			m_bfc.Encrypt(inBuf, 
				outBuf, 
				0, 
				Blowfish.BLOCKSIZE, 
				inBuf.Length);

			// copy the IV
			Array.Copy(iv, 0, outBuf, 0, Blowfish.BLOCKSIZE);  

			// BASE64 encode the whole thing
			String sResult = Convert.ToBase64String(outBuf);
 
			// finally clear the plaintext buffer
			Array.Clear(inBuf, 0, inBuf.Length);

			return sResult;
		}


		/// <summary>
		///   decrypts a string
		/// </summary>
		/// <remarks>
		///   The string has to be decrypted with the same key, 
		///   otherwise the result will be just garbage. If you
		///   want to check if the key is the right one use the
		///   VerifyKey() method.
		/// </remarks>
		/// <param name="sCipherText"> 
		///   the string to decrypt
		/// </param> 
		/// <returns> 
		///   the decrypted (original) string (null on error)
		/// </returns> 
		public String Decrypt
			(String sCipherText)
		{
			byte[] cdata = Convert.FromBase64String(sCipherText);

			if (cdata.Length < Blowfish.BLOCKSIZE) return null;

			// set the cbc IV
			m_bfc.Iv = cdata;
  
			// decrypt the data
			byte[] outBuf = new byte[cdata.Length];

			int nDataAbs = cdata.Length - Blowfish.BLOCKSIZE; 
			nDataAbs /= Blowfish.BLOCKSIZE;  
			nDataAbs *= Blowfish.BLOCKSIZE;  

			m_bfc.Decrypt(cdata, 
				outBuf, 
				Blowfish.BLOCKSIZE, 
				0, 
				nDataAbs);

			// calculate the original size
			int nOrigSize = nDataAbs - 
				Blowfish.BLOCKSIZE + 
				outBuf[nDataAbs - 1];
 
			// UTF8 decode the result and pass it back

			return m_ue.GetString(outBuf, 0, nOrigSize);
		}


		/// <summary>
		///   securely invalidates this instance
		/// </summary>
		/// <remarks>
		///   Removes all sensitive data, after this call it is 
		///   not possible to encrypt or data anymore properly!  
		/// </remarks>
		/// <returns>
		///   self reference
		/// </returns>
		public BlowfishSimple Burn()
		{
			m_bfc.Burn();
			return this;
		}
	}


}	// (end of namespace)

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
India India
Vasudevan Deepak Kumar is from Chennai, India who has been in the programming career since 1994, when he was 15 years old. He has his Bachelors of Engineering (in Computer Science and Engineering) from Vellore Engineering College. He also has a MBA in Systems from Alagappa University, Karaikudi, India.
He started his programming career with GWBasic and then in his college was involved in developing programs in Fortran, Cobol, C++. He has been developing in Microsoft technologies like ASP, SQLServer 2000.
His current focus is ASP.NET, C#, VB.NET, PHP, SQL Server and MySQL. In his past-time, he listens to polite Carnatic Music. But the big question is that with his current Todolist backlog, does he get some past time?

Comments and Discussions