sql >> Databasteknik >  >> RDS >> Sqlserver

TSQL md5-hash som skiljer sig från C# .NET md5

Om du har att göra med NVARCHAR / NCHAR data (som lagras som UTF-16 Little Endian ), då skulle du använda Unicode kodning, inte BigEndianUnicode . I .NET kallas UTF-16 för Unicode medan andra Unicode-kodningar hänvisas till med sina faktiska namn:UTF7, UTF8 och UTF32. Därför Unicode i sig är Little Endian i motsats till BigEndianUnicode . UPPDATERING: Se avsnittet i slutet om UCS-2 och tilläggstecken.

På databassidan:

SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF

På .NET-sidan:

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

Den här frågan hänför sig dock till VARCHAR / CHAR data, som är ASCII, så saker och ting är lite mer komplicerade.

På databassidan:

SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934

Vi ser redan .NET-sidan ovan. Från dessa hashade värden bör det finnas två frågor:

  • Varför inte någon av dem matchar HASHBYTES värde?
  • Varför visar artikeln "sqlteam.com" som är länkad i @Eric J:s svar att tre av dem (ASCII , UTF7 och UTF8 ) matchar alla HASHBYTES värde?

Det finns ett svar som täcker båda frågorna:Kodsidor. Testet som gjordes i "sqlteam"-artikeln använde "säkra" ASCII-tecken som ligger i intervallet 0 - 127 (i termer av int / decimalvärdet) som inte varierar mellan kodsidor. Men intervallet 128 - 255 -- där vi hittar tecknet "è" -- är den Utökade uppsättning som varierar beroende på kodsida (vilket är vettigt eftersom detta är anledningen till att ha kodsidor).

Försök nu:

SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D

Det matchar ASCII hashat värde (och igen, eftersom "sqlteam"-artikeln / -testet använde värden i intervallet 0 - 127, såg de inga ändringar när de använde COLLATE ). Bra, nu har vi äntligen hittat ett sätt att matcha VARCHAR / CHAR data. Allt bra?

Tja, inte riktigt. Låt oss ta en titt och se vad vi faktiskt hashade:

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];

Returnerar:

TheChar TheASCIIvalue   CharCP1255  TheASCIIvalueCP1255
è       232             ?           63

En ? ? Bara för att verifiera, kör:

SELECT CHAR(63) AS [WhatIs63?];
-- ?

Ah, så Code Page 1255 har inte è tecken, så det översätts som allas favorit ? . Men varför matchade det då MD5-hashvärdet i .NET när man använder ASCII-kodning? Kan det vara så att vi faktiskt inte matchade det hashade värdet för è , men i stället matchade det hashade värdet för ? :

SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D

Japp. Den sanna ASCII-teckenuppsättningen är bara de första 128 tecknen (värden 0 - 127). Och som vi precis såg, è är 232. Så, med ASCII kodning i .NET är inte så användbart. Inte heller använde COLLATE på T-SQL-sidan.

Är det möjligt att få en bättre kodning på .NET-sidan? Ja, genom att använda Encoding.GetEncoding(Int32), som gör det möjligt att ange kodsidan. Kodsidan som ska användas kan hittas med hjälp av följande fråga (använd sys.columns när du arbetar med en kolumn istället för en bokstavlig eller variabel):

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

Frågan ovan returnerar (för mig):

Latin1_General_100_CI_AS_SC    1252

Så låt oss prova Code Page 1252:

System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934

Woo ho! Vi har en matchning för VARCHAR data som använder vår standard SQL Server-sortering :). Naturligtvis, om data kommer från en databas eller ett fält som är inställt på en annan sortering, GetEncoding(1252) kanske inte fungerar och du måste hitta den faktiska matchande kodsidan med hjälp av frågan som visas ovan (en kodsida används över många sorteringar, så en annan sortering behöver inte nödvändigtvis innebär en annan kodsida).

För att se vilka de möjliga kodsidorna är, och vilken kultur/lokal de tillhör, se listan över kodsidor här (listan finns i avsnittet "Anmärkningar").

Ytterligare information relaterad till vad som faktiskt lagras i NVARCHAR / NCHAR fält:

Alla UTF-16-tecken (2 eller 4 byte) kan lagras, även om standardbeteendet för de inbyggda funktionerna förutsätter att alla tecken är UCS-2 (2 byte vardera), vilket är en delmängd av UTF-16. Från och med SQL Server 2012 är det möjligt att komma åt en uppsättning Windows-kollationer som stöder de 4 byte-tecken som kallas tilläggstecken. Använder en av dessa Windows-kollationer som slutar på _SC , antingen specificerad för en kolumn eller direkt i en fråga, gör det möjligt för de inbyggda funktionerna att korrekt hantera de 4 byte tecknen.

-- 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];

Returnerar:

SupplementaryChar   LEN   DATALENGTH   UNICODE   LEFT   HASHBYTES
𨝫                  2     4             55393    �     0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫                  1     4            165739    𨝫     0x7A04F43DA81E3150F539C6B99F4B8FA9

Som du kan se, varken DATALENGTH inte heller HASHBYTES Är påverkade. För mer information, se MSDN-sidan för Collation och Unicode Support (särskilt avsnittet "Supplerande tecken").



  1. MySQL:Välj Random Entry, men vikt mot vissa poster

  2. Barman 2.11:barman-cloud-restore och barman-cloud-wal-restore

  3. MySQL Update Inner Join-tabeller fråga

  4. Få månadsnamn från datum i Oracle