如何使用C#计算hashbyte SHA1?

时间:2014-02-25 17:40:46

标签: c# sql-server asp.net-mvc hash sql-server-2012

在表格中,我有一列URL,我用它来保存网址。我正在使用公式(CONVERT([varbinary](20),hashbytes('SHA1',[URL])))计算另一列中的哈希值。它工作正常。

现在我需要在C#中获得类似的函数来获取哈希值,以便在插入新行之前我可以比较并检查类似的行是否存在。我尝试了几个链接,但没有运气。

以下是链接:

http://weblogs.sqlteam.com/mladenp/archive/2009/04/28/Comparing-SQL-Server-HASHBYTES-function-and-.Net-hashing.aspx

How do I calculate the equivalent to SQL Server (hashbytes('SHA1',[ColumnName])) in C#?

** I found this link working. All I need to do is change formula in the db. but is it possible to make it in one line 
**

http://forums.asp.net/t/1782626.aspx

DECLARE @HashThis nvarchar(4000);
DECLARE @BinHash varbinary(4000);
SELECT @HashThis = CONVERT(nvarchar(4000),'Password@Test');
SELECT @BinHash = HashBytes('SHA1', @HashThis);

SELECT cast(N'' as xml).value('xs:base64Binary(xs:hexBinary(sql:variable("@BinHash")))', 'nvarchar(4000)');

在c#

string pwd = "Password@Test"; 
var sha1Provider = HashAlgorithm.Create("SHA1"); 
var binHash = sha1Provider.ComputeHash(Encoding.Unicode.GetBytes(pwd)); 
Console.WriteLine(Convert.ToBase64String(binHash)); 

我正在使用sql server 2012.数据库的归类是SQL_Latin1_General_CP1_CI_AS

由于

Paraminder

2 个答案:

答案 0 :(得分:4)

这是一个编码问题:

C#/.Net / CLR字符串在内部是UTF-16编码的字符串。这意味着每个字符至少两个字节。

Sql Server不同:

  • charvarchar使用与该列使用的collation相关联的代码页将每个字符表示为单个字节

  • ncharnvarchar使用[旧的和过时的] UCS-2 Unicode编码将每个字符表示为2个字节 - 这在1996年随着Unicode 2.0的发布而被弃用, UTF-16。

    UTF-16和UCS-2之间的最大区别在于UCS-2只能表示Unicode BMP(Basic Multilingual Plane)中的字符; UTF-16可以表示任何 Unicode字符。根据我的理解,在BMP中,UCS-2和UTF-16表示是相同的。

这意味着要计算与SQL Server计算的哈希相同的哈希,您将需要获得与SQL Server具有的字节表示相同的字节表示。由于听起来您使用charvarchar使用排序SQL_Latin1_General_CP1_CI_ASper the documentation,因此CP1部分表示代码页1252,其余表示案例 - 不敏感,重音敏感。所以......

您可以通过以下方式获取代码页1252的编码:

Encoding enc = Encoding.GetEncoding(1252);

使用该信息,并给出此表:

create table dbo.hash_test
(
  id          int           not null identity(1,1) primary key clustered ,
  source_text varchar(2000) collate SQL_Latin1_General_CP1_CI_AS not null ,
  hash        as
              ( hashbytes( 'SHA1' , source_text ) ) ,
)
go
insert dbo.hash_test ( source_text ) values ( 'the quick brown fox jumped over the lazy dog.' )
insert dbo.hash_test ( source_text ) values ( 'She looked like something that might have occured to Ibsen in one of his less frivolous moments.' )
go

你会得到这个输出

1: the quick brown fox jumped over the lazy dog.
   sql: 6039D100 3323D483 47DDFDB5 CE2842DF 758FAB5F
   c#:  6039D100 3323D483 47DDFDB5 CE2842DF 758FAB5F

2: She looked like something that might have occured to Ibsen in one of his less frivolous moments.
   sql: D92501ED C462E331 B0E129BF 5B4A854E 8DBC490C
   c#:  D92501ED C462E331 B0E129BF 5B4A854E 8DBC490C

来自这个程序

class Program
{

  static byte[] Sha1Hash( string s )
  {
    SHA1    sha1         = SHA1.Create() ;
    Encoding windows1252 = Encoding.GetEncoding(1252) ;
    byte[]   octets      = windows1252.GetBytes(s) ;
    byte[]   hash        = sha1.ComputeHash( octets ) ;

    return hash ;
  }

  static string HashToString( byte[] bytes )
  {
    StringBuilder sb = new StringBuilder() ;

    for ( int i = 0 ; i < bytes.Length ; ++i )
    {
      byte b = bytes[i] ;
      if ( i > 0 && 0 == i % 4 ) sb.Append( ' ' ) ;
      sb.AppendFormat( b.ToString("X2") ) ;
    }

    string s = sb.ToString() ;
    return s ;
  }

  private static DataTable ReadDataFromSqlServer()
  {
    DataTable dt = new DataTable();

    using ( SqlConnection conn = new SqlConnection( "Server=localhost;Database=sandbox;Trusted_Connection=True;"))
    using ( SqlCommand cmd = conn.CreateCommand() )
    using ( SqlDataAdapter sda = new SqlDataAdapter(cmd) )
    {
      cmd.CommandText = "select * from dbo.hash_test" ;
      cmd.CommandType = CommandType.Text;
      conn.Open();
      sda.Fill( dt ) ;
      conn.Close() ;
    }

    return dt ;
  }

  static void Main()
  {
    DataTable dt = ReadDataFromSqlServer() ;

    foreach ( DataRow row in dt.Rows )
    {
      int    id            = (int)    row[ "id"          ] ;
      string sourceText    = (string) row[ "source_text" ] ;
      byte[] sqlServerHash = (byte[]) row[ "hash"        ] ;
      byte[] myHash        = Sha1Hash( sourceText ) ;

      Console.WriteLine();
      Console.WriteLine( "{0:##0}: {1}" , id , sourceText ) ;
      Console.WriteLine( "   sql: {0}" , HashToString( sqlServerHash ) ) ;
      Console.WriteLine( "   c#:  {0}" , HashToString( myHash        ) ) ;

      Debug.Assert( sqlServerHash.SequenceEqual(myHash) ) ;

    }

    return ;
  }

}

容易!

答案 1 :(得分:0)

我建议任何时候创建一个哈希,它可以在一个地方完成。无论是在代码中还是在数据库中。从长远来看,它将使您的生活更轻松。这意味着要么在插入记录之前更改C#代码以创建哈希,要么在存储过程中执行重复检查。

无论如何,应该同步复制检查和插入,以便在检查任何重复项和实际插入记录之间不会发生其他插入。最简单的方法是在同一事务中执行它们。

如果你坚持按原样保留逻辑,那么我建议你在数据库中创建哈希,但是通过存储过程或用户定义的函数公开它,可以从你的C#代码中调用它。