简体   繁体   English

身份框架和自定义密码哈希

[英]Identity Framework and Custom Password Hasher

I have added identity framework to my WebApi and followed the steps outlined here: 我已将标识框架添加到WebApi中,并按照此处概述的步骤操作:

http://bitoftech.net/2015/01/21/asp-net-identity-2-with-asp-net-web-api-2-accounts-management/ http://bitoftech.net/2015/01/21/asp-net-identity-2-with-asp-net-web-api-2-accounts-management/

All of this is working fine. 所有这一切都很好。 The problem I have, is that my client has another system of which the API integrates with (to collect data) and that has it's own login methods. 我遇到的问题是,我的客户端有另一个系统,API与其集成(收集数据)并拥有自己的登录方法。 So, with that in mind, my client has asked me to use a CustomPasswordHasher to encrypt and decrypt passwords. 因此,考虑到这一点,我的客户要求我使用CustomPasswordHasher来加密和解密密码。 What they would like to do is be able to get a password hash and convert it into the actual password so that they can use it to log into the old system (both passwords / accounts will be the same). 他们想要做的是能够获得密码哈希并将其转换为实际密码,以便他们可以使用它来登录旧系统(两个密码/帐户都是相同的)。 I know this is very unothadox but I have no choice in the matter. 我知道这是非常不公正的,但我在此事上别无选择。

My question is, how easy is this to do? 我的问题是,这有多容易? I have found a few topics on how to create a custom password hasher, but none show me how to actually get the password from the hashed password, they only show how to compare. 我找到了一些关于如何创建自定义密码哈希的主题,但没有一个告诉我如何从哈希密码中实际获取密码,它们只显示如何比较。

Currently I have this: 目前我有这个:

public class PasswordHasher : IPasswordHasher
{
    private readonly int _saltSize;
    private readonly int _bytesRequired;
    private readonly int _iterations;

    public PasswordHasher()
    {
        this._saltSize = 128 / 8;
        this._bytesRequired = 32;
        this._iterations = 1000;
    }

    public string HashPassword(string password)
    {

        // Create our defaults
        var array = new byte[1 + this._saltSize + this._bytesRequired];

        // Try to hash our password
        using (var pbkdf2 = new Rfc2898DeriveBytes(password, this._saltSize, this._iterations))
        {
            var salt = pbkdf2.Salt;
            Buffer.BlockCopy(salt, 0, array, 1, this._saltSize);

            var bytes = pbkdf2.GetBytes(this._bytesRequired);
            Buffer.BlockCopy(bytes, 0, array, this._saltSize + 1, this._bytesRequired);
        }

        // Return the password base64 encoded
        return Convert.ToBase64String(array);
    }

    public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
    {

        // Throw an error if any of our passwords are null
        ThrowIf.ArgumentIsNull(() => hashedPassword, () => providedPassword);

        // Get our decoded hash
        var decodedHashedPassword = Convert.FromBase64String(hashedPassword);

        // If our password length is 0, return an error
        if (decodedHashedPassword.Length == 0)
            return PasswordVerificationResult.Failed;

        var t = decodedHashedPassword[0];

        // Do a switch
        switch (decodedHashedPassword[0])
        {
            case 0x00:
                return PasswordVerificationResult.Success;

            default:
                return PasswordVerificationResult.Failed;
        }
    }

    private bool VerifyHashedPassword(byte[] hashedPassword, string password)
    {

        // If we are not matching the original byte length, then we do not match
        if (hashedPassword.Length != 1 + this._saltSize + this._bytesRequired)
            return false;

        //// Get our salt
        //var salt = pbkdf2.Salt;
        //Buffer.BlockCopy(salt, 0, array, 1, this._saltSize);

        //var bytes = pbkdf2.GetBytes(this._bytesRequired);
        //Buffer.BlockCopy(bytes, 0, array, this._saltSize + 1, this._bytesRequired);

        return true;
    }
}

If I really wanted to I could just do this: 如果我真的想,我可以这样做:

public class PasswordHasher : IPasswordHasher
{
    public string HashPassword(string password)
    {
        // Do no hashing
        return password;
    }

    public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
    {

        // Throw an error if any of our passwords are null
        ThrowIf.ArgumentIsNull(() => hashedPassword, () => providedPassword);

        // Just check if the two values are the same
        if (hashedPassword.Equals(providedPassword))
            return PasswordVerificationResult.Success;

        // Fallback
        return PasswordVerificationResult.Failed;
    }
}

