Click here to Skip to main content
15,894,646 members
Articles / Mobile Apps

ForestPad - a method for storing and retrieving textual information

Rate me:
Please Sign up or sign in to vote.
3.85/5 (14 votes)
6 Jun 2004CPOL11 min read 131.3K   404   30  
Three applications: PocketPC, Windows Desktop, and a Web Service collaborate to syncronize your textual information
/*
 * this code taken from a newsgroup and modified...TLR
 * 
 * 
 * 
	Crypto class
	--
	Uses crypto API functions to encrypt and decrypt data. A passphrase 
	string is used to create a 128-bit hash that is used to create a 
	40-bit crypto key. The same key is required to encrypt and decrypt 
	the data.
*/

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;

namespace ForestPadUtilities
{
	/// <summary>
	/// Encrypts and decrypts data using the crypto APIs.
	/// </summary>
	public class CryptoCE
	{
		// API functions
		private class WinApi
		{
			#region Crypto API imports
		
			private const uint ALG_CLASS_HASH = (4 << 13);
			private const uint ALG_TYPE_ANY = (0);
			private const uint ALG_CLASS_DATA_ENCRYPT = (3 << 13);
			private const uint ALG_TYPE_STREAM = (4 << 9);
			private const uint ALG_TYPE_BLOCK = (3 << 9);

			private const uint ALG_SID_DES = 1;
			private const uint ALG_SID_RC4 = 1;
			private const uint ALG_SID_RC2 = 2;
			private const uint ALG_SID_MD5 = 3;

			public const string MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0";
			
			public const uint PROV_RSA_FULL = 1;
			public const uint CRYPT_VERIFYCONTEXT = 0xf0000000;
			public const uint CRYPT_EXPORTABLE = 0x00000001;

