Making SQL and .NET SHA1 Hashes Match

by kevin 8/7/2008 1:37:00 PM

A friend at SnagAJob.com came to me with an interesting problem today. He said that the HashBytes function in SQL Server was outputting different results from the HashAlgorithm.ComputeHash method in .NET. Here's a T-SQL script that hashes the URL to my blog.

DECLARE @data NVARCHAR(max)
SET @data = N'http://www.gotnet.biz/Blog'
SELECT HashBytes('SHA1', @data)

This script outputs 0x7FC8C5E43E9425C890AB96E660C86FC9CB077F4D as the hash value. The algorithm in C# attempting to do the same thing might look like this:

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

public class HashTest
{
    static void Main()
    {
        DoHash(new SHA1CryptoServiceProvider());
        Console.ReadLine();
    }

    private static void DoHash(HashAlgorithm algo)
    {
        var bytes = Encoding.UTF8.GetBytes(
            "http://www.gotnet.biz/Blog");
        var hash = algo.ComputeHash(bytes);
        Console.Write("{0} ", algo.GetType().Name);
        foreach (var b in hash)
            Console.Write("{0:X2}", b);
        Console.WriteLine();
    }
}

This code outputs 0x10397796345455fa6332db477972dc360b54ef2, a different hash value. Do you see the problem in the code? I didn't at first but it's simpler than you think.

The encoding that I used in the C# code is UTF8 which means the 8-bit Universal Character Set/UNICODE Transformation Format. That's a mouthful, isn't it? In .NET, the UTF8 encoding corresponds to Windows code page 65001 where each source character may map to between one and four characters in the encoded output. I used that encoding implicitly because in working with XML as often as I do, I'm accustomed to using the UTF8 encoding for nearly everything I do. My friend who posed the original question had done the same thing. However, in this case, it's a bad choice.

Looking at the T-SQL code above, notice that the data type for my string is NVARCHAR, that's UNICODE. And although all strings in .NET are stored in UNICODE and the UTF8 encoding is, as its name implies, just transforming the UNICODE to an 8-bit transportable format, the computed SHA1 hash on a UTF-8 encoded string in .NET is clearly not the same as SQL Server's result.

Playing around with some other transforms in the System.Text namespace, I discovered that by replacing the UTF8 encoding with the so-called Unicode encoding (or by switching the SQL data type to VARCHAR) makes the hash computations match between SQL and .NET in my example above. I capitalized Unicode as I did there quite deliberately because I am referring to the type in the System.Text namespace called UnicodeEncoding (which is available as the static Unicode property on the Encoding class) not the UNICODE standard.

In .NET, the Unicode encoding corresponds to Windows code page 1200 and goes by the familiar alias UTF-16. As that alias may imply, the.NET UnicodeEncoding uses a sequence of one or two 16-bit integers to represent each character in the original text. The results are easy to understand visually so I made the graphic shown here.

You can see that the contents of the byte stream from the two encodings is different. The UTF8 encoding strips the high order zero bytes for cultures where they are superfluous whereas the Unicode encoding preserves them. To sum up, when hashing NVARCHARs in SQL, the equivalent encoding to use in .NET code is the UnicodeEncoding. When hashing VARCHARs in SQL server, the matching .NET encoding is the UTF8Encoding.

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , ,

C# | CTS | Security | Software Development | SQL Server | SQL Server 2008

Windows Impersonator Class

by kevin 7/8/2008 11:34:00 PM

I wrote a handy little class called Impersonator a while ago to assist me in doing Windows impersonation. There are dozens of examples of this type of thing on the net. However, none of them were as "handy" as I prefer. By handy, I mean idiot-proof, of course. I like the IDisposable pattern a lot (I use the word pattern here loosely). IDisposable is usually meant to help idiots like me avoid mistakes. And in the case of changing the user identity on the running thread, forgetting to revert to the initial identity could be catastrophic. When combined with C#'s using statement, I get an extra measure of safety from forgetfulness or from rogue exceptions that might be thrown inside the impersonating code.

For the uninitiated, C#'s using statement makes IDisposable really hum by encapsulating the statement's code in a try/finally block where the IDisposable.Dispose method is called automatically and reliably. With my Impersonator class, you can now write code like this:

using (new Impersonator("domain", "username", "password"))
{
   // do whatever you want in here as domain\username
   // the security context will automatically revert on exit
   // when IDisposable.Dispose is called on the Impersonator
}

Pretty simple, huh? The code compiled by the C# compiler would actually look something like this:

Impersonator X = null;
try
{
   X = new Impersonator("domain", "username", "password");
   // do whatever you want in here as domain\username
   // the security context will automatically revert on exit
   // because of the Dispose call in the finally block
}
finally
{
   if (X != null)
      X.Dispose();
}

Now, if IDisposable were a real design pattern, there might be better compiler support. Wouldn't it be great that for classes implementing IDisposable, they could NOT be instantiated outside of a using statement context? That might be a bit restrictive in some cases but think about how fool-proof that would be. If you were required to instantiate IDisposable types within a using statement, you could guarantee that a type, which could be doing radical things like changing the identity on the running thread, would always clean up after itself. Alas, the C# team at Microsoft has been ignoring my plea for help.

