Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Hash md5 TSQL różni się od C# .NET md5

Jeśli masz do czynienia z NVARCHAR / NCHAR dane (które są przechowywane jako UTF-16 Little Endian ), wtedy użyjesz Unicode kodowanie, a nie BigEndianUnicode . W .NET, UTF-16 nazywa się Unicode podczas gdy inne kodowania Unicode są określane przez ich rzeczywiste nazwy:UTF7, UTF8 i UTF32. Stąd Unicode sam w sobie jest Little Endian w przeciwieństwie do BigEndianUnicode . AKTUALIZACJA: Proszę zapoznać się z sekcją na końcu dotyczącą UCS-2 i znaków uzupełniających.

Po stronie bazy danych:

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

Po stronie .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

Jednak to pytanie dotyczy VARCHAR / CHAR dane, które są ASCII, więc sprawy są nieco bardziej skomplikowane.

Po stronie bazy danych:

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

Widzimy już stronę .NET powyżej. Z tych zaszyfrowanych wartości powinny wynikać dwa pytania:

  • Dlaczego nie żadne z nich pasuje do HASHBYTES wartość?
  • Dlaczego artykuł „sqlteam.com” połączony w odpowiedzi @Eric J. pokazuje, że trzy z nich (ASCII , UTF7 i UTF8 ) wszystkie pasują do HASHBYTES wartość?

Jest jedna odpowiedź, która obejmuje oba pytania:strony kodowe. W teście przeprowadzonym w artykule „sqlteam” użyto „bezpiecznych” znaków ASCII z zakresu od 0 do 127 (pod względem wartości int/dziesiętnych), które nie różnią się między stronami kodowymi. Ale zakres 128-255 – w którym znajdujemy znak „è” – to Rozszerzony zestaw, który różni się w zależności od strony kodowej (co ma sens, ponieważ jest to powód posiadania stron kodowych).

Teraz spróbuj:

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

To pasuje do ASCII zahaszowana wartość (i ponownie, ponieważ artykuł / test "sqlteam" używał wartości z zakresu od 0 do 127, nie zauważyli żadnych zmian podczas używania COLLATE ). Świetnie, teraz w końcu znaleźliśmy sposób na dopasowanie VARCHAR / CHAR dane. Wszystko w porządku?

Cóż, nie za bardzo. Przyjrzyjmy się, co właściwie haszowaliśmy:

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

Zwroty:

TheChar TheASCIIvalue   CharCP1255  TheASCIIvalueCP1255
è       232             ?           63

? ? Aby zweryfikować, uruchom:

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

Ach, więc strona kodowa 1255 nie zawiera è znak, więc zostanie przetłumaczony jako ulubiony ? . Ale w takim razie dlaczego to pasowało do wartości skrótu MD5 w .NET podczas korzystania z kodowania ASCII? Czy to możliwe, że tak naprawdę nie pasowaliśmy do zahaszowanej wartości è , ale zamiast tego pasowały do ​​zahaszowanej wartości ? :

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

Tak. Prawdziwy zestaw znaków ASCII to tylko pierwsze 128 znaków (wartości od 0 do 127). I jak właśnie widzieliśmy, è wynosi 232. Tak więc, używając ASCII kodowanie w .NET nie jest tak pomocne. Nie używałem też COLLATE po stronie T-SQL.

Czy można uzyskać lepsze kodowanie po stronie .NET? Tak, przy użyciu Encoding.GetEncoding(Int32), który umożliwia określenie strony kodowej. Używaną stronę kodową można wykryć za pomocą następującego zapytania (użyj sys.columns podczas pracy z kolumną zamiast literałem lub zmienną):

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

Powyższe zapytanie zwraca (dla mnie):

Latin1_General_100_CI_AS_SC    1252

Wypróbujmy więc stronę kodową 1252:

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

Hura! Mamy dopasowanie dla VARCHAR dane, które używają naszego domyślnego sortowania SQL Server :). Oczywiście, jeśli dane pochodzą z bazy danych lub pola ustawionego na inne sortowanie, to GetEncoding(1252) może nie działa i będziesz musiał znaleźć pasującą stronę kodową za pomocą zapytania pokazanego powyżej (strona kodowa jest używana w wielu sortowaniach, więc inne sortowanie nie koniecznie oznaczać inną stronę kodową).

Aby zobaczyć, jakie są możliwe wartości strony kodowej i jakiej kultury/lokalizacji one dotyczą, zapoznaj się z listą stron kodowych tutaj (lista znajduje się w sekcji „Uwagi”).

Dodatkowe informacje związane z tym, co jest faktycznie przechowywane w NVARCHAR / NCHAR pola:

Można przechowywać dowolny znak UTF-16 (2 lub 4 bajty), chociaż domyślne zachowanie funkcji wbudowanych zakłada, że ​​wszystkie znaki to UCS-2 (każdy po 2 bajty), który jest podzbiorem UTF-16. Począwszy od SQL Server 2012, możliwy jest dostęp do zestawu sortowania systemu Windows, który obsługuje 4-bajtowe znaki znane jako znaki uzupełniające. Korzystanie z jednego z tych sortowań Windows kończących się na _SC , określony dla kolumny lub bezpośrednio w zapytaniu, umożliwi wbudowanym funkcjom prawidłową obsługę 4-bajtowych znaków.

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

Zwroty:

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

Jak widać, ani DATALENGTH ani HASHBYTES są dotknięte. Więcej informacji można znaleźć na stronie MSDN dotyczącej obsługi sortowania i Unicode (w szczególności w sekcji „Znaki uzupełniające”).



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. 4 sposoby zapobiegania przeciążeniu alertów dzięki monitorowaniu serwera SQL

  2. Jak połączyć się z bazą danych MSSQL przy użyciu modułu DBI Perla w systemie Windows?

  3. Kolejność instrukcji SQL Select bez klauzuli Order By

  4. SQL Server — jak zablokować tabelę do czasu zakończenia procedury składowanej

  5. Zwróć typ wyzwalacza DML w tabeli w SQL Server