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");
}
Related
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 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 created a wesite in asp.net and use ms-sql database to save the records. Now want to convert it in node.js application. And want to use same sql database. In asp.net application I have encrypt the password for registered user. Below is code.
public static string CreateHash(string unHashed)
{
System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] data = System.Text.Encoding.ASCII.GetBytes(unHashed);
data = x.ComputeHash(data);
return System.Text.Encoding.ASCII.GetString(data);
}
public static bool MatchHash(string HashData, string HashUser)
{
HashUser = CreateHash(HashUser);
if (HashUser == HashData)
return true;
else
return false;
}
Now problem is that how I use same encryption in node.js. So when node application is ready old user can also make login. It only possible if node app also use same encryption that I have use in asp.net.
For node I have created all environment and use mssql module for database communication. Please help me fix that. Thanks!!
First of all MD5 shall no longer be used if you are serious about security.
Based on your comment and code, I fear there is a 'data loss' in the initial ASP.net code.
Let us have a look at CreateHash function again, I've added comments:
public static string CreateHash(string unHashed)
{
System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
// Convert unHashed string to bytes using ASCII coding
byte[] data = System.Text.Encoding.ASCII.GetBytes(unHashed);
// Compute MD5 hash from bytes
data = x.ComputeHash(data);
// Decode MD5 resulting bytes as ASCII
return System.Text.Encoding.ASCII.GetString(data);
}
The last line confuses me, it is decoding bytes received from MD5 function as if they were ASCII, but that is incorrect assumption. And the resulting encoded string as you gave in comment contains lots of "?'s".
Next node.js code will do similar except encode the string using hex rather than ascii:
var crypto = require('crypto')
function createHash(data) {
return crypto.createHash('md5').update(data, 'ascii').digest('hex')
}
To emulate "bytes to ascii" you could try .digest('binary') instead of hex. If it does not give what you expect, then you have to make a separate 'conversion' step from hex to ascii. (I am not experienced enough to give you elegant solution to the later one)
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!
Is this how hashed password stored in SQL Server should look like?
alt text http://img28.imageshack.us/img28/2545/88871034.gif
This is function I use to hash password (I found it in some tutorial)
public string EncryptPassword(string password)
{
//we use codepage 1252 because that is what sql server uses
byte[] pwdBytes = Encoding.GetEncoding(1252).GetBytes(password);
byte[] hashBytes = System.Security.Cryptography.MD5.Create().ComputeHash(pwdBytes);
return Encoding.GetEncoding(1252).GetString(hashBytes);
}
EDIT
I tried to use sha-1 and now strings seem to look like as they are suppose to:
public string EncryptPassword(string password)
{
return FormsAuthentication.HashPasswordForStoringInConfigFile(password, "sha1");
}
// example output: 39A43BDB7827112409EFED3473F804E9E01DB4A8
Result from the image above looks like broken string, but this sha-1 looks normal....
Will this be secure enough?
Your're close, but not quite there.
For a secure hash, you will need a salt value in another column. Second, try to stay away from MD5 as a hashing provider. It's not as secure as SHA-1 or SHA-2. SHA-1 is included in .NET just like MD5 is.