I'm trying to migrate to the new Universal Membership providers (from a home brew solution). I've migrated our old User table to the Users/Memberships table.
When I run Membership.ValidateUser(txtUsername.Text.Trim(), txtPassword.Text.Trim()), it always returns false, even though I know the username/password is correct.
Here is how I generated the password, hash, and salt:
var salt = Crypto.GenerateSalt();
var hashedPassword = this.GenerateHashWithSalt(password, salt);
This is the GenerateHashWithSalt method I'm using
private string GenerateHashWithSalt(string password, string salt)
{
string hashWithSalt = password + salt;
byte[] saltedHashBytes = Encoding.UTF8.GetBytes(hashWithSalt);
HashAlgorithm algo = HashAlgorithm.Create(Membership.HashAlgorithmType);
byte[] hash = algo.ComputeHash(saltedHashBytes);
return Convert.ToBase64String(hash);
}
I've also gone with:
var salt = Crypto.GenerateSalt();
var saltedPassword = password + salt;
var hashedPassword = Crypto.HashPassword(saltedPassword);
Neither of these seem to work. What am I missing?
Scott
Bah, answered it.
So step 1 of all of this was migrating users from our 2-way encrypted passwords to 1 way hash.
Every example I saw of doing that included hashing the password manually. Turns out Membership.CreateUser(username,password); hashes the password (and salts), so I was hashing a hashed password, which is why auth was failing.
I simply had to call Membership.CreateUser, passing in the username & plain text pass, and it worked. Weee!
Related
Before we begin I want to make it clear that I know how bad this question is. It's a terrible situation but I'm being constrained by some very strange specifications.
I know that not only should you not try to write your own wrappers for this kind of stuff, but Microsoft have made it clear this shouldn't even be used. So before you start writing replies saying "Why are you even doing this" please try to understand that I have had these conversations with my superiors already, but the push for new features and lack of time means that despite it being atrocious; nevertheless - here I am.
We have an ASP Net membership database, started some time well before my time at this company and now hosting some 40k users. We have a platform in .Net 3.5 which lets users log in.
My job currently is to write an API in .Net Core 2.1 part of which is to allow for user creation and updating but there in lies the problem - migrating from Membership to Identity is not an option so I've been told to create a wrapper for the stored procedures in the Membership database.
This has been mostly successful with the only issue being; the subject of this question. Creating a user via aspnet_Membership_CreateUser I need to submit the data in such a way that it can be successfully validated in our platform.
I had originally followed this post but found that it's been designed for PasswordFormat 1 - Hashed; I then found that our user base used PasswordFormat 2 - Encrypted and as such the users I was creating would not validate.
The code looked something like this
public bool CreateUser(string userName, string password, string email, string securityQuestion, string securityAnswer, bool isApproved)
{
bool success = false;
//Just so I can attempt to login afterwards
password = "Hello World!";
//Our password and password salt need to be base64 encoded before we can save them to the DB
string salt = Guid.NewGuid().ToString();
string encryptedSalt = salt.Base64Encode();
//Concatenate our salt and password
IEnumerable<byte> saltedpass = salt.GetBytes(Encoding.UTF8).Concat(password.GetBytes(Encoding.UTF8));
//Use SHA1 to hash more - equivilant to the HASHBYTES('SHA1' T-SQL
byte[] sha1HashedPass = PasswordHelper.HashBytes(_validationMethod, saltedpass.ToArray(), _validationKey);
string hashedPass = sha1HashedPass.ToBase64String();
int errorCode = MembershipCreateUser(_applicationName, userName, hashedPass, encryptedSalt, email, securityQuestion, securityAnswer, isApproved);
if (errorCode == 0)
{
success = true;
}
return success;
}
Worth noting that _validationKey is the machine key shared across the applications which use this database, which I'm passing into the SHA1 mechanism.
So, intentionally and woefully bad security practice aside;
Is there a way in C# to generate an encrypted (not hashed) passwords and salts in this way?
Thank you for the comments - thankfully we were able to support Hashed passwords in our platform; the issue was with my code and not ASP Membership.
As mentioned I was taking a post that was originally written in T-SQL and trying to build a C# implementation of it. My implementation of this code was incorrect and as such the passwords and salts I was generating were not able to be validated by ASP Net Membership, this was not obvious in my original post because I had obfuscated the method which was SHA1 hashing my data.
//Using hard coded just for example
string username = "joeborder";
string password = "Hello World!";
string salt = "TastySalt";
Encoding encoder = Encoding.Unicode; //Encoding was also incorrect
//Our password and password salt need to be base64 encoded before we can save them to the DB
string encryptedSalt = salt.Base64Encode();
//Concatenate our salt and password
IEnumerable<byte> saltedpass = salt.GetBytes(encoder).Concat(password.GetBytes(encoder));
//Use SHA1 to hash more - equivilant to the HASHBYTES('SHA1') T-SQL
var SHA1Hasher = new SHA1CryptoServiceProvider(); //Originally I was using HMACSHA1 which was producing a different output
byte[] sha1HashedPass = SHA1Hasher.ComputerHash(saltedpass.ToArray());
string hashedPass = sha1HashedPass.ToBase64String();
/*
EXEC aspnet_Membership_CreateUser
#ApplicationName = "MyApp",
#UserName = username,
#Password = hashedPass,
#PasswordSalt = encryptedSalt,
...Etc
*/
Then in our .Net 3.5 application the following code would work
string username = "joeborder";
string password = "Hello World!";
if (Membership.ValidateUser(username, password))
{
Console.WriteLine("You've gotta be kidding me thats a clutch");
}
I'm creating a mobile application that connects to a web service. It needs to login to the system using a password. The password is stored on the server as a MD5 hashed password that was hashed using this method:
Byte[] Initial = <Key goes here>
MD5CryptoServiceProvider Provider = new MD5CryptoServiceProvider();
List<Byte> Encoding = new List<Byte>(Initial);
Encoding.AddRange(ASCIIEncoding.ASCII.GetBytes(inputString));
return Convert.ToBase64String(Provider.ComputeHash(Encoding.ToArray()));
The key and code to hash the password on the web application cannot be used in my Xamarin PCL project because it's not possible to use the 'MD5CryptoServiceProvider' in Xamarin PCL.
I need to create an equivalent method in the xamarin application to hash the password, before it's compared to the web service version.
To do this I have chose PCLCrypto but I can't seem to find anywhere to include the same key that was used to originally encrypt the password.
This is the Xamarin code:
Byte[] Initial = <the same key as was used to originally cache it>
// step 1, calculate MD5 hash from input
var hasher = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Md5);
byte[] inputBytes = Encoding.UTF8.GetBytes(inputString);
byte[] hash = hasher.HashData(inputBytes);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
return sb.ToString();
Thanks in advance for your help.
I have a solution but i use plateform specific code.
In xamarin.forms part you implement an interface like for example:
public interface SpecificCodes
{
Boolean comparePasswords(String userPassword,String DBPassword);
}
In xamarin.android you create a class which implements interface
[assembly:Dependency(typeof(MyProject.Droid.Code.SpecificPartsAndroid))]
namespace MyProject.Droid.Code
{
...
public class SpecificPartsAndroid: SpecificCodes
{
public Boolean comparePasswords(String userPassword, String DBPassword)
{
byte[] dig = hash(new Java.Lang.String(userPassword).GetBytes("UTF-8"),"MD5");
byte[] res=Base64.Decode(DBPassword,Base64Flags.Default);
return String.Compare(dig.ToString(),res.ToString())==0?true:false;
}
private static byte[] hash (byte[] toHash, String algorithm)
{
MessageDigest md = MessageDigest.GetInstance(algorithm);
return md.Digest(toHash);
}
}
}
In xamarin.ios do the same
And then to call your function use the code below in Xamarin.forms
DependencyService.Get<SpecificCodes>().comparePasswords(pwdUser, pwdHashed);
Hope it helps
Just using a hash function is not sufficient and just adding a salt does little to improve the security. Instead iIterate over an HMAC with a random salt for about a 100ms duration and save the salt with the hash. Use functions such as PBKDF2, password_hash, Bcrypt and similar functions. The point is to make the attacker spend a lot of time finding passwords by brute force.
Protecting your users is important, please use secure password methods.
See How to securely hash passwords, The Theory on Security Stackexchange.
See OWASP (Open Web Application Security Project) Password Storage Cheat Sheet.
See Modern, Secure, Salted Password Hashing Made Simple
See Toward Better Password Requirements by Jim Fenton:
I have a Registrations table which new users get put into. A later process creates a database for that user, and inserts an ASP.NET Identity User record from the data in the registration table (email & name).
I'd like to extend this so that at the point of registration, users can enter their password, which will then be set up in the new database.
To do this properly, I'd need to create a SecurityStamp value and then encrypt the password using that to get the PasswordHash, I believe. So then I would store these values in the registrations table, and then I could copy them into the user's new database when that is set up, and they would be able to log in with the password they registered.
How would I do this - generate the SecurityStamp and then hash the password?
SecurityStamp can be anything random, non-repeatable - Guid.NewGuid().ToString() does the job nicely.
For password hashing UserManager has property PasswordHasher that does password hashing for you:
var userManager = new UserManager(context);
var passwordHash = userManager.PasswordHasher.HashPassword("mySecurePassword");
Use this namespace
using Microsoft.AspNetCore.Identity;
to generate a hash use
var pass = new PasswordHasher<object>().HashPassword(null, "your password");
to test your password hash
var passCheck = new PasswordHasher<object>().VerifyHashedPassword(null, hashedpassword, "your test password");
return ((int)passCheck) == 1 ? "correct password" : "invalid password";
I am developing a website by using ASP.NET. I want to implement login authentication for my users. I am using SALT HASH method to securely save users' passwords to the DB. By looking at various codes I wrote a code like below to generate the SALT and the Hashed passwords to store in Database.
private string hashedPassword;
private string Salt;
private string SaltPlusPassword;
private const byte saltSize = 24;
byte[] saltArray;
byte[] hashedPasswordArray;
byte[] bytes;
public void Save_Login(string passWord)
{
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
saltArray = new byte[saltSize];
rng.GetBytes(saltArray);
}
Salt = Convert.ToBase64String(saltArray);
SaltPlusPassword = String.Format("{0}{1}", Salt, passWord);
using (SHA256 sha = SHA256.Create())
{
bytes = Encoding.UTF8.GetBytes(SaltPlusPassword);
hashedPasswordArray = sha.ComputeHash(bytes);
}
hashedPassword = Convert.ToBase64String(hashedPasswordArray);
}
//Salt will be save to DB
//hashedPassword will be save to DB.
So I have few questions.
1) I read in an article that saying "make your salt is at least as long as the hash function's output" ok. What are the sizes for saltArray, hashedPasswordArray and bytes arrays which are declared in my code? I used saltArray size as 24. Is it ok?
2) What will happen if I use ?
bytes = Encoding.Unicode.GetBytes(SaltPlusPassword);
instead of
bytes = Encoding.UTF8.GetBytes(SaltPlusPassword);
3) What is the datatype should I use to store salt and the hashed password in the DB? ( My db is MYSQL )
4) Is there any performance difference if I use SHA256Managed instead of SHA256? Which is best?
5) Finally am I doing this in the right way? What are the weaknesses in above code? What are your suggestions?
Rather than deal with all these issues, why not use the built in identity management tools provided by ASP.NET. See here
http://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity
Much more common and robust.
I have a working Form Integration but need to move to v 3.00 of Form Integration which requires AES coding rather than Xor. The sample code is a J2EE app which hides away the method of encrypting. SagePay support have been unable to provide an API class/method for encrypting/decrypting; I suspect they are there, if anyone has used them and can advise on this?
Alternatively I am trying to do the encryption in my own code. Again SagePay have not been forthcoming, in that they don't provide enough information about how the encryption is done. I believe I have worked out that the password they provide is used both for the encryption key and iv, though they don't state this in the integration guide. Here is code I have tried for encoding inString
byte[] byteDataToEncrypt = inString.getBytes();
final byte[] keyBytes = webSite.encryptionPassword().getBytes("ASCII");
final byte[] ivBytes = webSite.encryptionPassword().getBytes("ASCII");
final SecretKey key = new SecretKeySpec(keyBytes, "AES");
final IvParameterSpec iv = new IvParameterSpec(ivBytes);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] byteCipherText = cipher.doFinal(byteDataToEncrypt);
byte[] aesEncryptedBase64encoded = Base64.encodeBase64(byteCipherText);
String crypt = "#" + new String(aesEncryptedBase64encoded);
I do not know why # would need to be on the front, but I have seen it in other code that has been posted online for SagePay integration. Without it SagePay test server tells me the encryption method is not recognised. With it, it tells me the Currency field is missing, though I know the currency field is set ok to "GBP" in the source and I am using the provided test server password.
Any help very much appreciated whether from SagePay or from the developer community.
John
This works even if moving to ver4.00. The only thing that needs to be different for ver 4.00 is that it needs the encrypted string to be in upper case. Hence instead of
String crypt = "#" + Hex.encodeHexString(byteCipherText);
it will need to be
String crypt = "#" + Hex.encodeHexString(byteCipherText).toUpperCase();
Latest Sagepay/Opayo documentation at least verbally lists what all is needed and even if it is still not very comprehensive, together with this code, I could work it out.
Thanks.
The solution that worked for me is to replace this:
byte[] aesEncryptedBase64encoded = Base64.encodeBase64(byteCipherText);
String crypt = "#" + new String(aesEncryptedBase64encoded);
with this
String crypt = "#" + Hex.encodeHexString(byteCipherText);
or if using the SagePay API directly, replace all the code with just these two lines:
byte[] aesEncrypted = CryptographyHelper.AESEncrypt(detail, "ISO-8859-1", password);
String crypt = "#" + Hex.encodeHexString(aesEncrypted);