【问题标题】:Generate ASP.Net Membership password hash in pure T-SQL在纯 T-SQL 中生成 ASP.Net Membership 密码哈希
【发布时间】:2011-02-17 19:56:41
【问题描述】:

我正在尝试在 ASP.Net Membership 系统中创建默认 SHA-1 密码散列的纯 t-sql 表示。理想情况下,我会得到这样的结果:

UserName           Password              GeneratedPassword
cbehrens           34098kw4D+FKJ==       34098kw4D+FKJ==

注意:那是伪造的 base-64 文本。我有正确往返的 base64_encode 和解码函数。这是我的尝试,但不起作用:

SELECT UserName, Password, dbo.base64_encode(HASHBYTES('SHA1', dbo.base64_decode(PasswordSalt) +  'test')) As TestPassword FROM aspnet_Users U JOIN aspnet_membership M ON U.UserID = M.UserID

我在这个主题上尝试了许多变体,但都无济于事。我需要在纯 T-Sql 中执行此操作;涉及控制台应用程序或类似的东西会使工作加倍。

因此,如果有人能提供从 ASP.Net 会员资料中复制该密码的确切语法,我将不胜感激。

【问题讨论】:

  • 所以要明确一点;问题是PasswordGeneratedPassword 不匹配?
  • 是的。我无法找出精确的 T-SQL 来重现 .net Membership 代码的结果。
  • 不……而且还是错了。我今天似乎很难表达自己。
  • 您能否发布您的编码/解码功能,以便我可以使用它们进行测试?
  • 我从这里得到了它们:sqlservercentral.com/scripts/Miscellaneous/31520

标签: asp.net tsql sql-server-2008 hash asp.net-membership


【解决方案1】:

我通过从这里 ASP.NET Identity default Password Hasher, how does it work and is it secure? 逆向 C# 代码和从这里 Is there a SQL implementation of PBKDF2? 一些奇妙的 PBKDF2 SQL 函数编写了一个散列存储过程

首先创建取自Is there a SQL implementation of PBKDF2?的这两个函数

create FUNCTION [dbo].[fn_HMAC]
(
        @hash_algorithm varchar(25),
        @key VARCHAR(MAX),
        @message VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)


