重用现有Microsoft Identity用户表时,密码(哈希)不匹配

Ted*_*erg 6 asp.net asp.net-identity asp.net-core

我们有一个带有Microsoft Identity表的现有SQL数据库,最初由ASP.NET Core应用程序生成.

我们还有一个ASP.NET 4应用程序,它也使用Microsoft Identity.

我们希望ASP.NET 4应用程序能够使用与原始.NET Core应用程序相同的数据库验证登录.

但是,当我们尝试验证密码时,它们不匹配.

我只是猜测.NET 4应用程序生成的密码哈希值无法通过ASP.NET 4应用程序验证,但我不确定从何处开始.:)

.NET Core应用程序中没有自定义密码散列,我很难找到任何可能影响散列的配置?

非常感谢任何帮助或指针!

编辑:似乎这可能是由Identity V2/V3中的不同哈希算法引起的.但是不知道如何在ASP.NET 4应用程序中模仿V3散列算法.

Kri*_*ner 5

根据以下文档:https://github.com/aspnet/Identity/blob/a8ba99bc5b11c5c48fc31b9b0532c0d6791efdc8/src/Microsoft.AspNetCore.Identity/PasswordHasher.cs

    /* =======================
     * HASHED PASSWORD FORMATS
     * =======================
     * 
     * Version 2:
     * PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
     * (See also: SDL crypto guidelines v5.1, Part III)
     * Format: { 0x00, salt, subkey }
     *
     * Version 3:
     * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
     * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
     * (All UInt32s are stored big-endian.)
     */
Run Code Online (Sandbox Code Playgroud)

有一次,身份使用了不同的哈希算法 - 也许它在一个中使用版本2格式,在另一个中使用版本3格式?

该类的构造函数接受选项,您可以尝试调整以获取正确的哈希值?

public PasswordHasher(IOptions<PasswordHasherOptions> optionsAccessor = null)
Run Code Online (Sandbox Code Playgroud)

编辑:

我在这里找到了Identity v2.0源:https://aspnetidentity.codeplex.com/和git repo:https://git01.codeplex.com/aspnetidentity

通过源代码,您会看到它的散列方法.

Crypto.HashPassword.cs

public static string HashPassword(string password)
    {
        if (password == null)
        {
            throw new ArgumentNullException("password");
        }

        // Produce a version 0 (see comment above) text hash.
        byte[] salt;
        byte[] subkey;
        using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount))
        {
            salt = deriveBytes.Salt;
            subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
        }

        var outputBytes = new byte[1 + SaltSize + PBKDF2SubkeyLength];
        Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
        Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, PBKDF2SubkeyLength);
        return Convert.ToBase64String(outputBytes);
    }
Run Code Online (Sandbox Code Playgroud)

与aspnet身份核心中的v2相比:

    private static byte[] HashPasswordV2(string password, RandomNumberGenerator rng)
    {
        const KeyDerivationPrf Pbkdf2Prf = KeyDerivationPrf.HMACSHA1; // default for Rfc2898DeriveBytes
        const int Pbkdf2IterCount = 1000; // default for Rfc2898DeriveBytes
        const int Pbkdf2SubkeyLength = 256 / 8; // 256 bits
        const int SaltSize = 128 / 8; // 128 bits

        // Produce a version 2 (see comment above) text hash.
        byte[] salt = new byte[SaltSize];
        rng.GetBytes(salt);
        byte[] subkey = KeyDerivation.Pbkdf2(password, salt, Pbkdf2Prf, Pbkdf2IterCount, Pbkdf2SubkeyLength);

        var outputBytes = new byte[1 + SaltSize + Pbkdf2SubkeyLength];
        outputBytes[0] = 0x00; // format marker
        Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
        Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, Pbkdf2SubkeyLength);
        return outputBytes;
    }
Run Code Online (Sandbox Code Playgroud)

身份v2散列和身份核心v2散列看起来非常相似,现在与身份核心v3散列进行比较:

    private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested)
    {
        // Produce a version 3 (see comment above) text hash.
        byte[] salt = new byte[saltSize];
        rng.GetBytes(salt);
        byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);

        var outputBytes = new byte[13 + salt.Length + subkey.Length];
        outputBytes[0] = 0x01; // format marker
        WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
        WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
        WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
        Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
        Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
        return outputBytes;
    }
Run Code Online (Sandbox Code Playgroud)

我不会假装理解这些方法中发生了什么,但是从身份v2和身份核心,我们从无参数构造函数转到接受配置选项的构造函数.V2使用SHA1,V3使用SHA256(以及其他内容).

默认情况下,身份核心看起来会使用V3方法进行哈希处理,这在旧版本的身份中不存在 - 这可能是导致问题的原因.

https://github.com/aspnet/Identity/blob/a8ba99bc5b11c5c48fc31b9b0532c0d6791efdc8/src/Microsoft.AspNetCore.Identity/PasswordHasherOptions.cs

请注意,在上面的源代码中,V3用作默认值.

    /// <summary>
    /// Gets or sets the compatibility mode used when hashing passwords.
    /// </summary>
    /// <value>
    /// The compatibility mode used when hashing passwords.
    /// </value>
    /// <remarks>
    /// The default compatibility mode is 'ASP.NET Identity version 3'.
    /// </remarks>
    public PasswordHasherCompatibilityMode CompatibilityMode { get; set; } = PasswordHasherCompatibilityMode.IdentityV3;
Run Code Online (Sandbox Code Playgroud)

不幸的是,这似乎意味着您在身份核心中散列的密码不能在较旧版本的身份中进行散列,因为旧方法未实现.也许你可以创建自己的模仿在v3中完成的工作?