Manually hashing password the same as ASP.NET Identity v2.2.1 - asp.net-identity-2

I have an ASP.NET Web Api that makes use of ASP.NET Identity v2.2.1 to manage users. I am able to add/edit users without issue. However, I have a second project that cannot make use of the API but needs to be able to change a Users password directly via the database.
I am trying to figure out how to hash the password entered by the user without going through the API. I need to make sure that I am using the same hashing algorithm that ASP.NET Identity is using. I came across some code in this SO article but I am not sure if it is the same hashing algorithm used by v2.2.1.
using using System.Security.Cryptography;
public static string HashPassword(string password)
{
private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes
private const int PBKDF2SubkeyLength = 256 / 8; // 256 bits
private const int SaltSize = 128 / 8; // 128 bits
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);
}
I would like to avoid having to add ASP.NET Identity as a dependency to this project hence why I would like to hash the password manually.

I would recommend you to use SimpleCrypto
This is how I've used that in a project I believe this will help you. One can add this DLL from nuget
[HttpPost]
public ActionResult Register(RegisterViewModel model)
{
try
{
if (ModelState.IsValid)
{
{
var crypto = new SimpleCrypto.PBKDF2();
var encrypPass = crypto.Compute(model.Password);
var newUser = db.Users.Create();
newUser.Email = model.Email;
newUser.Password = encrypPass;
newUser.PasswordSalt = crypto.Salt;
// newUser.Name = model.UserName;
newUser.Username = model.UserName;
//newUser.AddedBy = model.;
db.Users.Add(newUser);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "");
}
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
eve.Entry.Entity.GetType().Name, eve.Entry.State);
foreach (var ve in eve.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
return View();
}
Your valid check at login will be like this
private bool IsValid(string email, string password)
{
var crypto = new SimpleCrypto.PBKDF2();
bool isValid = false;
{
var user = db.Users.FirstOrDefault(u => u.Email == email);
if (user != null)
{
if (user.Password == crypto.Compute(password, user.PasswordSalt))
{
isValid = true;
}
}
}
return isValid;
}

Related

Create user inside a transaction

When creating a user like so:
using var trans = Db.BeginTransaction();
AuthRepository.CreateUserAuth(newUser, request.Password);
AuthRepository.AssignRoles(created, new List<string> { request.role });
//.. do other stuff
throw new Exception("other code may throw this");
trans.Commit();
The Auth repo has it's own connection so it's not part of the transaction. This means, if my code bails out, I end up with an unwanted user.
Is there any way to use a transaction with AuthRepository or is only way to manually write to user and role tables? I couldn't find an example of creating the password hash in docs when manually saving, is there any example?
You can't use the existing Auth Repository APIs within a transaction because each uses its own DB connection.
If you want you can take the implementation of those OrmLite Auth Respository APIs in OrmLiteAuthRepository.cs and move them into your method so they're all using the same DB Connection + Transaction.
Also note that to use Transactions in OrmLite you should use OpenTransaction() instead, e.g:
using (var dbTrans = db.OpenTransaction())
{
}
Adding class I used here in case it helps anyone as needed a little refactoring:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using OutReachPete.ServiceModel.User;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.OrmLite;
namespace OutReachPete.ServiceInterface.User
{
public class UserHelper
{
public static bool ForceCaseInsensitiveUserNameSearch { get; set; } = true;
public static bool UseDistinctRoleTables { get; set; } = true;
public static UserAuthCustom CreateUserAuth(UserAuthCustom newUser, string password, IDbConnection db)
{
newUser.ValidateNewUser(password);
AssertNoExistingUser(db, newUser);
newUser.PopulatePasswordHashes(password);
newUser.CreatedDate = DateTime.UtcNow;
newUser.ModifiedDate = newUser.CreatedDate;
db.Save(newUser);
newUser = db.SingleById<UserAuthCustom>(newUser.Id);
return newUser;
}
public static void AssertNoExistingUser(IDbConnection db, IUserAuth newUser,
IUserAuth exceptForExistingUser = null)
{
if (newUser.UserName != null)
{
var existingUser = GetUserAuthByUserName(db, newUser.UserName);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format(ErrorMessages.UserAlreadyExistsTemplate1,
newUser.UserName.SafeInput()));
}
if (newUser.Email != null)
{
var existingUser = GetUserAuthByUserName(db, newUser.Email);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format(ErrorMessages.EmailAlreadyExistsTemplate1,
newUser.Email.SafeInput()));
}
}
public static UserAuthCustom GetUserAuthByUserName(IDbConnection db, string userNameOrEmail)
{
var isEmail = userNameOrEmail.Contains("#");
var lowerUserName = userNameOrEmail.ToLower();
UserAuthCustom userAuth = null;
// Usernames/Emails are saved in Lower Case so we can do an exact search using lowerUserName
if (HostContext.GetPlugin<AuthFeature>()?.SaveUserNamesInLowerCase == true)
{
return isEmail
? db.Select<UserAuthCustom>(q => q.Email == lowerUserName).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName == lowerUserName).FirstOrDefault();
}
// Try an exact search using index first
userAuth = isEmail
? db.Select<UserAuthCustom>(q => q.Email == userNameOrEmail).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName == userNameOrEmail).FirstOrDefault();
if (userAuth != null)
return userAuth;
// Fallback to a non-index search if no exact match is found
if (ForceCaseInsensitiveUserNameSearch)
{
userAuth = isEmail
? db.Select<UserAuthCustom>(q => q.Email.ToLower() == lowerUserName).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName.ToLower() == lowerUserName).FirstOrDefault();
}
return userAuth;
}
public static void AssignRoles(IAuthRepository userAuthRepo, IUserAuth userAuth, IDbConnection db,
ICollection<string> roles = null, ICollection<string> permissions = null)
{
if (userAuthRepo is IManageRoles managesRoles)
{
AssignRoles(userAuth.Id.ToString(), db, roles, permissions);
}
else
{
AssignRolesInternal(userAuth, roles, permissions);
SaveUserAuth(userAuth, db);
}
}
public static IUserAuth GetUserAuth(string userAuthId, IDbConnection db)
{
if (string.IsNullOrEmpty(userAuthId))
throw new ArgumentNullException(nameof(userAuthId));
return db.SingleById<UserAuthCustom>(int.Parse(userAuthId));
}
public static void AssignRoles(string userAuthId, IDbConnection db, ICollection<string> roles = null, ICollection<string> permissions = null)
{
var userAuth = GetUserAuth(userAuthId, db);
if (!UseDistinctRoleTables)
{
if (!roles.IsEmpty())
{
foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
{
if (userAuth.Roles == null)
userAuth.Roles = new List<string>();
userAuth.Roles.Add(missingRole);
}
}
if (!permissions.IsEmpty())
{
foreach (var missingPermission in permissions.Where(x => userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
{
if (userAuth.Permissions == null)
userAuth.Permissions = new List<string>();
userAuth.Permissions.Add(missingPermission);
}
}
SaveUserAuth(userAuth, db);
}
else
{
var now = DateTime.UtcNow;
var userRoles = db.Select<UserAuthRole>(q => q.UserAuthId == userAuth.Id);
if (!roles.IsEmpty())
{
var roleSet = userRoles.Where(x => x.Role != null).Select(x => x.Role).ToHashSet();
foreach (var role in roles)
{
if (!roleSet.Contains(role))
{
db.Insert(new UserAuthRole
{
UserAuthId = userAuth.Id,
Role = role,
CreatedDate = now,
ModifiedDate = now,
});
}
}
}
if (!permissions.IsEmpty())
{
var permissionSet = userRoles.Where(x => x.Permission != null).Select(x => x.Permission).ToHashSet();
foreach (var permission in permissions)
{
if (!permissionSet.Contains(permission))
{
db.Insert(new UserAuthRole
{
UserAuthId = userAuth.Id,
Permission = permission,
CreatedDate = now,
ModifiedDate = now,
});
}
}
}
}
}
private static void AssignRolesInternal(IUserAuth userAuth, ICollection<string> roles, ICollection<string> permissions)
{
if (!roles.IsEmpty())
{
foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
{
if (userAuth.Roles == null)
userAuth.Roles = new List<string>();
userAuth.Roles.Add(missingRole);
}
}
if (!permissions.IsEmpty())
{
foreach (var missingPermission in permissions.Where(x =>
userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
{
if (userAuth.Permissions == null)
userAuth.Permissions = new List<string>();
userAuth.Permissions.Add(missingPermission);
}
}
}
public static void SaveUserAuth(IUserAuth userAuth, IDbConnection db)
{
if (userAuth == null)
throw new ArgumentNullException(nameof(userAuth));
userAuth.ModifiedDate = DateTime.UtcNow;
if (userAuth.CreatedDate == default(DateTime))
userAuth.CreatedDate = userAuth.ModifiedDate;
db.Save((UserAuthCustom)userAuth);
}
}
}
Then just call it like:
var created = UserHelper.CreateUserAuth(newUser, request.Password, Db);
UserHelper.AssignRoles(AuthRepository, created, Db, new List<string> { request.CreateUserType.ToString() });
It will only use the connection passed. Just change UserAuthCustom to whatever you user class is.

How to retrieve Claims Value in .Net Core 2.0

As it says in the title I have already assigned claims to the registered user, I am now trying to retrieve the claim value when the user logs into the application within the UserClaims table in sql server which I find a bit difficult to do as this is my first time using claims.
Looking for directions on our to achieve this, thank you in advance.
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.UserName, Email = model.Email, UserRoleId = model.RoleId };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created a new account with password.");
await addUserClaims(model.CusomterId, model.UserName);
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
List<UserRole> roles = _userRoleRepo.GetAll();
model.CreateRoleList(roles);
List<Customer> customers = await _customerRepository.GetAll();
model.SetupCustomerOptionList(customers);
// If we got this far, something failed, redisplay form
return View(model);
}
private async Task addUserClaims(string CustomerID ,string username)
{
// Customer customer = _customerRepository.GetById(customerid);
List<Customer> customers = await _customerRepository.GetAll();
Customer customer = _customerRepository.GetById(CustomerID);
var user = await _userManager.FindByNameAsync(username);
;
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Name, CustomerID));
}
Set
var claims = new List<Claim>
{
new Claim("Currency", "PKR")
};
Get
#User.Claims.FirstOrDefault(c => c.Type == "Currency").Value
Very easy
public static class IdentityExtension
{
public static string GetId(this IIdentity identity)
{
ClaimsIdentity claimsIdentity = identity as ClaimsIdentity;
Claim claim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
return claim.Value;
}
}
Example
User.Identity.GetId();

