TSQL md5 散列不同於 C# .NET md5
我生成了一個 md5 雜湊,如下所示:
DECLARE @varchar varchar(400) SET @varchar = 'è' SELECT CONVERT(VARCHAR(2000), HASHBYTES( 'MD5', @varchar ), 2)哪個輸出:
785D512BE4316D578E6650613B45E934但是使用以下方法生成 MD5 雜湊:
System.Text.Encoding.UTF8.GetBytes("è")生成:
0a35e149dbbb2d10d744bf675c7744b1C# .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/CHARdata有關,它是ASCII,所以事情有點複雜。在數據庫方面:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR] -- 785D512BE4316D578E6650613B45E934我們已經在上面看到了 .NET 方面。從這些散列值應該有兩個問題:
- 為什麼它們中的任何一個都不匹配該
HASHBYTES值?- 為什麼@Eric J. 的答案中連結的“sqlteam.com”文章顯示其中三個(
ASCII、、UTF7和UTF8)都匹配HASHBYTES值?有一個答案涵蓋了這兩個問題:程式碼頁。在“sqlteam”文章中完成的測試使用了 0 - 127 範圍內的“安全”ASCII 字元(就 int / decimal 值而言),這些字元在程式碼頁之間沒有變化。但是 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?]; -- ?啊,所以 Code Page 1255 沒有這個
è字元,所以它被翻譯為每個人的最愛?。但是,為什麼在使用 ASCII 編碼時,它與 .NET 中的 MD5 散列值匹配?可能我們實際上沒有匹配 的雜湊值è,而是匹配 的雜湊值?:SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR] -- 0xD1457B72C3FB323A2671125AEF3EAB5D對。真正的ASCII字元集只是前 128 個字元(值 0 - 127)。正如我們剛剛看到的,它
è是 232。因此,ASCII在 .NET 中使用編碼並沒有太大幫助。也沒有COLLATE在 T-SQL 端使用。是否有可能在 .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嗚呼!我們有一個
VARCHAR使用我們預設 SQL Server 排序規則的數據匹配:)。當然,如果數據來自數據庫或設置為不同排序規則的欄位,則GetEncoding(1252)可能無法正常工作,您必須使用上面顯示的查詢找到實際匹配的程式碼頁(程式碼頁用於許多排序規則,所以不同的排序規則不一定意味著不同的程式碼頁)。要查看可能的程式碼頁值是什麼,以及它們所屬的文化/區域設置,請在此處查看程式碼頁列表(列表位於“備註”部分)。
NVARCHAR與/NCHAR欄位中實際儲存的內容相關的附加資訊:可以儲存任何UTF-16字元(2 或 4 字節),儘管內置函式的預設行為假定所有字元都是 UCS-2(每個 2 字節),它是 UTF-16 的子集。從 SQL Server 2012 開始,可以訪問一組支持稱為補充字元的 4 字節字元的 Windows 排序規則。使用這些以 結尾的 Windows 排序規則之一(
_SC為列指定或直接在查詢中指定)將允許內置函式正確處理 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不受影響。有關詳細資訊,請參閱排序規則和 Unicode 支持的 MSDN 頁面(特別是“補充字元”部分)。