Saltar al contenido

Hash TSQL md5 diferente a C # .NET md5

Este dilema se puede resolver de diversas formas, pero te mostramos la resolución más completa para nosotros.

Solución:

Si estás lidiando con NVARCHAR / NCHAR datos (que se almacenan como UTF-16 Little Endian), entonces usarías el Unicode codificación, no BigEndianUnicode. En .NET, UTF-16 se llama Unicode mientras que otras codificaciones Unicode se denominan por sus nombres reales: UTF7, UTF8 y UTF32. Por eso, Unicode por sí mismo es Little Endian Opuesto a BigEndianUnicode. ACTUALIZAR: Consulte la sección al final sobre UCS-2 y caracteres suplementarios.

En el lado de la base de datos:

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

En el lado de .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

Sin embargo, esta pregunta se refiere a VARCHAR / CHAR datos, que es ASCII, por lo que las cosas son un poco más complicadas.

En el lado de la base de datos:

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

Ya vemos el lado .NET arriba. A partir de esos valores hash, debería haber dos preguntas:

  • Por qué no alguna de ellos coinciden con el HASHBYTES ¿valor?
  • ¿Por qué el artículo “sqlteam.com” vinculado en la respuesta de @Eric J. muestra que tres de ellos (ASCII, UTF7, y UTF8) todos coinciden con el HASHBYTES ¿valor?

Hay una respuesta que cubre ambas preguntas: Páginas de códigos. La prueba realizada en el artículo “sqlteam” utilizó caracteres ASCII “seguros” que están en el rango de 0 a 127 (en términos del valor int / decimal) que no varían entre las páginas de códigos. Pero el rango 128-255, donde encontramos el carácter “è”, es el Extendido conjunto que varía según la página de códigos (lo que tiene sentido ya que esta es la razón para tener páginas de códigos).

Ahora intenta:

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

Que coincide con el ASCII valor hash (y nuevamente, debido a que el artículo / prueba “sqlteam” usó valores en el rango de 0 a 127, no vieron ningún cambio al usar COLLATE). Genial, ahora finalmente encontramos una manera de combinar VARCHAR / CHAR datos. ¿Todo está bien?

Bueno en realidad no. Echemos un vistazo a lo que realmente estábamos hash:

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

Devoluciones:

TheChar TheASCIIvalue   CharCP1255  TheASCIIvalueCP1255
è       232             ?           63

A ? ? Solo para verificar, ejecute:

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

Ah, entonces la página de códigos 1255 no tiene è personaje, por lo que se traduce como el favorito de todos ?. Pero entonces, ¿por qué coincidía con el valor hash MD5 en .NET cuando se usa la codificación ASCII? ¿Podría ser que en realidad no estuviéramos igualando el valor hash de è, sino que coincidían con el valor hash de ?:

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

Sí. los true El juego de caracteres ASCII es solo los primeros 128 caracteres (valores 0 – 127). Y como acabamos de ver, el è es 232. Entonces, usando el ASCII la codificación en .NET no es tan útil. Tampoco estaba usando COLLATE en el lado de T-SQL.

¿Es posible obtener una mejor codificación en el lado de .NET? Sí, utilizando Encoding.GetEncoding (Int32), que permite especificar la página de códigos. La página de códigos a usar se puede descubrir mediante la siguiente consulta (use sys.columns cuando se trabaja con una columna en lugar de un literal o variable):

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

La consulta anterior devuelve (para mí):

Latin1_General_100_CI_AS_SC    1252

Entonces, intentemos la página de códigos 1252:

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

¡Woo hoo! Tenemos un partido para VARCHAR datos que utilizan nuestra intercalación predeterminada de SQL Server :). Por supuesto, si los datos provienen de una base de datos o un campo configurado en una intercalación diferente, entonces GetEncoding(1252)podría no funciona y tendrá que encontrar la página de códigos coincidente real utilizando la consulta que se muestra arriba (una página de códigos se usa en muchas intercalaciones, por lo que una intercalación diferente no funciona necesariamente implica una página de códigos diferente).

Para ver cuáles son los posibles valores de la página de códigos y a qué cultura / configuración regional pertenecen, consulte la lista de páginas de códigos aquí (la lista se encuentra en la sección “Comentarios”).


Información adicional relacionada con lo que realmente se almacena en NVARCHAR / NCHAR los campos:

Se puede almacenar cualquier carácter UTF-16 (2 o 4 bytes), aunque el comportamiento predeterminado de las funciones integradas asume que todos los caracteres son UCS-2 (2 bytes cada uno), que es un subconjunto de UTF-16. A partir de SQL Server 2012, es posible acceder a un conjunto de intercalaciones de Windows que admiten los caracteres de 4 bytes conocidos como caracteres suplementarios. Usando una de estas intercalaciones de Windows que terminan en _SC, ya sea especificado para una columna o directamente en una consulta, permitirá que las funciones integradas manejen correctamente los caracteres de 4 bytes.

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

Devoluciones:

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

Como ves, tampoco DATALENGTH ni HASHBYTES Son afectados. Para obtener más información, consulte la página de MSDN para compatibilidad con intercalación y Unicode (específicamente la sección “Caracteres complementarios”).

SQL Server usa UCS-2 en lugar de UTF-8 para codificar datos de caracteres.

Si estuviera utilizando un campo NVarChar, lo siguiente funcionaría:

System.Text.Encoding.Unicode.GetBytes("è"); // Updated per @srutzky's comments

Para obtener más información sobre el hash de SQL y C #, consulte

http://weblogs.sqlteam.com/mladenp/archive/2009/04/28/Comparing-SQL-Server-HASHBYTES-function-and-.Net-hashing.aspx

Eres capaz de respaldar nuestra publicación ejecutando un comentario o puntuándolo te lo agradecemos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *