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

时间:2016-06-08 12:37:03

标签: c# asp.net encryption asp.net-web-api asp.net-identity

我已将身份框架添加到我的WebApi,并按照此处列出的步骤进行操作:

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

所有这一切都很好。 我遇到的问题是,我的客户端有另一个系统,API与其集成(收集数据)并拥有自己的登录方法。因此,考虑到这一点,我的客户要求我使用CustomPasswordHasher来加密和解密密码。 他们想要做的是能够获得密码哈希并将其转换为实际密码,以便他们可以使用它来登录旧系统(两个密码/帐户都是相同的)。 我知道这是非常不公正的,但我没有选择。

我的问题是,这有多容易? 我找到了一些关于如何创建自定义密码哈希的主题,但没有一个告诉我如何从哈希密码中实际获取密码,它们只显示如何比较。

目前我有这个:

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

如果我真的想,我可以这样做:

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

但这会很疯狂,因为所有密码都会以纯文本形式存储。当然有一种方法来加密"密码和"解密"我打电话的时候?

1 个答案:

答案 0 :(得分:1)

所以,我尽量保证安全。这就是我所做的。 我创建了一个新的提供者:

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

然后我的 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;
    }
}

要使用此 PasswordHasher ,您可以这样调用它:

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

这似乎满足了我的条件。请告诉我是否存在安全隐患!