【问题标题】:How to calculate hashbyte SHA1 using C#?如何使用 C# 计算 hashbyte SHA1?
【发布时间】:2014-02-25 17:40:46
【问题描述】:

在表格中,我有一个列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

谢谢

参数

【问题讨论】:

  • 如果您能展示您正在尝试使用 C# 的内容会有所帮助,这样我们就可以检查该代码是否有错误。
  • 两个链接都展示了如何在 C# 中创建哈希,那么您的问题是什么?
  • c# 代码没有给我数据库中的确切哈希值。

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


【解决方案1】:

这是一个编码问题:

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

Sql Server 不同:

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

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

    UTF-16 和 UCS-2 最大的区别在于 UCS-2 只能表示 Unicode BMP(基本多语言平面)内的字符; 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 ;
  }

}

简单!

【讨论】:

  • 谢谢伙计。爱它。它工作得很好,但如果 source_text 字段是 nvarchar,它会失败。任何解决方案。
  • 只需更改此行即可。 UnicodeEncoding dstEncoding = new UnicodeEncoding(); byte[] octets = dstEncoding.GetBytes(s);
  • 非常感谢您的辛勤工作。我真的很感激。
  • @Parminder:Encoding 类有一个静态属性 Encoding.Unicode,它提供缓存的 UTF-16 编码。无需在每次需要时都实例化一个新的。 Encoding.GetEncoding() 重载也是如此:它们提供缓存副本以减少开销。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 2012-06-14
    • 2016-05-23
    • 2020-11-01
    • 2015-04-13
    • 2016-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多