AS
BEGIN
    --HASH key if longer than 16 characters
    IF(LEN(@key) >64)
        SET @key = HASHBYTES(@hash_algorithm,@key)


    DECLARE @i_key_pad VARCHAR(MAX), @o_key_pad VARCHAR(MAX), @position INT
        SET @position = 1
        SET @i_key_pad = ''
        SET @o_key_pad = ''

    --splice ipad & opod with key
    WHILE @position <= LEN(@key)
       BEGIN
        SET @i_key_pad = @i_key_pad + CHAR(ASCII(SUBSTRING(@key, @position, 1)) ^ 54) 
        SET @o_key_pad = @o_key_pad + CHAR(ASCII(SUBSTRING(@key, @position, 1)) ^ 92) 
        SET @position = @position + 1
       END 

    --pad i_key_pad & o_key_pad
        SET @i_key_pad = LEFT(@i_key_pad + REPLICATE('6',64),64)
        SET @o_key_pad = LEFT(@o_key_pad + REPLICATE('\',64),64)


RETURN HASHBYTES(@hash_algorithm,CONVERT(VARBINARY(MAX),@o_key_pad) + HASHBYTES(@hash_algorithm,@i_key_pad + @message))

END

GO

CREATE function [dbo].[fn_PBKDF2] 
(
@hash_algorithm varchar(25),
@password varchar(max),
@salt varchar(max),
@rounds int,
@outputbytes int
)
returns varchar(max)
as
begin

declare @hlen int
select @hlen = len(HASHBYTES(@hash_algorithm, 'test'))
declare @l int 
SET @l = (@outputbytes +@hLen -1)/@hLen
declare @r int 
SET @r = @outputbytes - (@l - 1) * @hLen
declare @t varchar(max), @u varchar(max), @block1 varchar(max)

declare @output varchar(max) 
SET @output = ''

declare @i int 
SET @i = 1
while @i <= @l
    begin
    set @block1 = @salt +cast(cast(@i as varbinary(4)) as varchar(4))
    set @u = dbo.fn_HMAC(@hash_algorithm,@password,@block1)
    set @t = @u

    declare @j int 
    SET @j = 1
    while @j < @rounds
        begin
        set @u = dbo.fn_HMAC(@hash_algorithm,@password,@u)


        declare @k int 
        SET @k = 0 
        DECLARE @workstring varchar(max) 
        SET @workstring = ''
        while @k < @hLen
            begin
            set @workstring = @workstring + char(ascii(substring(@u,@k+1,1))^ascii(substring(@t,@k+1,1)))
            set @k = @k + 1
            end
        set @t = @workstring
        set @j = @j + 1
        end

        select @output = @output + case when @i = @l then left(@t,@r) else @t end
    set @i = @i + 1
    end

  return master.dbo.fn_varbintohexstr(convert(varbinary(max), @output ))


end
GO

然后创建存储过程以生成哈希密码

CREATE PROCEDURE [dbo].[EncryptPassword2]
    @passwordIn AS VARCHAR(MAX),
    @passwordOut VARCHAR(max) OUTPUT
AS

        -- Generate 16 byte salt
    DECLARE @saltVarBin VARBINARY(max)
    SET @saltVarBin = (SELECT CAST(newid() AS binary(16)))

    -- Base64 encode the salt
    DECLARE @saltOut VARCHAR(max)
    SET @saltOut = cast('' as xml).value('xs:base64Binary(sql:variable("@saltVarBin"))', 'varchar(max)')

    -- Decode salt to pass to function fn_PBKDF2
    DECLARE @decodedsalt varchar(max)
    SET @decodedsalt = convert(varchar(max),(SELECT CAST('' as xml).value('xs:base64Binary(sql:variable("@saltOut"))', 'varbinary(max)')))

    -- Build the password binary string from 00 + salt binary string + password binary string created by 32 byte 1000 iteration ORC_PBKDF2 hashing
    DECLARE @passwordVarBinStr VARCHAR(max)
    -- Identity V1.0 and V2.0 Format: { 0x00, salt, subkey } 
    SET @passwordVarBinStr = '0x00' + REPLACE(master.dbo.fn_varbintohexstr(@saltVarBin) + (SELECT dbo.fn_PBKDF2('sha1', @passwordIn, @decodedsalt, 1000, 32)),'0x','')
    -- Identity V3.0 Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey } (comment out above line and uncomment below line)
    --SET @passwordVarBinStr = '0x01000000010000271000000010' + REPLACE(master.dbo.fn_varbintohexstr(@saltVarBin) + (SELECT dbo.fn_PBKDF2('SHA2_256', @passwordIn, @decodedsalt,10000, 32)),'0x','')


    -- Convert the password binary string to base 64
    DECLARE @passwordVarBin VARBINARY(max)
    SET @passwordVarBin =  (select cast('' as xml).value('xs:hexBinary( substring(sql:variable("@passwordVarBinStr"), sql:column("t.pos")) )', 'varbinary(max)') from (select case substring(@passwordVarBinStr, 1, 2) when '0x' then 3 else 0 end) as t(pos))
    SET @passwordOut = cast(''as xml).value('xs:base64Binary(sql:variable("@passwordVarBin"))', 'varchar(max)')

RETURN

最后执行存储过程使用

DECLARE @NewPassword varchar(100)
DECLARE @EncryptPassword VARCHAR(max)

select @NewPassword = 'password12344'                                           

EXECUTE EncryptPassword2 @NewPassword, @PasswordOut = @EncryptPassword OUTPUT;

PRINT @EncryptPassword

请注意,存储过程可能需要针对更高版本的 SQL Server 进行更改,因为这是专门为 2005 年编写的,我相信在更高版本中转换为 base64 会有所不同。

【讨论】:

  • 抱歉回复旧帖,支持SHA256(ASP.NET Identity V3)吗?
  • 它是为 v1.0 编写的。它需要移动到支持 SHA2_256 的 SQL 2012 并且格式与 v3.0 格式完全不同:{ 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }跨度>
  • 要使上述工作适用于 v3.0,请将行 SET @passwordVarBinStr = '0x00' + REPLACE(master.dbo.fn_varbintohexstr(@saltVarBin) + (SELECT dbo.fn_PBKDF2('sha1', @passwordIn, @decodedsalt, 1000, 32)),'0x','') 更改为 SET @passwordVarBinStr = '0x01000000010000271000000010' + REPLACE(master.dbo.fn_varbintohexstr(@saltVarBin) + (SELECT dbo.fn_PBKDF2('SHA2_256', @passwordIn, @decodedsalt,10000, 32)),'0x','') 并在 SQL 2012 或更高版本上运行。在这里使用迈克尔的答案stackoverflow.com/questions/2138429/…
  • 谢谢戴夫。我会试试看。
【解决方案2】:

如果您运行的是 2005 或更高版本,则可以创建 CLR (.NET) UDF:

[SqlFunction(
  IsDeterministic = true, IsPrecise = true, 
  DataAccess = DataAccessKind.None,
  SystemDataAccess = SystemDataAccessKind.None
)]
public static string EncodePassword(string pass, string salt) {
  byte[] bytes = Encoding.Unicode.GetBytes(pass);
  byte[] src = Convert.FromBase64String(salt);
  byte[] dst = new byte[src.Length + bytes.Length];
  Buffer.BlockCopy(src, 0, dst, 0, src.Length);
  Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
  using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) {
    return Convert.ToBase64String(sha1.ComputeHash(dst));
  }
}