How to find object type from URL SharePoint online (o365)?

I found out how to determine the object type from URL for SharePoint on prem:
https://blogs.msdn.microsoft.com/sanjaynarang/2009/04/06/find-sharepoint-object-type-from-url/
But I didn't find anything for SharePoint Online (CSOM).
Is it possible for SharePoint online?
For the most scenarios such as:
folder url, e.g. https://contoso.sharepoint.com//Documents/Forms/AllItems.aspx?RootFolder=%2FDocuments%2FArchive
list item url, e.g. https://contoso.sharepoint.com/Lists/ShoppingCart/DispForm.aspx?ID=9
list/library url, e.g. https://contoso.sharepoint.com/Lists/Announcements
page url, e.g. https://contoso.sharepoint.com/Lists/Announcements/Newsletter.aspx
the following example demonstrates how to determine client object type:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Web;
using Microsoft.SharePoint.Client;
namespace O365Console
{
static class ClientObjectExtensions
{
public static ClientObject ResolveClientObjectFromUrl(string resourceUrl, ICredentials credentials)
{
ClientObject targetObject = null;
var resourceUri = new Uri(resourceUrl);
using (var rootCtx = new ClientContext(resourceUri.Scheme + Uri.SchemeDelimiter + resourceUri.Host))
{
rootCtx.Credentials = credentials;
var webUrl = Web.WebUrlFromPageUrlDirect(rootCtx, resourceUri);
using (var ctx = new ClientContext(webUrl.ToString()))
{
ctx.Credentials = credentials;
var queryBag = System.Web.HttpUtility.ParseQueryString(resourceUri.Query);
if (queryBag["Id"] != null)
{
var listUrl = string.Join(string.Empty,
resourceUri.Segments.Take(resourceUri.Segments.Length - 1));
var list = ctx.Web.GetList(listUrl);
targetObject = TryRetrieve(() => list.GetItemById(Convert.ToInt32(queryBag["Id"])));
}
else if (queryBag["RootFolder"] != null)
{
var folderUrl = HttpUtility.UrlDecode(queryBag["RootFolder"]);
targetObject = TryRetrieve(() => ctx.Web.GetFolderByServerRelativeUrl(folderUrl));
}
else if (queryBag.Count > 0)
{
throw new Exception("Unsupported query string parameter found");
}
else
{
targetObject = TryRetrieve(() => ctx.Web.GetFileByServerRelativeUrl(resourceUri.AbsolutePath));
if (targetObject == null)
{
targetObject = TryRetrieve(() => ctx.Web.GetList(resourceUri.AbsolutePath),list => list.RootFolder);
if (targetObject == null || ((List)targetObject).RootFolder.ServerRelativeUrl != resourceUri.AbsolutePath)
targetObject = TryRetrieve(() => ctx.Web.GetFolderByServerRelativeUrl(resourceUri.AbsolutePath));
}
}
}
}
return targetObject;
}
private static T TryRetrieve<T>(Func<T> loadMethod, params Expression<Func<T,object>>[] retrievals) where T : ClientObject
{
try
{
var targetObject = loadMethod();
targetObject.Context.Load(targetObject, retrievals);
targetObject.Context.ExecuteQuery();
return targetObject;
}
catch
{
}
return default(T);
}
}
}
Usage
var credentials = GetCredentials(userName, password);
var clientObj = ClientObjectExtensions.ResolveClientObjectFromUrl("https://contoso.sharepoint.com/Lists/Announcements", credentials);
Console.WriteLine(clientObj.GetType().Name);
where
static ICredentials GetCredentials(string userName,string password)
{
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
return new SharePointOnlineCredentials(userName, securePassword);
}

PCLCrypto exception 'CryptographicException: Bad PKCS7 padding. Invalid length'

I'm struggling with the PCLCryptho libraby, I can't get it working without retrieving the exception 'CryptographicException: Bad PKCS7 padding. Invalid length'. Running the code once is working, but running it multiple times after each other fails (with different input strings). The decryption takes place after a new instance of the program. I'm running this code on iOS with Xamarin Forms. Here's my code (I'm using the same VI each time and save the salt in the Settinsg for now):
public static string EncryptAnswer(string answer, string passWord)
{
try
{
var keyMaterial = CreateKey(passWord);
var cipherTextBuffer = GetBytes(answer);
var symmetricAlgorithm = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var symmetricKey = symmetricAlgorithm.CreateSymmetricKey(keyMaterial);
using (var encryptor = WinRTCrypto.CryptographicEngine.CreateEncryptor(symmetricKey, GetBytes("vivivivivivivivi")))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (var bWriter = new BinaryWriter(cs))
{
bWriter.Write(cipherTextBuffer, 0, cipherTextBuffer.Length);
cs.FlushFinalBlock();
}
}
return GetString(ms.ToArray());
}
}
}
catch (Exception e)
{
return string.Empty;
}
}
public static string DecryptAnswer(string encryptedAnswer, string passWord)
{
try
{
var cipherTextBuffer = GetBytes(encryptedAnswer);
var keyMaterial = CreateKey(passWord);
var symmetricAlgorithm = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var symmetricKey = symmetricAlgorithm.CreateSymmetricKey(keyMaterial);
using (var decryptor = WinRTCrypto.CryptographicEngine.CreateDecryptor(symmetricKey, GetBytes("vivivivivivivivi")))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
using (var binWriter = new BinaryWriter(cs))
{
binWriter.Write(cipherTextBuffer, 0, cipherTextBuffer.Length);
}
return GetString(ms.ToArray());
}
}
}
catch (Exception e)
{
}
return string.Empty;
}
public static byte[] CreateSalt()
{
var salt = WinRTCrypto.CryptographicBuffer.GenerateRandom(8);
CrossSettings.Current.AddOrUpdateValue("Salt", GetString(salt));
return salt;
}
private static byte[] GetSalt()
{
var saltString = CrossSettings.Current.GetValueOrDefault<string>("Salt");
var salt = GetBytes(saltString);
return salt;
}
private static byte[] CreateKey(string passWord)
{
var key = NetFxCrypto.DeriveBytes.GetBytes(passWord, GetSalt(), 1000, 32);
return key;
}
private static byte[] GetBytes(string str)
{
return Encoding.Unicode.GetBytes(str);
}
private static string GetString(byte[] bytes)
{
return Encoding.Unicode.GetString(bytes, 0, bytes.Length);
}
This seems to be equal to the answers and examples I found. Can someone tell me what's wrong?

