sql >> Database >  >> RDS >> Sqlserver

TSQL md5-hash anders dan C# .NET md5

Als u te maken heeft met NVARCHAR / NCHAR gegevens (die worden opgeslagen als UTF-16 Little Endian ), dan gebruikt u de Unicode codering, niet BigEndianUnicode . In .NET heet UTF-16 Unicode terwijl andere Unicode-coderingen worden aangeduid met hun werkelijke naam:UTF7, UTF8 en UTF32. Vandaar, Unicode op zichzelf is Little Endian in tegenstelling tot BigEndianUnicode . UPDATE: Zie het gedeelte aan het einde over UCS-2 en aanvullende tekens.

Aan de databasekant:

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

Aan de .NET-kant:

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

Deze vraag heeft echter betrekking op VARCHAR / CHAR data, wat ASCII is, en dus zijn de dingen een beetje ingewikkelder.

Aan de databasekant:

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

We zien hierboven al de .NET-kant. Van die gehashte waarden moeten er twee vragen zijn:

  • Waarom geen van hen komen overeen met de HASHBYTES waarde?
  • Waarom blijkt uit het artikel "sqlteam.com" dat is gelinkt in het antwoord van @Eric J. dat drie van hen (ASCII , UTF7 , en UTF8 ) komen allemaal overeen met de HASHBYTES waarde?

Er is één antwoord dat beide vragen dekt:codepagina's. De test in het "sqlteam"-artikel gebruikte "veilige" ASCII-tekens die in het bereik van 0 - 127 liggen (in termen van de int / decimale waarde) die niet variëren tussen codepagina's. Maar het bereik van 128 - 255 -- waar we het teken "è" vinden -- is de Uitgebreide set die wel verschilt per codepagina (wat logisch is omdat dit de reden is voor het hebben van codepagina's).

Probeer nu:

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

Dat komt overeen met de ASCII gehashte waarde (en nogmaals, omdat het "sqlteam" artikel / test waarden in het bereik van 0 - 127 gebruikte, zagen ze geen veranderingen bij het gebruik van COLLATE ). Geweldig, nu hebben we eindelijk een manier gevonden om VARCHAR te matchen / CHAR gegevens. Alles goed?

Nou niet echt. Laten we eens kijken naar wat we eigenlijk aan het hashen waren:

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

Retourneren:

TheChar TheASCIIvalue   CharCP1255  TheASCIIvalueCP1255
è       232             ?           63

Een ? ? Voer ter verificatie het volgende uit:

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

Ah, dus codepagina 1255 heeft niet de è karakter, dus het wordt vertaald als ieders favoriete ? . Maar waarom kwam dat overeen met de MD5-gehashte waarde in .NET bij gebruik van de ASCII-codering? Zou het kunnen dat we niet echt overeenkomen met de gehashte waarde van è , maar kwamen in plaats daarvan overeen met de gehashte waarde van ? :

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

JEP. De echte ASCII-tekenset is slechts de eerste 128 tekens (waarden 0 - 127). En zoals we net zagen, de è is 232. Dus, met behulp van de ASCII codering in .NET is niet zo handig. Evenmin gebruikte COLLATE aan de T-SQL-kant.

Is het mogelijk om een ​​betere codering te krijgen aan de .NET-kant? Ja, door Encoding.GetEncoding(Int32) te gebruiken, waarmee de codepagina kan worden gespecificeerd. De te gebruiken codepagina kan worden gevonden met behulp van de volgende zoekopdracht (gebruik sys.columns bij het werken met een kolom in plaats van een letterlijke of variabele):

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

De bovenstaande vraag retourneert (voor mij):

Latin1_General_100_CI_AS_SC    1252

Laten we dus codepagina 1252 proberen:

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

Woehoe! We hebben een match voor VARCHAR gegevens die onze standaard SQL Server-sortering gebruiken :). Als de gegevens afkomstig zijn uit een database of een veld dat is ingesteld op een andere sortering, dan is GetEncoding(1252) natuurlijk misschien werkt niet en u zult de daadwerkelijk overeenkomende codepagina moeten vinden met behulp van de bovenstaande zoekopdracht (een codepagina wordt voor veel sorteringen gebruikt, dus een andere sortering is niet noodzakelijk impliceert een andere codepagina).

Om te zien wat de mogelijke codepagina-waarden zijn, en tot welke cultuur / locale ze behoren, raadpleegt u de lijst met codepagina's hier (de lijst staat in het gedeelte "Opmerkingen").

Aanvullende informatie met betrekking tot wat er feitelijk is opgeslagen in NVARCHAR / NCHAR velden:

Elk UTF-16-teken (2 of 4 bytes) kan worden opgeslagen, hoewel het standaardgedrag van de ingebouwde functies ervan uitgaat dat alle tekens UCS-2 zijn (elk 2 bytes), wat een subset is van UTF-16. Vanaf SQL Server 2012 is het mogelijk om toegang te krijgen tot een set Windows-sorteringen die de 4-byte-tekens ondersteunen die bekend staan ​​als aanvullende tekens. Een van deze Windows-sorteringen gebruiken die eindigen op _SC , gespecificeerd voor een kolom of rechtstreeks in een query, zorgen ervoor dat de ingebouwde functies de 4-byte-tekens correct kunnen verwerken.

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

Retourneren:

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

Zoals je kunt zien, is geen van beide DATALENGTH noch HASHBYTES zijn aangetast. Raadpleeg voor meer informatie de MSDN-pagina voor ondersteuning voor sorteren en Unicode (met name de sectie "Aanvullende tekens").



  1. Een Oracle-database migreren naar MySQL op AWS, deel 1

  2. decimaal(s,p) of getal(s,p)?

  3. Unieke beperking op combinatie van twee kolommen?

  4. SQL Server SMO klaagt over ontbrekende DLL