你需要在你的类中包含以下命名空间:

using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;

类必须是public

构建 .dll,然后运行以下(每个要调用 UDF 的数据库)SQL 语句:

sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO

IF OBJECT_ID (N'dbo.EncodePassword', N'FS') IS NOT NULL
  DROP FUNCTION dbo.EncodePassword;    
IF EXISTS (SELECT name FROM sys.assemblies WHERE name='UDF')
DROP ASSEMBLY UDF

CREATE ASSEMBLY UDF FROM 'FULL_PATH_TO.dll' WITH PERMISSION_SET=SAFE    
GO

CREATE FUNCTION EncodePassword(
  @pass NVARCHAR(4000),
  @salt NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
-- return NULL if any input parameter(s) are NULL
WITH RETURNS NULL ON NULL INPUT
AS
EXTERNAL NAME UDF.[NAMESPACE.CLASSNAME].EncodePassword
GO

显然,将“NAMESPACE.CLASSNAME”替换为命名空间(如果有)和您的类的名称。你可能想弄乱输入参数和返回值的大小。

然后用 T-SQL 调用 UDF:

SELECT UserName,Password
,dbo.EncodePassword('PASSWORD', PasswordSalt) As TestPassword 
FROM aspnet_Users U 
JOIN aspnet_membership M ON U.UserID = M.UserID

为我工作:)