LastPwdSEt is Not Updating by Updating a password

I like to calculate the passowrd expiration date for the user in active directory
I am able to get the MaxPWdAge and LastPwdSet attributes.
But the problem is whenever i am changing the password of one of a user for testing purposes, lastPwdSet is not updating and it is still showing the old date?
Can anybody tell me why is this happen ?
public bool CheckPassWordExpiryDate(string LdapPath, string Username, string Password)
{
DomainConfiguration domainConfig = new DomainConfiguration();
// Configuration(web.config) changes
DirectoryEntry de = new DirectoryEntry("LDAP://" + LdapPath, domainConfig.UserName, domainConfig.Password);
DirectoryEntry entry = new DirectoryEntry();
entry.Username = Username;
entry.Password = Password;
//Function to get maximum password age from the active directory
int maxPwdAge = GetMaxPasswordAge();
// Function to get last password set date for the use.
DateTime pwdLastSet = GetPwdLastSet("pwdLastSet", Username);
//Add maximum password age days to Last password set days , if it is less than today's date means that password has been expired else it is not expired
if (pwdLastSet.AddDays(maxPwdAge) < DateTime.Now)
{
return true;
}
else
{
return false;
}
}
public static int GetMaxPasswordAge()
{
DomainConfiguration domainConfig = new DomainConfiguration();
using (new SPMonitoredScope("AD Properties"))
{
using (DirectoryEntry domain = new DirectoryEntry("LDAP://" + domainConfig.DomainName, domainConfig.UserName, domainConfig.Password))
{
DirectorySearcher ds = new DirectorySearcher(
domain,
"(objectClass=*)",
null,
SearchScope.Base
);
SearchResult sr = ds.FindOne();
TimeSpan maxPwdAge = TimeSpan.MinValue;
if (sr.Properties.Contains("maxPwdAge"))
maxPwdAge = TimeSpan.FromTicks((long)sr.Properties["maxPwdAge"][0]);
return maxPwdAge.Duration().Days;
}
}
}
public DateTime GetPwdLastSet(string attr, string UserName)
{
DomainConfiguration domainConfig = new DomainConfiguration();
using (new SPMonitoredScope("AD Properties"))
{
using (DirectoryEntry domain = new DirectoryEntry("LDAP://" + domainConfig.DomainName, domainConfig.UserName, domainConfig.Password))
{
//DirectorySearcher searcher = new DirectorySearcher(domain, "(|(objectClass=organizationalUnit)(objectClass=container)(objectClass=builtinDomain)(objectClass=domainDNS))");
DirectorySearcher searcher = new DirectorySearcher(domain);
searcher.PageSize = 1000;
searcher.Filter = "(SAMAccountName='" + UserName + "')";
searcher.Filter = "(|(objectCategory=group)(objectCategory=person))";
var user = searcher.FindOne();
DateTime pwdLastSet = DateTime.FromFileTime((Int64)user.Properties["PwdLastSet"][0]);
return pwdLastSet;
}
}
}
} }

Resources