Here's the code for the Impersonator class for your enjoyment. Let me know if you like it. I have many other gems like this one lying around.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace gotnet.Security
{
    /// <summary>
    /// Impersonate a local or domain user. This class uses the IDisposable
    /// pattern to automatically revert to the caller's original security
    /// context safely. When instantiated from within C# code, this class
    /// should always be constructed in a using statement to ensure that the
    /// disposal routine runs. If you are not using VB.NET, you should call
    /// the <see cref="Dispose"/> method in a Finally block.
    /// </summary>
    /// <example>
    /// <code>
    /// // enter the following block of code as user Fortunato
    ///
    /// using (new Impersonator("MyDomain", "Amontillado", "pAs5w0Rd"))
    /// {
    ///     // the running thread identity is now that of user Amontillado
    ///     // execute whatever you like here as the user Amontillado
    ///     // buy some bricks, some mortar, some good wine, etc.
    /// }
    ///
    /// // at this point, the thread identity has reverted to user Fortunato
    /// </code>
    /// </example>
    public class Impersonator : IDisposable
    {
        #region // DllImports
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(String lpszUsername,
            String lpszDomain, String lpszPassword, int dwLogonType,
            int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(IntPtr handle);
        #endregion // DllImports

        private readonly IntPtr tokenHandle = new IntPtr(0);
        private readonly WindowsImpersonationContext impersonatedUser; 

        /// <summary>
        /// Impersonate a user. When instantiated from within C# code, this
        /// class should always be constructed in a using statement to ensure
        /// that the disposal routine runs. If you are not using VB.NET, you
        /// should call the <see cref="Dispose"/> method in a Finally block.
        /// </summary>
        /// <param name="domainName">
        /// The domain to authenticate against. May be a machine name.
        /// </param>
        /// <param name="userName">
        /// The user name to authenticate with.
        /// </param>
        /// <param name="password">
        /// The password to authenticate with.
        /// </param>
        /// <exception cref="Win32Exception">
        /// The LogonUser operation failed.
        /// </exception>
        /// <exception cref="UnauthorizedAccessException">
        /// Windows returned the Windows NT status code STATUS_ACCESS_DENIED.
        /// </exception>
        /// <exception cref="OutOfMemoryException">
        /// There is insufficient memory available.
        /// </exception>
        /// <exception cref="System.Security.SecurityException">
        /// The caller does not have the correct permissions.
        /// </exception>
        public Impersonator(string domainName, string userName,
            string password)
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_INTERACTIVE = 2;
            tokenHandle = IntPtr.Zero; 

            if (!LogonUser(userName, domainName, password,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                ref tokenHandle))
            {
                int ret = Marshal.GetLastWin32Error();
                throw new Win32Exception(ret);
            }

            WindowsIdentity newId = new WindowsIdentity(tokenHandle);
            impersonatedUser = newId.Impersonate();
        } 

        /// <summary>
        /// Cleanup by reverting the user identity and closing any previously
        /// obtained handles. This method will be called automatically if you
        /// construct the Impersonator object from within a C# using statement.
        /// If you are using VB.NET, be sure to call Dispose from within a
        /// Finally block to make sure it happens. Failure to do so may leave
        /// the running thread in an unusable state.
        /// </summary>
        /// <exception cref="System.Security.SecurityException">
        /// An attempt is made to use this method for any purpose other than
        /// to revert identity to self.
        /// </exception>
        public void Dispose()
        {
            if (impersonatedUser != null)
            {
                impersonatedUser.Undo();
                impersonatedUser.Dispose();
            }
            if (tokenHandle != IntPtr.Zero)
                CloseHandle(tokenHandle);
        }
    }
}

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

C# | Security

Silverlight Assembly Handling

by kevin 5/20/2008 9:16:00 PM
Hanu Kommalapati is blogging again! Awesome. In his latest post, he addresses the issue of assembly handling by Silverlight. With security at the forefront of everyone's mind these days, you need to be able to relate to others how Silverlight handles XAP files and their contents on the web desktop. In his post, Hanu explains how this is done. Scroll down and read his reply to a reader's question to fully understand. In essence, he says, "As of beta1, [the XAP files] won't leave any footprint on disk." This is good to know when you are selling the idea of using Silverlight to your clients or your management team.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , ,

Silverlight | Security

Powered by BlogEngine.NET 1.3.1.0
Theme by Mads Kristensen


Kevin's on Twitter / FriendFeed

W. Kevin Hazzard Welcome to Kevin Hazzard's Blog. Kevin is a Software Architect, Professor and Microsoft MVP specializing in C#, WCF, Silverlight and IronPython.

View Kevin Hazzard's profile on LinkedIn
Microsoft MVP Award Foolish robot!

Calendar

<<  October 2008  >>
MoTuWeThFrSaSu
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008

Sign in