Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

TSQL md5-Hash anders als C# .NET md5

Wenn Sie es mit NVARCHAR zu tun haben / NCHAR Daten (die als UTF-16 Little Endian gespeichert werden ), dann würden Sie den Unicode verwenden Codierung, nicht BigEndianUnicode . In .NET heißt UTF-16 Unicode während andere Unicode-Codierungen mit ihren tatsächlichen Namen bezeichnet werden:UTF7, UTF8 und UTF32. Daher Unicode an sich ist Little Endian im Gegensatz zu BigEndianUnicode . AKTUALISIERUNG: Bitte lesen Sie den Abschnitt am Ende zu UCS-2 und ergänzenden Zeichen.

Auf der Datenbankseite:

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

Auf der .NET-Seite:

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

Diese Frage bezieht sich jedoch auf VARCHAR / CHAR Daten, die ASCII sind, und daher sind die Dinge etwas komplizierter.

Auf der Datenbankseite:

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

Oben sehen wir bereits die .NET-Seite. Aus diesen gehashten Werten sollten sich zwei Fragen ergeben:

  • Warum nicht keine davon stimmen mit den HASHBYTES überein Wert?
  • Warum zeigt der in der Antwort von @Eric J. verlinkte "sqlteam.com"-Artikel, dass drei von ihnen (ASCII , UTF7 und UTF8 ) stimmen alle mit den HASHBYTES überein Wert?

Es gibt eine Antwort, die beide Fragen abdeckt:Codepages. Der im Artikel „sqlteam“ durchgeführte Test verwendete „sichere“ ASCII-Zeichen im Bereich von 0 bis 127 (in Bezug auf den Int-/Dezimalwert), die sich nicht zwischen den Codepages unterscheiden. Aber der Bereich von 128 bis 255 – wo wir das „è“-Zeichen finden – ist der Erweiterte Satz, der je nach Codepage variiert (was sinnvoll ist, da dies der Grund für Codepages ist).

Versuchen Sie jetzt:

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

Das entspricht dem ASCII gehashter Wert (und noch einmal, weil der "sqlteam"-Artikel/Test Werte im Bereich von 0 - 127 verwendete, sahen sie keine Änderungen bei der Verwendung von COLLATE ). Großartig, jetzt haben wir endlich einen Weg gefunden, VARCHAR abzugleichen / CHAR Daten. Alles gut?

Nicht wirklich. Werfen wir einen Blick darauf, was wir tatsächlich gehasht haben:

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

Rückgabe:

TheChar TheASCIIvalue   CharCP1255  TheASCIIvalueCP1255
è       232             ?           63

Ein ? ? Führen Sie einfach zur Überprüfung Folgendes aus:

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

Ah, Codepage 1255 hat also nicht den è Zeichen, also wird es als jedermanns Lieblings-? übersetzt . Aber warum stimmte das dann mit dem MD5-Hashwert in .NET überein, wenn die ASCII-Codierung verwendet wurde? Könnte es sein, dass wir den gehashten Wert von è nicht wirklich abgeglichen haben , sondern stimmten stattdessen mit dem Hash-Wert von ? überein :

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

Jep. Der wahre ASCII-Zeichensatz ist nur die ersten 128 Zeichen (Werte 0 - 127). Und wie wir gerade gesehen haben, der è ist 232. Verwenden Sie also den ASCII Codierung in .NET ist nicht so hilfreich. COLLATE wurde auch nicht verwendet auf der T-SQL-Seite.

Ist es möglich, auf der .NET-Seite eine bessere Codierung zu erhalten? Ja, mithilfe von Encoding.GetEncoding(Int32), wodurch die Codepage angegeben werden kann. Die zu verwendende Codepage kann mit der folgenden Abfrage ermittelt werden (verwenden Sie sys.columns wenn mit einer Spalte statt mit einem Literal oder einer Variablen gearbeitet wird):

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

Die obige Abfrage gibt (für mich) zurück:

Latin1_General_100_CI_AS_SC    1252

Versuchen wir es also mit Codepage 1252:

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

Woo hoo! Wir haben eine Übereinstimmung für VARCHAR Daten, die unsere standardmäßige SQL Server-Sortierung verwenden :). Wenn die Daten natürlich aus einer Datenbank oder einem Feld stammen, das auf eine andere Sortierung festgelegt ist, dann GetEncoding(1252) vielleicht nicht funktionieren und Sie müssen die tatsächlich passende Codepage finden, indem Sie die oben gezeigte Abfrage verwenden (eine Codepage wird über viele Sortierungen hinweg verwendet, daher ist eine andere Sortierung nicht notwendigerweise erforderlich implizieren eine andere Codepage).

Um zu sehen, was die möglichen Codepage-Werte sind und zu welcher Kultur/Gebietsschema sie gehören, sehen Sie sich bitte die Liste der Codepages hier an (die Liste befindet sich im Abschnitt „Bemerkungen“).

Zusätzliche Informationen zu dem, was tatsächlich in NVARCHAR gespeichert ist / NCHAR Felder:

Jedes UTF-16-Zeichen (2 oder 4 Byte) kann gespeichert werden, obwohl das Standardverhalten der integrierten Funktionen davon ausgeht, dass alle Zeichen UCS-2 (jeweils 2 Byte) sind, was eine Teilmenge von UTF-16 ist. Ab SQL Server 2012 ist es möglich, auf eine Reihe von Windows-Sortierungen zuzugreifen, die die als ergänzende Zeichen bezeichneten 4-Byte-Zeichen unterstützen. Verwenden einer dieser Windows-Sortierungen, die auf _SC enden , entweder für eine Spalte oder direkt in einer Abfrage angegeben, ermöglicht es den integrierten Funktionen, die 4-Byte-Zeichen richtig zu verarbeiten.

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

Rückgabe:

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

Wie Sie sehen können, weder DATALENGTH noch HASHBYTES sind betroffen. Weitere Informationen finden Sie auf der MSDN-Seite für Sortierung und Unicode-Unterstützung (insbesondere im Abschnitt „Ergänzende Zeichen“).