sql >> Databáze >  >> RDS >> Sqlserver

TSQL md5 hash se liší od C# .NET md5

Pokud máte co do činění s NVARCHAR / NCHAR data (která jsou uložena jako UTF-16 Little Endian ), pak byste použili Unicode kódování, nikoli BigEndianUnicode . V .NET se UTF-16 nazývá Unicode zatímco ostatní kódování Unicode jsou označována svými skutečnými názvy:UTF7, UTF8 a UTF32. Proto Unicode sám o sobě je Little Endian na rozdíl od BigEndianUnicode . AKTUALIZACE: Viz část na konci týkající se UCS-2 a doplňkových znaků.

Na straně databáze:

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

Na straně .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

Tato otázka se však týká VARCHAR / CHAR data, což je ASCII, takže věci jsou trochu složitější.

Na straně databáze:

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

Strana .NET již vidíme výše. Z těchto hashovaných hodnot by měly být dvě otázky:

  • Proč ne žádné z nich odpovídají HASHBYTES hodnotu?
  • Proč článek „sqlteam.com“ odkazovaný v odpovědi @Erica J. ukazuje, že tři z nich (ASCII , UTF7 a UTF8 ) všechny odpovídají HASHBYTES hodnotu?

Existuje jedna odpověď, která pokrývá obě otázky:Kódové stránky. Test provedený v článku "sqlteam" použil "bezpečné" znaky ASCII, které jsou v rozsahu 0 - 127 (ve smyslu hodnoty int / desítkové), které se mezi kódovými stránkami neliší. Ale rozsah 128–255 – kde najdeme znak „è“ – je Rozšířený sada, která se liší podle kódové stránky (což dává smysl, protože to je důvod, proč mít kódové stránky).

Nyní zkuste:

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

To odpovídá ASCII hashovaná hodnota (a znovu, protože článek / test "sqlteam" používal hodnoty v rozsahu 0 - 127, nezaznamenali při použití COLLATE žádné změny ). Skvělé, nyní jsme konečně našli způsob, jak najít shodu s VARCHAR / CHAR data. Všechno v pořádku?

No ne tak úplně. Pojďme se podívat, co jsme vlastně hashovali:

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

Vrátí:

TheChar TheASCIIvalue   CharCP1255  TheASCIIvalueCP1255
è       232             ?           63

? ? Pro ověření spusťte:

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

Aha, takže kódová stránka 1255 nemá è znak, takže bude přeložen jako oblíbený ? . Ale proč se to shodovalo s hodnotou hash MD5 v .NET při použití kódování ASCII? Je možné, že jsme ve skutečnosti neodpovídali hashované hodnotě è , ale místo toho odpovídaly hashované hodnotě ? :

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

Ano. Skutečná znaková sada ASCII je jen prvních 128 znaků (hodnoty 0 - 127). A jak jsme právě viděli, è je 232. Tedy pomocí ASCII kódování v .NET není tak užitečné. Nepoužil jste ani COLLATE na straně T-SQL.

Je možné získat lepší kódování na straně .NET? Ano, pomocí Encoding.GetEncoding(Int32), který umožňuje specifikovat kódovou stránku. Kódovou stránku, kterou chcete použít, lze zjistit pomocí následujícího dotazu (použijte sys.columns při práci se sloupcem místo literálu nebo proměnné):

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

Výše uvedený dotaz vrací (pro mě):

Latin1_General_100_CI_AS_SC    1252

Takže zkusme Kódovou stránku 1252:

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

Hurááá! Máme shodu pro VARCHAR data, která používají naše výchozí řazení SQL Server :). Samozřejmě, pokud data pocházejí z databáze nebo pole nastaveného na jiné řazení, pak GetEncoding(1252) možná nefunguje a budete muset najít skutečnou odpovídající kódovou stránku pomocí dotazu uvedeného výše (kódová stránka se používá v mnoha řazeních, takže jiné řazení nemusí implikovat jinou kódovou stránku).

Chcete-li zjistit, jaké jsou možné hodnoty kódové stránky a jaké kultury / národního prostředí se týkají, prohlédněte si seznam kódových stránek zde (seznam je v sekci "Poznámky").

Další informace související s tím, co je skutečně uloženo v NVARCHAR / NCHAR pole:

Lze uložit jakýkoli znak UTF-16 (2 nebo 4 bajty), i když výchozí chování vestavěných funkcí předpokládá, že všechny znaky jsou UCS-2 (každý 2 bajty), což je podmnožina UTF-16. Počínaje SQL Serverem 2012 je možné přistupovat k sadě kolací systému Windows, které podporují 4bajtové znaky známé jako doplňkové znaky. Pomocí jednoho z těchto řazení Windows končících na _SC , buď zadaný pro sloupec nebo přímo v dotazu, umožní vestavěným funkcím správně zpracovat 4bajtové znaky.

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

Vrátí:

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

Jak vidíte, ani DATALENGTH ani HASHBYTES jsou ovlivněny. Další informace naleznete na stránce MSDN pro podporu řazení a Unicode (konkrétně v sekci "Doplňkové znaky").



  1. INT vs jedinečný identifikátor pro pole ID v databázi

  2. Připojení Pythonu k databázi MySQL pomocí konektoru MySQL a příkladu PyMySQL

  3. 13 blogových článků o doporučených postupech a tipech pro návrh databáze

  4. Jak TRUNCATE() funguje v MariaDB