sql-server tsql hash encoding sql-server-2008-r2

sql server - Calcular el hash MD5 de una cadena UTF8



sql-server tsql (2)

Debe crear un UDF para convertir los datos de NVARCHAR a bytes en la representación UTF-8. Digamos que se llama dbo.NCharToUTF8Binary entonces puede hacer:

hashbytes(''md5'', dbo.NCharToUTF8Binary(N''abc'', 1))

Aquí hay un UDF que hará eso:

create function dbo.NCharToUTF8Binary(@txt NVARCHAR(max), @modified bit) returns varbinary(max) as begin -- Note: This is not the fastest possible routine. -- If you want a fast routine, use SQLCLR set @modified = isnull(@modified, 0) -- First shred into a table. declare @chars table ( ix int identity primary key, codepoint int, utf8 varbinary(6) ) declare @ix int set @ix = 0 while @ix < datalength(@txt)/2 -- trailing spaces begin set @ix = @ix + 1 insert @chars(codepoint) select unicode(substring(@txt, @ix, 1)) end -- Now look for surrogate pairs. -- If we find a pair (lead followed by trail) we will pair them -- High surrogate is /uD800 to /uDBFF -- Low surrogate is /uDC00 to /uDFFF -- Look for high surrogate followed by low surrogate and update the codepoint update c1 set codepoint = ((c1.codepoint & 0x07ff) * 0x0800) + (c2.codepoint & 0x07ff) + 0x10000 from @chars c1 inner join @chars c2 on c1.ix = c2.ix -1 where c1.codepoint >= 0xD800 and c1.codepoint <=0xDBFF and c2.codepoint >= 0xDC00 and c2.codepoint <=0xDFFF -- Get rid of the trailing half of the pair where found delete c2 from @chars c1 inner join @chars c2 on c1.ix = c2.ix -1 where c1.codepoint >= 0x10000 -- Now we utf-8 encode each codepoint. -- Lone surrogate halves will still be here -- so they will be encoded as if they were not surrogate pairs. update c set utf8 = case -- One-byte encodings (modified UTF8 outputs zero as a two-byte encoding) when codepoint <= 0x7f and (@modified = 0 OR codepoint <> 0) then cast(substring(cast(codepoint as binary(4)), 4, 1) as varbinary(6)) -- Two-byte encodings when codepoint <= 0x07ff then substring(cast((0x00C0 + ((codepoint/0x40) & 0x1f)) as binary(4)),4,1) + substring(cast((0x0080 + (codepoint & 0x3f)) as binary(4)),4,1) -- Three-byte encodings when codepoint <= 0x0ffff then substring(cast((0x00E0 + ((codepoint/0x1000) & 0x0f)) as binary(4)),4,1) + substring(cast((0x0080 + ((codepoint/0x40) & 0x3f)) as binary(4)),4,1) + substring(cast((0x0080 + (codepoint & 0x3f)) as binary(4)),4,1) -- Four-byte encodings when codepoint <= 0x1FFFFF then substring(cast((0x00F0 + ((codepoint/0x00040000) & 0x07)) as binary(4)),4,1) + substring(cast((0x0080 + ((codepoint/0x1000) & 0x3f)) as binary(4)),4,1) + substring(cast((0x0080 + ((codepoint/0x40) & 0x3f)) as binary(4)),4,1) + substring(cast((0x0080 + (codepoint & 0x3f)) as binary(4)),4,1) end from @chars c -- Finally concatenate them all and return. declare @ret varbinary(max) set @ret = cast('''' as varbinary(max)) select @ret = @ret + utf8 from @chars c order by ix return @ret end

Tengo una tabla SQL en la que almaceno valores de cadena grandes que deben ser únicos. Para garantizar la unicidad, tengo un índice único en una columna en la que almaceno una representación de cadena del hash MD5 de la cadena grande.

La aplicación C # que guarda estos registros utiliza el siguiente método para realizar el hash:

public static string CreateMd5HashString(byte[] input) { var hashBytes = MD5.Create().ComputeHash(input); return string.Join("", hashBytes.Select(b => b.ToString("X"))); }

Para llamar a esto, primero convierto la string al byte[] usando la codificación UTF-8:

// this is what I use in my app CreateMd5HashString(Encoding.UTF8.GetBytes("abc")) // result: 90150983CD24FB0D6963F7D28E17F72

Ahora me gustaría poder implementar esta función de hashing en SQL, usando la función HASHBYTES , pero obtengo un valor diferente:

print hashbytes(''md5'', N''abc'') -- result: 0xCE1473CF80C6B3FDA8E3DFC006ADC315

Esto se debe a que SQL calcula el MD5 de la representación UTF-16 de la cadena. Obtengo el mismo resultado en C # si hago CreateMd5HashString(Encoding.Unicode.GetBytes("abc")) .

No puedo cambiar la forma en que se realiza el hash en la aplicación.

¿Hay alguna manera de hacer que SQL Server calcule el hash MD5 de los bytes UTF-8 de la cadena?

Busqué preguntas similares, intenté usar intercalaciones, pero hasta ahora no tuve suerte.


SQL Server no admite de forma nativa el uso de cadenas UTF-8, y no lo ha hecho durante bastante tiempo . Como notó, NCHAR y NVARCHAR usan UCS-2 en lugar de UTF-8 .

Si insiste en usar la función HASHBYTES , debe poder pasar el byte[] UTF-8 byte[] como VARBINARY de su código C # para preservar la codificación. HASHBYTES acepta VARBINARY en lugar de NVARCHAR . Esto podría lograrse con una función CLR que acepte NVARCHAR y devuelva los resultados de Encoding.UTF8.GetBytes como VARBINARY .

Dicho esto, sugiero mantener este tipo de reglas de negocio aisladas dentro de su aplicación en lugar de la base de datos. Sobre todo porque la aplicación ya está realizando esta lógica.