我生成了一个md5哈希,如下所示:
DECLARE @varchar varchar(400)
SET @varchar = 'è'
SELECT CONVERT(VARCHAR(2000), HASHBYTES( 'MD5', @varchar ), 2)
哪个输出:
785D512BE4316D578E6650613B45E934
但是使用以下方法生成MD5哈希:
System.Text.Encoding.UTF8.GetBytes("è")
产生:
0a35e149dbbb2d10d744bf675c7744b1
C#.NET方法中的编码设置为UTF8,我以为varchar也是UTF8,关于我做错了什么想法?
最佳答案
如果要处理NVARCHAR
/ NCHAR
数据(存储为UTF-16 Little Endian),则应使用Unicode
编码,而不是BigEndianUnicode
。在.NET中,UTF-16被称为Unicode
,而其他Unicode编码则通过其实际名称来引用:UTF7,UTF8和UTF32。因此,Unicode
本身就是Little Endian
,而不是BigEndianUnicode
。更新:请参阅末尾有关UCS-2和补充字符的部分。
在数据库方面:
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
在.NET方面:
System.Text.Encoding.ASCII.GetBytes("è")
// D1457B72C3FB323A2671125AEF3EAB5D
System.Text.Encoding.UTF7.GetBytes("è")
// F63A0999FE759C5054613DDE20346193
System.Text.Encoding.UTF8.GetBytes("è")
// 0A35E149DBBB2D10D744BF675C7744B1
System.Text.Encoding.UTF32.GetBytes("è")
// 86D29922AC56CF022B639187828137F8
System.Text.Encoding.BigEndianUnicode.GetBytes("è")
// 407256AC97E4C5AEBCA825DEB3D2E89C
System.Text.Encoding.Unicode.GetBytes("è") // this one matches HASHBYTES('MD5', N'è')
// FAC02CD988801F0495D35611223782CF
但是,此问题与
VARCHAR
/ CHAR
数据有关,它是ASCII,因此情况要复杂一些。在数据库方面:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
我们已经在上面看到了.NET方面。从这些哈希值中,应该有两个问题:
为什么它们都不匹配
HASHBYTES
值?为什么@Eric J.的答案中链接的“ sqlteam.com”文章显示其中三个(
ASCII
,UTF7
和UTF8
)都与HASHBYTES
值匹配?有一个答案涵盖了两个问题:代码页。在“ sqlteam”文章中完成的测试使用了0到127范围内的“安全” ASCII字符(以int /十进制值表示),这些字符在代码页之间没有变化。但是128-255的范围(我们在其中找到“è”字符)是扩展集,它的确因代码页而异(这是有代码页的原因,这很有意义)。
现在尝试:
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
该值与
ASCII
哈希值匹配(同样,因为“ sqlteam”文章/测试使用了0-127范围内的值,因此在使用COLLATE
时它们没有看到任何更改)。太好了,现在我们终于找到了一种匹配VARCHAR
/ CHAR
数据的方法。都好?好吧,不是真的。让我们看一下我们实际的哈希值:
SELECT 'è' AS [TheChar],
ASCII('è') AS [TheASCIIvalue],
'è' COLLATE SQL_Latin1_General_CP1255_CI_AS AS [CharCP1255],
ASCII('è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [TheASCIIvalueCP1255];
返回值:
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
?
吗?只是为了验证,运行:SELECT CHAR(63) AS [WhatIs63?];
-- ?
嗯,所以代码页1255没有
è
字符,因此它被转换为每个人都喜欢的?
。但是,为什么在使用ASCII编码时,它与.NET中的MD5哈希值匹配?可能是我们实际上没有匹配è
的哈希值,而是匹配了?
的哈希值:SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
对。真正的ASCII字符集只是前128个字符(值0-127)。正如我们刚刚看到的,
è
是232。因此,在.NET中使用ASCII
编码并没有帮助。在T-SQL端也没有使用COLLATE
。是否可以在.NET端获得更好的编码?是的,通过使用Encoding.GetEncoding(Int32),它允许指定代码页。可以使用以下查询发现要使用的代码页(使用列而不是文字或变量时使用
sys.columns
):SELECT sd.[collation_name],
COLLATIONPROPERTY(sd.[collation_name], 'CodePage') AS [CodePage]
FROM sys.databases sd
WHERE sd.[name] = DB_NAME(); -- replace function with N'{db_name}' if not running in the DB
上面的查询返回(对我来说):
Latin1_General_100_CI_AS_SC 1252
因此,让我们尝试一下代码页1252:
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
呜呜!我们使用默认的SQL Server排序规则对
VARCHAR
数据进行匹配:)。当然,如果数据来自数据库或设置为其他排序规则的字段,则GetEncoding(1252)
可能无法正常工作,您将必须使用上面显示的查询找到实际匹配的代码页(在许多情况下都使用代码页归类,因此不同的归类并不一定意味着不同的代码页。若要查看可能的代码页值,以及它们对应的文化/地区,请查看代码页here的列表(列表在“备注”部分中)。
与
NVARCHAR
/ NCHAR
字段中实际存储的内容有关的其他信息:尽管内置函数的默认行为假定所有字符都是UCS-2(每个2个字节),它是UTF-16的子集,但是可以存储任何UTF-16字符(2或4个字节)。从SQL Server 2012开始,可以访问一组支持4字节字符(称为补充字符)的Windows排序规则。使用以
_SC
结尾的Windows归类之一(为列指定或直接在查询中)将允许内置函数正确处理4个字节的字符。-- The database's collation is set to: SQL_Latin1_General_CP1_CI_AS
SELECT N'𨝫' AS [SupplementaryCharacter],
LEN(N'𨝫') AS [LEN],
DATALENGTH(N'𨝫') AS [DATALENGTH],
UNICODE(N'𨝫') AS [UNICODE],
LEFT(N'𨝫', 1) AS [LEFT],
HASHBYTES('MD5', N'𨝫') AS [HASHBYTES];
SELECT N'𨝫' AS [SupplementaryCharacter],
LEN(N'𨝫' COLLATE Latin1_General_100_CI_AS_SC) AS [LEN],
DATALENGTH(N'𨝫' COLLATE Latin1_General_100_CI_AS_SC) AS [DATALENGTH],
UNICODE(N'𨝫' COLLATE Latin1_General_100_CI_AS_SC) AS [UNICODE],
LEFT(N'𨝫' COLLATE Latin1_General_100_CI_AS_SC, 1) AS [LEFT],
HASHBYTES('MD5', N'𨝫' COLLATE Latin1_General_100_CI_AS_SC) AS [HASHBYTES];
返回值:
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
如您所见,
DATALENGTH
和HASHBYTES
均不受影响。有关更多信息,请参见Collation and Unicode Support的MSDN页面(特别是“补充字符”部分)。
关于.net - TSQL md5哈希与C#.NET md5不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27908449/