but that would be crazy, because all the passwords would be stored as plain text. 但那会很疯狂,因为所有密码都会以纯文本形式存储。 Surely there is a way to "encrypt" the password and "decrypt" it when I make a call? 当然有一种方法可以“加密”密码并在我打电话时“解密”它?

So, I have tried to be as secure as possible. 所以,我尽量保证安全。 This is what I have done. 这就是我所做的。 I created a new provider: 我创建了一个新的提供者:

public class AdvancedEncryptionStandardProvider
{

    // Private properties
    private readonly ICryptoTransform _encryptor, _decryptor;
    private UTF8Encoding _encoder;

    /// <summary>
    /// Default constructor
    /// </summary>
    /// <param name="key">Our shared key</param>
    /// <param name="secret">Our secret</param>
    public AdvancedEncryptionStandardProvider(string key, string secret)
    {

        // Create our encoder
        this._encoder = new UTF8Encoding();

        // Get our bytes
        var _key = _encoder.GetBytes(key);
        var _secret = _encoder.GetBytes(secret);

        // Create our encryptor and decryptor
        var managedAlgorithm = new RijndaelManaged();
        managedAlgorithm.BlockSize = 128;
        managedAlgorithm.KeySize = 128;

        this._encryptor = managedAlgorithm.CreateEncryptor(_key, _secret);
        this._decryptor = managedAlgorithm.CreateDecryptor(_key, _secret);
    }

    /// <summary>
    /// Encrypt a string
    /// </summary>
    /// <param name="unencrypted">The un-encrypted string</param>
    /// <returns></returns>
    public string Encrypt(string unencrypted)
    {
        return Convert.ToBase64String(Encrypt(this._encoder.GetBytes(unencrypted)));
    }

    /// <summary>
    /// Decrypt a string
    /// </summary>
    /// <param name="encrypted">The encrypted string</param>
    /// <returns></returns>
    public string Decrypt(string encrypted)
    {
        return this._encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
    }

    /// <summary>
    /// Encrypt some bytes
    /// </summary>
    /// <param name="buffer">The bytes to encrypt</param>
    /// <returns></returns>
    public byte[] Encrypt(byte[] buffer)
    {
        return Transform(buffer, this._encryptor);
    }

    /// <summary>
    /// Decrypt some bytes
    /// </summary>
    /// <param name="buffer">The bytes to decrypt</param>
    /// <returns></returns>
    public byte[] Decrypt(byte[] buffer)
    {
        return Transform(buffer, this._decryptor);
    }

    /// <summary>
    /// Writes bytes to memory
    /// </summary>
    /// <param name="buffer">The bytes</param>
    /// <param name="transform"></param>
    /// <returns></returns>
    protected byte[] Transform(byte[] buffer, ICryptoTransform transform)
    {

        // Create our memory stream
        var stream = new MemoryStream();

        // Write our bytes to the stream
        using (var cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }

        // Retrun the stream as an array
        return stream.ToArray();
    }
}

Then my PasswordHasher , I changed to this: 然后我的PasswordHasher ,我改为:

public class PasswordHasher : IPasswordHasher
{

    // Private properties
    private readonly AdvancedEncryptionStandardProvider _provider;

    public PasswordHasher(AdvancedEncryptionStandardProvider provider)
    {
        this._provider = provider;
    }

    public string HashPassword(string password)
    {
        // Do no hashing
        return this._provider.Encrypt(password);
    }

    public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
    {

        // Throw an error if any of our passwords are null
        ThrowIf.ArgumentIsNull(() => hashedPassword, () => providedPassword);

        // Just check if the two values are the same
        if (hashedPassword.Equals(this.HashPassword(providedPassword)))
            return PasswordVerificationResult.Success;

        // Fallback
        return PasswordVerificationResult.Failed;
    }
}

To use this PasswordHasher , you invoke it like this: 要使用此PasswordHasher ,您可以像这样调用它:

var passwordHasher = new PasswordHasher(new AdvancedEncryptionStandardProvider(ConfigurationManager.AppSettings["as:key"], ConfigurationManager.AppSettings["as:secret"]));

This seems to satisfy my conditions. 这似乎满足了我的条件。 Let me know if there are security risks please! 如果有安全隐患请告诉我!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM