зеркало из https://github.com/aspnet/Identity.git
Port PasswordsHasher, crypto only in Net45
This commit is contained in:
Родитель
edb709ce9d
Коммит
5128a030da
|
@ -1,10 +1,105 @@
|
|||
#if K10
|
||||
#if NET45
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
//using System.Security.Cryptography;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNet.Identity
|
||||
{
|
||||
internal static class Crypto
|
||||
{
|
||||
private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes
|
||||
private const int PBKDF2SubkeyLength = 256/8; // 256 bits
|
||||
private const int SaltSize = 128/8; // 128 bits
|
||||
|
||||
/* =======================
|
||||
* HASHED PASSWORD FORMATS
|
||||
* =======================
|
||||
*
|
||||
* Version 0:
|
||||
* 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 }
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// hashedPassword must be of the format of HashWithPassword (salt + Hash(salt+input)
|
||||
public static bool VerifyHashedPassword(string hashedPassword, string password)
|
||||
{
|
||||
if (hashedPassword == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (password == null)
|
||||
{
|
||||
throw new ArgumentNullException("password");
|
||||
}
|
||||
|
||||
var hashedPasswordBytes = Convert.FromBase64String(hashedPassword);
|
||||
|
||||
// Verify a version 0 (see comment above) text hash.
|
||||
|
||||
if (hashedPasswordBytes.Length != (1 + SaltSize + PBKDF2SubkeyLength) || hashedPasswordBytes[0] != 0x00)
|
||||
{
|
||||
// Wrong length or version header.
|
||||
return false;
|
||||
}
|
||||
|
||||
var salt = new byte[SaltSize];
|
||||
Buffer.BlockCopy(hashedPasswordBytes, 1, salt, 0, SaltSize);
|
||||
var storedSubkey = new byte[PBKDF2SubkeyLength];
|
||||
Buffer.BlockCopy(hashedPasswordBytes, 1 + SaltSize, storedSubkey, 0, PBKDF2SubkeyLength);
|
||||
|
||||
byte[] generatedSubkey;
|
||||
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, PBKDF2IterCount))
|
||||
{
|
||||
generatedSubkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
|
||||
}
|
||||
return ByteArraysEqual(storedSubkey, generatedSubkey);
|
||||
}
|
||||
|
||||
// Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized.
|
||||
[MethodImpl(MethodImplOptions.NoOptimization)]
|
||||
private static bool ByteArraysEqual(byte[] a, byte[] b)
|
||||
{
|
||||
if (ReferenceEquals(a, b))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a == null || b == null || a.Length != b.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var areSame = true;
|
||||
for (var i = 0; i < a.Length; i++)
|
||||
{
|
||||
areSame &= (a[i] == b[i]);
|
||||
}
|
||||
return areSame;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
namespace Microsoft.AspNet.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements password hashing methods
|
||||
/// </summary>
|
||||
public class PasswordHasher : IPasswordHasher
|
||||
{
|
||||
/// <summary>
|
||||
/// Hash a password
|
||||
/// </summary>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
public virtual string HashPassword(string password)
|
||||
{
|
||||
#if NET45
|
||||
return Crypto.HashPassword(password);
|
||||
#else
|
||||
return password;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that a password matches the hashedPassword
|
||||
/// </summary>
|
||||
/// <param name="hashedPassword"></param>
|
||||
/// <param name="providedPassword"></param>
|
||||
/// <returns></returns>
|
||||
public virtual PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
|
||||
{
|
||||
#if NET45
|
||||
return Crypto.VerifyHashedPassword(hashedPassword, providedPassword) ? PasswordVerificationResult.Success : PasswordVerificationResult.Failed;
|
||||
#else
|
||||
return hashedPassword == providedPassword ? PasswordVerificationResult.Success : PasswordVerificationResult.Failed;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Identity
|
|||
}
|
||||
Store = store;
|
||||
UserValidator = new UserValidator<TUser, TKey>(this);
|
||||
//PasswordHasher = new PasswordHasher();
|
||||
PasswordHasher = new PasswordHasher();
|
||||
//ClaimsIdentityFactory = new ClaimsIdentityFactory<TUser, TKey>();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"version" : "0.1-alpha-*",
|
||||
"dependencies": {
|
||||
"System.Collections.Immutable" : "1.1.15.0",
|
||||
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*",
|
||||
"System.Security.Claims" : "0.1-alpha-*"
|
||||
},
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
#if NET45
|
||||
using System.Security.Claims;
|
||||
#else
|
||||
using System.Security.ClaimsK;
|
||||
#endif
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
@ -53,21 +49,21 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
|
|||
Assert.Equal(providerKey, logins.First().ProviderKey);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public async Task CreateUserLoginAndAddPasswordTest()
|
||||
//{
|
||||
// var manager = CreateManager();
|
||||
// var login = new UserLoginInfo("Provider", "key");
|
||||
// var user = new InMemoryUser("CreateUserLoginAddPasswordTest");
|
||||
// UnitTestHelper.IsSuccess(await manager.Create(user));
|
||||
// UnitTestHelper.IsSuccess(await manager.AddLogin(user.Id, login));
|
||||
// UnitTestHelper.IsSuccess(await manager.AddPassword(user.Id, "password"));
|
||||
// var logins = await manager.GetLogins(user.Id);
|
||||
// Assert.NotNull(logins);
|
||||
// Assert.Equal(1, logins.Count());
|
||||
// Assert.Equal(user, await manager.Find(login));
|
||||
// Assert.Equal(user, await manager.Find(user.UserName, "password"));
|
||||
//}
|
||||
[Fact]
|
||||
public async Task CreateUserLoginAndAddPasswordTest()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var login = new UserLoginInfo("Provider", "key");
|
||||
var user = new InMemoryUser("CreateUserLoginAddPasswordTest");
|
||||
UnitTestHelper.IsSuccess(await manager.Create(user));
|
||||
UnitTestHelper.IsSuccess(await manager.AddLogin(user.Id, login));
|
||||
UnitTestHelper.IsSuccess(await manager.AddPassword(user.Id, "password"));
|
||||
var logins = await manager.GetLogins(user.Id);
|
||||
Assert.NotNull(logins);
|
||||
Assert.Equal(1, logins.Count());
|
||||
Assert.Equal(user, await manager.Find(login));
|
||||
Assert.Equal(user, await manager.Find(user.UserName, "password"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateUserAddRemoveLoginTest()
|
||||
|
@ -94,36 +90,36 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
|
|||
Assert.NotEqual(stamp, user.SecurityStamp);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public async Task RemovePasswordTest()
|
||||
//{
|
||||
// var manager = CreateManager();
|
||||
// var user = new InMemoryUser("RemovePasswordTest");
|
||||
// const string password = "password";
|
||||
// UnitTestHelper.IsSuccess(await manager.Create(user, password));
|
||||
// var stamp = user.SecurityStamp;
|
||||
// UnitTestHelper.IsSuccess(await manager.RemovePassword(user.Id));
|
||||
// var u = await manager.FindByName(user.UserName);
|
||||
// Assert.NotNull(u);
|
||||
// Assert.Null(u.PasswordHash);
|
||||
// Assert.NotEqual(stamp, user.SecurityStamp);
|
||||
//}
|
||||
[Fact]
|
||||
public async Task RemovePasswordTest()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var user = new InMemoryUser("RemovePasswordTest");
|
||||
const string password = "password";
|
||||
UnitTestHelper.IsSuccess(await manager.Create(user, password));
|
||||
var stamp = user.SecurityStamp;
|
||||
UnitTestHelper.IsSuccess(await manager.RemovePassword(user.Id));
|
||||
var u = await manager.FindByName(user.UserName);
|
||||
Assert.NotNull(u);
|
||||
Assert.Null(u.PasswordHash);
|
||||
Assert.NotEqual(stamp, user.SecurityStamp);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public async Task ChangePasswordTest()
|
||||
//{
|
||||
// var manager = CreateManager();
|
||||
// var user = new InMemoryUser("ChangePasswordTest");
|
||||
// const string password = "password";
|
||||
// const string newPassword = "newpassword";
|
||||
// UnitTestHelper.IsSuccess(await manager.Create(user, password));
|
||||
// var stamp = user.SecurityStamp;
|
||||
// Assert.NotNull(stamp);
|
||||
// UnitTestHelper.IsSuccess(await manager.ChangePassword(user.Id, password, newPassword));
|
||||
// Assert.Null(await manager.Find(user.UserName, password));
|
||||
// Assert.Equal(user, await manager.Find(user.UserName, newPassword));
|
||||
// Assert.NotEqual(stamp, user.SecurityStamp);
|
||||
//}
|
||||
[Fact]
|
||||
public async Task ChangePasswordTest()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var user = new InMemoryUser("ChangePasswordTest");
|
||||
const string password = "password";
|
||||
const string newPassword = "newpassword";
|
||||
UnitTestHelper.IsSuccess(await manager.Create(user, password));
|
||||
var stamp = user.SecurityStamp;
|
||||
Assert.NotNull(stamp);
|
||||
UnitTestHelper.IsSuccess(await manager.ChangePassword(user.Id, password, newPassword));
|
||||
Assert.Null(await manager.Find(user.UserName, password));
|
||||
Assert.Equal(user, await manager.Find(user.UserName, newPassword));
|
||||
Assert.NotEqual(stamp, user.SecurityStamp);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddRemoveUserClaimTest()
|
||||
|
@ -149,26 +145,15 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
|
|||
Assert.Equal(0, userClaims.Count);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public async Task ChangePasswordFallsIfPasswordTooShortTest()
|
||||
//{
|
||||
// var manager = CreateManager();
|
||||
// var user = new InMemoryUser("user");
|
||||
// const string password = "password";
|
||||
// UnitTestHelper.IsSuccess(await manager.Create(user, password));
|
||||
// var result = await manager.ChangePassword(user.Id, password, "n");
|
||||
// UnitTestHelper.IsFailure(result, "Passwords must be at least 6 characters.");
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public async Task ChangePasswordFallsIfPasswordWrongTest()
|
||||
//{
|
||||
// var manager = CreateManager();
|
||||
// var user = new InMemoryUser("user");
|
||||
// UnitTestHelper.IsSuccess(await manager.Create(user, "password"));
|
||||
// var result = await manager.ChangePassword(user.Id, "bogus", "newpassword");
|
||||
// UnitTestHelper.IsFailure(result, "Incorrect password.");
|
||||
//}
|
||||
[Fact]
|
||||
public async Task ChangePasswordFallsIfPasswordWrongTest()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var user = new InMemoryUser("user");
|
||||
UnitTestHelper.IsSuccess(await manager.Create(user, "password"));
|
||||
var result = await manager.ChangePassword(user.Id, "bogus", "newpassword");
|
||||
UnitTestHelper.IsFailure(result, "Incorrect password.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddDupeUserFailsTest()
|
||||
|
@ -193,17 +178,17 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
|
|||
Assert.NotEqual(stamp, user.SecurityStamp);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public async Task AddDupeLoginFailsTest()
|
||||
//{
|
||||
// var manager = CreateManager();
|
||||
// var user = new InMemoryUser("DupeLogin");
|
||||
// var login = new UserLoginInfo("provder", "key");
|
||||
// UnitTestHelper.IsSuccess(await manager.Create(user));
|
||||
// UnitTestHelper.IsSuccess(await manager.AddLogin(user.Id, login));
|
||||
// var result = await manager.AddLogin(user.Id, login);
|
||||
// UnitTestHelper.IsFailure(result, "A user with that external login already exists.");
|
||||
//}
|
||||
[Fact]
|
||||
public async Task AddDupeLoginFailsTest()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var user = new InMemoryUser("DupeLogin");
|
||||
var login = new UserLoginInfo("provder", "key");
|
||||
UnitTestHelper.IsSuccess(await manager.Create(user));
|
||||
UnitTestHelper.IsSuccess(await manager.AddLogin(user.Id, login));
|
||||
var result = await manager.AddLogin(user.Id, login);
|
||||
UnitTestHelper.IsFailure(result, "A user with that external login already exists.");
|
||||
}
|
||||
|
||||
// Lockout tests
|
||||
|
||||
|
@ -533,7 +518,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
|
|||
new InMemoryUser("1"), new InMemoryUser("2"), new InMemoryUser("3"),
|
||||
new InMemoryUser("4")
|
||||
};
|
||||
foreach (InMemoryUser u in users)
|
||||
foreach (var u in users)
|
||||
{
|
||||
UnitTestHelper.IsSuccess(await manager.Create(u));
|
||||
UnitTestHelper.IsSuccess(await manager.AddToRole(u.Id, role.Name));
|
||||
|
|
Загрузка…
Ссылка в новой задаче