我已将标识框架添加到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;
}
}
但那会很疯狂,因为所有密码都会以纯文本形式存储.当然有一种方法可以“加密”密码并在我打电话时“解密”它?
最佳答案 所以,我尽量保证安全.这就是我所做的.
我创建了一个新的提供者:
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"]));
这似乎满足了我的条件.如果有安全隐患请告诉我!