SQL Uniqueidentifier和hashbytes不匹配c#



在SQL server中创建哈希是不同的。为什么不一样呢?

using (SHA1Managed sha1 = new SHA1Managed())
{
Guid g = new Guid("81FE483B-39ED-4052-8FFC-55A710907D9B");
var appGateId = g.ToString();

var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(appGateId));
var sb = new StringBuilder();
foreach (byte b in hash)
{
sb.Append(b.ToString("x2"));
}
Console.WriteLine(sb.ToString());
}

这里的结果是ebb52fefab48f428b6ee03174276c8ade0b4ef1a

使用SQL Server和以下代码:

declare @a uniqueidentifier = '81FE483B-39ED-4052-8FFC-55A710907D9B', @b nvarchar(50)
select @b = CONVERT(nvarchar(50), @a)
select convert(varchar(50), hashbytes('SHA1', @b), 2)

结果是CC34B1E702F6E7FE0EE92ED4D5390BB4765B9A21

@Marc Gravell的评论解释了这里的问题。在c#中,您使用的是UTF-8字符串,但在SQL Server中,您使用的是nvarchar,这是UTF-16;这些是不可比较的。

但是,如果您实际使用varchar,并将字符更改为小写(正如在T-SQL中转换为基于字符串的数据类型时将uniqueidentifier更改为大写),则确实得到相同的值。您可以(如果您在2019+)显式地将值整理为UTF-8值,但考虑到GUID仅由十六进制字符(0-9A-F)组成,这似乎有点毫无意义:
DECLARE @a uniqueidentifier = '81FE483B-39ED-4052-8FFC-55A710907D9B', @b varchar(50);
SELECT @b = LOWER(CONVERT(varchar(50), @a));
SELECT @b, LOWER(CONVERT(varchar(50), HASHBYTES('SHA1', @b), 2));

要使它们相等,需要处理两个问题:

  • 如前所述,您正在请求UTF-8,而SQL Server正在使用UTF-16
  • c#用小写生成,SQL Server用大写生成

你可以修改c#代码

using (SHA1Managed sha1 = new SHA1Managed())
{
Guid g = new Guid("81FE483B-39ED-4052-8FFC-55A710907D9B");
var appGateId = g.ToString().ToUpper().Dump();
var hash = sha1.ComputeHash(Encoding.Unicode.GetBytes(appGateId));
var sb = new StringBuilder();
foreach (byte b in hash)
{
sb.Append(b.ToString("x2"));
}
Console.WriteLine(sb.ToString());
}

dotnetfiddle

或者您可以更改SQL Server代码

declare @a uniqueidentifier = '81FE483B-39ED-4052-8FFC-55A710907D9B', @b varchar(50)
select @b = LOWER(CONVERT(varchar(50), @a))
select convert(varchar(50), hashbytes('SHA1', @b COLLATE Latin1_General_100_BIN2_UTF8 ), 2)

,db&lt的在小提琴


老实说,我不明白你为什么要散列字符串表示。您也可以简单地散列实际的Guid字节。

最新更新