【讨论】:

  • 完美运行。绝对出色的作品。非常非常感谢。
  • 我本打算立即发布此内容,但我没想到。在肉眼看来,原始和生成的哈希看起来是一样的,但是生成的哈希右填充了空格(以填充 nvarchar(4000))。修复很简单:SELECT RTRIM(dbo.EncodePassword('PASSWORD', PasswordSalt)...
  • 我的错。 (复制/粘贴)尝试替换返回值 - 而不是 RETURNS NCHAR(4000) 尝试 RETURNS NVARCHAR(4000)。未经测试,但应该可以工作。
【解决方案3】:

您可以在 SQL 中创建此函数,而不是使用 CLR。在这个页面上你会发现非常好的例子:

http://svakodnevnica.com.ba/index.php?option=com_kunena&func=view&catid=4&id=4&Itemid=5&lang=en#7

附: byte[] src = Convert.FromBase64String(salt);是正确的方法...

狐狸

【讨论】:

  • 我能够很容易地根据我的需要定制该页面中包含的示例。对于某些人来说,这可能是唯一的选择,因为 Azure 不支持 CLR 扩展。
  • 我让它与单行 T-SQL 和上面帖子中的两个编码/解码函数一起工作。不需要其他任何东西:select dbo.adm_base64encode(Hashbytes('SHA1',dbo.adm_base64decode(N'')+convert(varbinary(max),N'
  • 上面列出的网址不再有效。
  • 代码的一个版本似乎在social.msdn.microsoft.com/Forums/vstudio/en-US/…,但我还没有尝试过。
【解决方案4】:

OP 请求“纯”sql - 我认为使用 CLR 是作弊 ;) 我很固执,必须自己弄清楚,所以这就是我所做的。

注意:先备份!!

Select * into dbo.aspnet_Membership_BACKUP from [dbo].[aspnet_Membership]

计算哈希的函数:

/*
    Create compatible hashes for the older style ASP.Net Membership

    Credit for Base64 encode/decode: http://stackoverflow.com/questions/5082345/base64-encoding-in-sql-server-2005-t-sql
*/
Create Function dbo.AspNetHashCreate (@clearPass nvarchar(64), @encodedSalt nvarchar(64))
Returns nvarchar(128)
as
begin
    declare @binSalt varbinary(128)
    declare @binPass varbinary(128)

    declare @result nvarchar(64)

    Select @binPass = CONVERT(VARBINARY(128), @clearPass)

    --  Passed salt is Base64 so decode to bin, then we'll combine/append it with password
    Select @binSalt = CAST(N'' as XML).value('xs:base64Binary(sql:column("bin"))','VARBINARY(128)') 
        from (Select @encodedSalt as bin) as temp;

    --  Hash the salt + pass, then convert to Base64 for the output
    Select @result = CAST(N'' as XML).value('xs:base64Binary(xs:hexBinary(sql:column("bin")))', 'NVARCHAR(64)')
        from (Select HASHBYTES('SHA1', @binSalt + @binPass) as bin) as temp2;

    --  Debug, check sizes
    --Select DATALENGTH(@binSalt), DATALENGTH(@binPass), DATALENGTH(@binSalt + @binPass)

    return @result
end

我正在将会员数据库从“清除”密码更改为更安全的散列格式 - 像这样称呼它:

Update [dbo].[aspnet_Membership] set PasswordFormat = 1, Password = dbo.AspNetHashCreate(password, PasswordSalt) where PasswordFormat = 0

即使我的数据库最初设置为“清除”密码,盐值也是为每条记录创建的,但是,如果由于某种原因您没有盐值,您可以使用以下方法创建它们:

/*
    Create compatible salts for the older style ASP.Net Membership (just a 16 byte random number in Base64)

    Note: Can't use newId() inside function so just call it like so: dbo.AspNetSaltCreate(newId())

    Credit for Base64 encode: http://stackoverflow.com/questions/5082345/base64-encoding-in-sql-server-2005-t-sql
*/
Create Function dbo.AspNetSaltCreate (@RndId uniqueidentifier)
    Returns nvarchar(24)
as
begin
    return
        (Select CAST(N'' as XML).value('xs:base64Binary(xs:hexBinary(sql:column("bin")))', 'NVARCHAR(64)')
            from (select cast(@RndId as varbinary(16)) as bin) as temp)
end

然后像这样使用它:

Update [dbo].[aspnet_Membership] set PasswordSalt = dbo.AspNetSaltCreate(newId()) where PasswordSalt = ''

享受吧!

【讨论】:

  • 这太棒了!我正在迁移旧的 ASP.NET 成员资格,但找不到测试我得到的迁移代码是否正确的方法,这有助于我验证它:)
【解决方案5】:

根据this SO post,,这是他们用来编码/散列您的密码/盐的过程。

public string EncodePassword(string pass, string salt)
{
    byte[] bytes = Encoding.Unicode.GetBytes(pass); //HERE
    byte[] src = Encoding.Unicode.GetBytes(salt); //and HERE
    byte[] dst = new byte[src.Length + bytes.Length];
    Buffer.BlockCopy(src, 0, dst, 0, src.Length);
    Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
    HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
    byte[] inArray = algorithm.ComputeHash(dst); //then they has the bytes not the string...
    return Convert.ToBase64String(inArray);
}

我可能是错的,但您似乎错过了获取密码和盐字节的步骤。您可以尝试添加它并查看它是否有效?

【讨论】:

  • 是的,我一直在看。我已经尝试将 CONVERT 转换为 varbinary,它没有任何区别 - 它返回的结果与我不调用 convert 函数时完全相同。如果在 T-SQL 中有另一种表示方式,我不知道。
猜你喜欢
  • 2011-02-10
  • 2011-02-19
  • 1970-01-01
  • 2016-02-06
  • 2015-05-12
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 2011-04-01
相关资源
最近更新 更多