Pierre Greborio.NET

Talking about .NET world

SHA-1 algorithm for Compact Framework

Compact Framework lacks of Cryptography support. If you have to hash a string you can't use SHA1CryptoServiceProvider since it isn't  supported on CF. One of the available workaround is to use Win.CE (2.10 and later) SDK functions through P/Invoke.

I developed a simple helper function that get a string as parameter and returns it's hashed value. I want to share here the code so that if somebody else will encounter the same issue can save some times :-D

Here is the code:

public static string HashString(string textToHash)
{
 string hashValue = string.Empty;

 if(textToHash == null || textToHash == string.Empty)
  throw new ArgumentNullException("textToHash", "The text to hash cannot be null.");

 const int PROV_RSA_FULL = 1;
 const int CALG_SHA1   = 0x8004;
 const int HP_HASHSIZE = 0x0004;
 const int HP_HASHVAL  =  0x0002;

 IntPtr hHash = IntPtr.Zero;
 IntPtr hProv = IntPtr.Zero;

 // Obtain handle to the default key container
 if(!HashNative.CryptAcquireContext(ref hProv, null, "Microsoft Base Cryptographic Provider v1.0", PROV_RSA_FULL, 0))
  throw new Win32Exception(Marshal.GetLastWin32Error());
 else
 {
  try
  {
   // Obtain handle to hash object.
   if(!HashNative.CryptCreateHash(hProv, CALG_SHA1, IntPtr.Zero, 0, ref hHash))
    throw new Win32Exception(Marshal.GetLastWin32Error());
   else
   {
    byte[] buffer = System.Text.Encoding.ASCII.GetBytes(textToHash);

    // Hash data.
    if(!HashNative.CryptHashData(hHash, buffer, buffer.Length, 0))
     throw new Win32Exception(Marshal.GetLastWin32Error());

    // Get size of hash value.
    int bufLen = Marshal.SizeOf(typeof(uint));
    byte[] hashBuf = new byte[bufLen];
    HashNative.CryptGetHashParam(hHash, HP_HASHSIZE, hashBuf, ref bufLen, 0);

    // Get hash value.
    bufLen = hashBuf[0];
    hashBuf = new byte[bufLen];
    if(!HashNative.CryptGetHashParam(hHash, HP_HASHVAL, hashBuf, ref bufLen, 0))
     throw new Win32Exception(Marshal.GetLastWin32Error());

    // Convert to base 64 string
    hashValue = Convert.ToBase64String(hashBuf, 0, bufLen);
   }
  }
  finally
  {
   // Release hash object.
   if(hHash != IntPtr.Zero)
    HashNative.CryptDestroyHash(hHash);

   // Release context object
   if(hProv != IntPtr.Zero)
    HashNative.CryptReleaseContext(hProv, 0);
  }
 }

 return hashValue;
}

The native declarations are:

internal class HashNative
{
 [DllImport("CoreDLL.Dll", SetLastError=true)]
 internal extern static bool CryptAcquireContext(ref IntPtr hCryptProv, string containerName, string providerName,
  int providerType, uint providerFlags);

 [DllImport("CoreDLL.Dll", SetLastError=true)]
 internal extern static bool CryptReleaseContext(IntPtr hCryptProv, uint providerFlags);

 [DllImport("CoreDLL.Dll", SetLastError=true)]
 internal extern static bool CryptCreateHash(IntPtr hProv, int algId, IntPtr hKey, uint dwFlags, ref IntPtr phHash);

 [DllImport("CoreDLL.Dll", SetLastError=true)]
 internal extern static bool CryptDestroyHash(IntPtr phHash);

 [DllImport("CoreDLL.Dll", SetLastError=true)]
 internal extern static bool CryptHashData(IntPtr hHash, byte[] pbData, int dwDataLen, uint dwFlags);

 [DllImport("CoreDLL.Dll", SetLastError=true)]
 internal extern static bool CryptGetHashParam(IntPtr hHash, int dwParam, [in,Out] byte[] pbData, ref int pdwDataLen, uint dwFlags);
}

Note that the above code is for demo purpose only and I don't have any responsability for it's usage. You can use and modify it freely.

Comments

padam jain said:

thanks for this great effort sir
this will really help me a lot.
thanks
padam jain
# February 23, 2004 10:12 AM

Pierre Greborio said:

Happy to hear that ;-)

Thanks
Pierre
# February 24, 2004 3:50 AM