Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用UTF8生成TSQL MD5_C#_.net_Tsql - Fatal编程技术网

C# 使用UTF8生成TSQL MD5

C# 使用UTF8生成TSQL MD5,c#,.net,tsql,C#,.net,Tsql,我有一个.NET函数MD5,当在“146.185.59.178acu-cell.com”上运行时,它返回F36674ED3DBCB151E1C0DFE4ACDB9F5 public static String MD5(String s) { using (var provider = System.Security.Cryptography.MD5.Create()) { StringBuilder builder = new StringBuilder();

我有一个.NET函数MD5,当在“146.185.59.178acu-cell.com”上运行时,它返回F36674ED3DBCB151E1C0DFE4ACDB9F5

public static String MD5(String s)
{
    using (var provider = System.Security.Cryptography.MD5.Create())
    {
        StringBuilder builder = new StringBuilder();

        foreach (Byte b in provider.ComputeHash(Encoding.UTF8.GetBytes(s)))
            builder.Append(b.ToString("x2").ToLower());

        return builder.ToString();
    }
}
我在TSQL中编写了相同的代码,但出于某种原因,只有varchar返回预期的结果。nvarchar返回不同的md5:f04b83328560f1bd1c08104b83bc30ea

declare @v varchar(150)   = '146.185.59.178acu-cell.com'
declare @nv nvarchar(150) = '146.185.59.178acu-cell.com'


select LOWER(CONVERT(VARCHAR(32), HashBytes('MD5', @v), 2))  
--f36674ed3dbcb151e1c0dfe4acdbb9f5
select LOWER(CONVERT(VARCHAR(32), HashBytes('MD5',@nv), 2)) 
--f04b83328560f1bd1c08104b83bc30ea

不确定这里发生了什么,因为我确实希望nvarchar返回f36674ed3dbcbb151e1c0dfe4acdbb9f5,就像在.NET中一样,因为文本的二进制表示形式不同,所以会得到不同的哈希值。以下查询演示了这一点:

declare@v varchar(150)='146.185.59.178acu-cell.com'
声明@nv nvarchar(150)='146.185.59.178acu-cell.com'
选择convert(varbinary(max),@v--0x3134362E31383352E3532E313736163752D63656C6C2E636F6D
选择转换(变量二进制(最大值),@nv)--0x3100340036002E003100380035002E000350039002E003100370038006100630075002D000630065006C006C002E0063006F006D00

nvarchar的额外0字节是由于它是一个2字节的Unicode数据类型

结果是我需要显式地将NVarChar转换为UTF8

在网上找到此代码:

    CREATE FUNCTION [dbo].[fnUTF8] (
    @String NVarChar(max)
) RETURNS VarChar(max) AS BEGIN
    DECLARE  @Result    VarChar(max)
        ,@Counter   Int
        ,@Len       Int
    SELECT   @Result    = ''
        ,@Counter   = 1
        ,@Len       = Len(@String)
    WHILE (@@RowCount > 0)
        SELECT   @Result    = @Result
                    + CASE  WHEN Code < 128     THEN ''
                        WHEN Code < 2048    THEN Char(192 + Code / 64)
                                    ELSE Char(224 + Code / 4096)
                        END
                    + CASE  WHEN Code < 128     THEN Char(Code)
                        WHEN Code < 2048    THEN Char(128 + Code % 64)
                                    ELSE Char(128 + Code / 64 % 64)
                        END
            ,@Counter   = @Counter + 1
        FROM    (SELECT UniCode(SubString(@String,@Counter,1)) AS Code) C
        WHERE   @Counter <= @Len
    RETURN  @Result
END
GO

你真的应该停止使用md5;这不是一个安全的散列。根据:“MD2、MD4、MD5、SHA和SHA1算法从SQL Server 2016(13.x)开始就被弃用。改用SHA2_256或SHA2_512。较旧的算法将继续工作,但它们会引发弃用事件。”此外,为什么您希望
varchar
nvarchar
的哈希值相同?它们不是相同的数据类型。但确实如此,@IanKemp。包含相同字符的
nvarchar
值和
varchar
值下面的值不相同。像
“我相信varchar和nvarchar是一样的”=N“我相信varchar和nvarchar是一样的”
返回true的唯一原因是
varchar
首先被隐式转换为
nvarchar
。如果要比较它们的基础(二进制)值,它们就不一样了。@Larnu是的,我有一个临时的brainfart;)还要注意的是,SQL Server(2019年之前)不支持UTF-8,因此,如果您包含除普通旧ASCII字符以外的任何字符,那么即使使用
VARCHAR
——通常的默认(
Latin1
)排序规则更像Windows-1252。使用
编码。Unicode
至少与
NVARCHAR
保持一致。我们知道SQL字符串不同的原因,这不是问题所在。问题是为什么.NET Unicode字符串不等于SQL Unicode字符串,因为顾名思义,UTF-8编码是8位的。MSSQL的nvarchar是UCS-2,即2字节或16位。如果要在两种平台上获得相同的结果,请在C#中使用
编码.bigendiaUnicode
,这与UCS-2非常接近,可以在大多数情况下使用。@user1165885但您从未尝试计算.NET字符串的has。Windows和.NET总是使用UTF16LE。您的代码使用
Encoding.UTF8
而不是
Encoding.UTF16
这在SQL Server 2019中要简单得多:
选择HASHBYTES('MD5',CONVERT(VARCHAR(MAX),N'146.185.59.178acu-cell.com')比较拉丁语1\u General\u CI\u AS\u SC\u UTF8)
。请注意,这样的函数可能会有糟糕的性能,如果需要将其应用于多行,这可能是一个问题。在这种情况下,在客户端执行哈希并仅向SQL Server提供字节是一个更好的主意(至少不是因为SQL Server会坚持为不再安全的哈希引发弃用事件)。还要注意,如果您的输入仅包含ASCII字符,则使用(例如)将其简单转换为
VARCHAR
一个
Latin1\u General\u CI\u AS
排序就足够了,因为这两种编码都是ASCII兼容的。只有在使用非ASCII字符时,才需要使用UTF-8。@Jeroenmoster谢谢!不幸的是,我们使用的是SQL 2016(13.x),为什么您坚持使用UTF8而不是UTF16?如果使用
Encoding.Unicode
,则无需编写任何此类(慢)代码。如果您有使用UTF8的正当理由,一个快速的替代方法是使用与客户端相同的代码在C#中编写SQLCLR UDF。@PanagiotisKanavos,因为在服务器上有数亿条旧记录是使用.NET编码的,该服务器使用的是OP中描述的UTF8。因此,为了实现这一点,我们需要将自己与现有数据对齐。。。
select LOWER(CONVERT(VARCHAR(32), HashBytes('MD5', [dbo].[fnUTF8](@nv)), 2))