			public static readonly uint CALG_MD5 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5);
			public static readonly uint CALG_DES = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DES);
			public static readonly uint CALG_RC2 = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_RC2);
			public static readonly uint CALG_RC4 = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_RC4);

			const string CryptDll = "coredll.dll";
			const string KernelDll = "coredll.dll";
			
			[DllImport(CryptDll)] 
			public static extern bool CryptAcquireContext(
				ref IntPtr phProv, string pszContainer, string pszProvider,
				uint dwProvType, uint dwFlags);

			[DllImport(CryptDll)] 
			public static extern bool CryptReleaseContext( 
				IntPtr hProv, uint dwFlags);

			[DllImport(CryptDll)] 
			public static extern bool CryptDeriveKey(
				IntPtr hProv, uint Algid, IntPtr hBaseData, 
				uint dwFlags, ref IntPtr phKey);
				
			[DllImport(CryptDll)] 
			public static extern bool CryptCreateHash(
				IntPtr hProv, uint Algid, IntPtr hKey, 
				uint dwFlags, ref IntPtr phHash);

			[DllImport(CryptDll)] 
			public static extern bool CryptHashData(
				IntPtr hHash, byte[] pbData, 
				uint dwDataLen, uint dwFlags);
				
			[DllImport(CryptDll)] 
			public static extern bool CryptEncrypt(
				IntPtr hKey, IntPtr hHash, bool Final, uint dwFlags, 
				byte[] pbData, ref uint pdwDataLen, uint dwBufLen);

			[DllImport(CryptDll)] 
			public static extern bool CryptDecrypt(
				IntPtr hKey, IntPtr hHash, bool Final, uint dwFlags, 
				byte[] pbData, ref uint pdwDataLen);

			[DllImport(CryptDll)] 
			public static extern bool CryptDestroyHash(IntPtr hHash);

			[DllImport(CryptDll)] 
			public static extern bool CryptDestroyKey(IntPtr hKey);

			#endregion

			#region Error reporting imports
	
			public const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

			[DllImport(KernelDll)]
			public static extern uint GetLastError();

			[DllImport(KernelDll)]
			public static extern uint FormatMessage(
				uint dwFlags, string lpSource, uint dwMessageId,
				uint dwLanguageId, StringBuilder lpBuffer, uint nSize,
				string [] Arguments);

			#endregion				
		}
	
		// all static methods
		private CryptoCE()
		{
		}
		
		/// <summary>
		/// Encrypt data. Use passphrase to generate the encryption key. 
		/// Returns a byte array that contains the encrypted data.
		/// </summary>
		static public byte[] Encrypt(string passphrase, byte[] data)
		{
			// holds encrypted data
			byte[] buffer = null;		

			// crypto handles
			IntPtr hProv = IntPtr.Zero;
			IntPtr hKey = IntPtr.Zero;

			try
			{
				// get crypto provider, specify the provider (3rd argument)
				// instead of using default to ensure the same provider is 
				// used on client and server
				if (!WinApi.CryptAcquireContext(ref hProv, null, WinApi.MS_DEF_PROV, 
					WinApi.PROV_RSA_FULL, WinApi.CRYPT_VERIFYCONTEXT))
					Failed("CryptAcquireContext");
				
				// generate encryption key from passphrase
				hKey = GetCryptoKey(hProv, passphrase);

				// determine how large of a buffer is required
				// to hold the encrypted data
				uint dataLength = (uint)data.Length;
				uint bufLength = (uint)data.Length;
				if (!WinApi.CryptEncrypt(hKey, IntPtr.Zero, true, 
					0, null, ref dataLength, bufLength))
					Failed("CryptEncrypt");
				
				// allocate and fill buffer with encrypted data
				buffer = new byte[dataLength];
				Buffer.BlockCopy(data, 0, buffer, 0, data.Length);
				
				dataLength = (uint)data.Length;
				bufLength = (uint)buffer.Length;
				if (!WinApi.CryptEncrypt(hKey, IntPtr.Zero, true, 
					0, buffer, ref dataLength, bufLength))
					Failed("CryptEncrypt");
			}
			
			finally
			{
				// release crypto handles
				if (hKey != IntPtr.Zero)
					WinApi.CryptDestroyKey(hKey);

				if (hProv != IntPtr.Zero)
					WinApi.CryptReleaseContext(hProv, 0);
			}
			
			return buffer;
		}


		/// <summary>
		/// Decrypt data. Use passphrase to generate the encryption key. 
		/// Returns a byte array that contains the decrypted data.
		/// </summary>
		static public byte[] Decrypt(string passphrase, byte[] data)
		{
			// make a copy of the encrypted data
			byte[] dataCopy = data.Clone() as byte[];
			
			// holds the decrypted data
			byte[] buffer = null;
			
			// crypto handles
			IntPtr hProv = IntPtr.Zero;
			IntPtr hKey = IntPtr.Zero;
			
			try
			{
				// get crypto provider, specify the provider (3rd argument)
				// instead of using default to ensure the same provider is 
				// used on client and server
				if (!WinApi.CryptAcquireContext(ref hProv, null, WinApi.MS_DEF_PROV, 
					WinApi.PROV_RSA_FULL, WinApi.CRYPT_VERIFYCONTEXT))
					Failed("CryptAcquireContext");
			
				// generate encryption key from the passphrase
				hKey = GetCryptoKey(hProv, passphrase);

				// decrypt the data
				uint dataLength = (uint)dataCopy.Length;
				if (!WinApi.CryptDecrypt(hKey, IntPtr.Zero, true, 
					0, dataCopy, ref dataLength))
					Failed("CryptDecrypt");
				
				// copy to a buffer that is returned to the caller
				// the decrypted data size might be less then
				// the encrypted size
				buffer = new byte[dataLength];
				Buffer.BlockCopy(dataCopy, 0, buffer, 0, (int)dataLength);
			}
			
			finally
			{
				// release crypto handles
				if (hKey != IntPtr.Zero)
					WinApi.CryptDestroyKey(hKey);

				if (hProv != IntPtr.Zero)
					WinApi.CryptReleaseContext(hProv, 0);
			}
			
			return buffer;
		}
		

		/// <summary>
		/// Create a crypto key form a passphrase. This key is 
		/// used to encrypt and decrypt data.
		/// </summary>
		static private IntPtr GetCryptoKey(IntPtr hProv, string passphrase)
		{
			// crypto handles
			IntPtr hHash = IntPtr.Zero;
			IntPtr hKey = IntPtr.Zero;
			
			try
			{
				// create 128 bit hash object
				if (!WinApi.CryptCreateHash(hProv, 
					WinApi.CALG_MD5, IntPtr.Zero, 0, ref hHash))
					Failed("CryptCreateHash");
				
				// add passphrase to hash
				byte[] keyData = ASCIIEncoding.ASCII.GetBytes(passphrase);
				if (!WinApi.CryptHashData(hHash, keyData, (uint)keyData.Length, 0))
					Failed("CryptHashData");
					
				// create 40 bit crypto key from passphrase hash
				if (!WinApi.CryptDeriveKey(hProv, WinApi.CALG_RC2, 
					hHash, WinApi.CRYPT_EXPORTABLE, ref hKey))
					Failed("CryptDeriveKey");
			}
			
			finally
			{
				// release hash object
				if (hHash != IntPtr.Zero)
					WinApi.CryptDestroyHash(hHash);
			}
			
			return hKey;
		}


		/// <summary>
		/// Throws SystemException with GetLastError information.
		/// </summary>
		static private void Failed(string command)
		{
			uint lastError = WinApi.GetLastError();
			StringBuilder sb = new StringBuilder(500);

			try
			{
				// get message for last error
				WinApi.FormatMessage(WinApi.FORMAT_MESSAGE_FROM_SYSTEM, 
					null, lastError, 0, sb, 500, null);
			}
			catch
			{
				// error calling FormatMessage
				sb.Append("N/A.");
			}
					
			throw new SystemException(
				string.Format("{0} failed.\r\nLast error - 0x{1:x}.\r\nError message - {2}",
				command, lastError, sb.ToString()));
		}

		
		public static string ConvertToHexString (byte [] data)
		{
			string converted = "";
			foreach (byte inByte in data)
			{
				converted = converted + String.Format("{0:X2} ",inByte);
			}
			return converted;
		}

		public static byte [] ConvertFromHexString (string hexString)
		{
			string[] byteStrings = hexString.Split(" ".ToCharArray());
			byte[] result = new byte[byteStrings.Length-1];

			for (int i=0; i<byteStrings.Length-1; i++)
			{
				result[i] = Byte.Parse (byteStrings[i],
					System.Globalization.NumberStyles.AllowHexSpecifier);
			}
			return result;
		}
	}
}

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 Snoffleware Studios LLC
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions