Port PasswordsHasher, crypto only in Net45

This commit is contained in:
Hao Kung 2014-03-06 11:54:33 -08:00
Родитель edb709ce9d
Коммит 5128a030da
5 изменённых файлов: 201 добавлений и 83 удалений

Просмотреть файл

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