Password (hash) doesn't match when re-using existing Microsoft Identity user tables

谁都会走 提交于 2019-12-07 02:48:55

问题


We have an existing SQL database with Microsoft Identity tables, originally generated by a ASP.NET Core app.

We also have an ASP.NET 4 app, which also uses Microsoft Identity.

We'd like the ASP.NET 4 app to be able to validate logins using the same database as the original .NET Core app.

However, when we try to validate passwords, they don't match.

I'm just guessing that the password hashes generated by the .NET Core app cannot be validated by the ASP.NET 4 app, but I'm not sure where to go from here. :)

There's no custom password hashing in the .NET Core app, and I'm struggling to find any config that might affect hashing?

Any help or pointer is greatly appreciated!

Edit: It seems this may be caused by different hashing algorithms in Identity V2/V3. Not sure how to mimic the V3 hashing algorithm in the ASP.NET 4 app, though.


回答1:


As per the documentation located: 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.)
     */

At one point, identity used a different hashing algorithm - maybe it's using the version 2 format in one, and the version 3 format in the other?

The constructor of the class takes in options, you can try tweaking that to get the correct hash?

public PasswordHasher(IOptions<PasswordHasherOptions> optionsAccessor = null)

EDIT:

I found the Identity v2.0 source here: https://aspnetidentity.codeplex.com/ and git repo: https://git01.codeplex.com/aspnetidentity

Looking through source, you come across its hashing method.

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);
    }

Compared to v2 in aspnet identity core:

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

Identity v2 hashing and identity core v2 hashing seem pretty similar, now compared to identity core v3 hash:

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

I'm not going to pretend to understand what's going on in these methods, but from identity v2, and identity core, we went from a parameterless constructor to one that takes in configuration options. V2 uses SHA1, V3 uses SHA256 (among other things).

It looks like identity core by default would hash using V3 method, which did not exist in the older version of identity - which would be the cause of your problem.

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

Note in the above source, V3 is used as the default.

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

Unfortunately, that looks like it means your passwords that were hashed in identity core cannot be hashed the same in an older version of identity, as that older method was not implemented. Perhaps you could create your own mimicking what was done in v3?



来源:https://stackoverflow.com/questions/38227829/password-hash-doesnt-match-when-re-using-existing-microsoft-identity-user-tab

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!