I need to validate (not decrypt) a password created in Symfony 2 in C#.
An existing application built in Symfony 2 is being rewritten in C#. The security token is stored in a local database.
The current Symfony password settings are
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
API\CoreEntityBundle\Entity\User:
algorithm: sha512
iterations: 512
encode_as_base64: true
How can I reliably hash the given password and compare against the stored token to determine if the password supplied is correct?
If your password is salted: Concatenate the password with the "{salt}" (such that "EncodedPassword{SaltUsed}" is the final string), store in salted. If it's not just store the password in salted.
Hash salted using sha512, store in digest
Repeat iterations - 1 times (511 in your case):
Concatenate digest and salted
Hash concatenation using sha512, store result in digest for next iteration
Base64 Encode the final digest.
You can also take a look at Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder for the implementation.
This is a working version.
string CalculateHash(string plain, string salt)
{
var salted = $"{plain}{{{salt}}}";
var saltedBytes = Encoding.UTF8.GetBytes(salted);
using (var sha512 = SHA512.Create())
{
var digest = sha512.ComputeHash(saltedBytes);
var outputBytes = new byte[digest.Length + saltedBytes.Length];
for (var iteration = 1; iteration < 512; iteration++)
{
Buffer.BlockCopy(digest, 0, outputBytes, 0, digest.Length);
Buffer.BlockCopy(saltedBytes, 0, outputBytes, digest.Length, saltedBytes.Length);
digest = sha512.ComputeHash(outputBytes);
}
var result = Convert.ToBase64String(digest);
return result;
}
Related
This should be so simple but I've spent 4 hours fiddling with this code and I just can't seem to get it to work.
The PHP code works as follows (I didn't write it and I can't change it, so I'm stuck with it):
$password = hash('sha512', "HelloWorld1");
$salt = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true);
$hashed = hash('sha512', $password.$salt);
$hashed and $salt are stored in the DB as is. That means $salt is already hashed for later on.
I have no idea why they decided to hash everything but what's done is done.
In this case, the result is
Pswd: ab3e648d69a71b33d0420fc3bfc9e2e8e3ef2a300385ea26bc22057a84cd9a5c359bd15c4a0a552122309e58938ce310839cd9d2ecad5f294266015d823331dd
Salt: fb5a0f741db0be2439dc14662aae3fc68eb5e16b446385d3ddd319b862d5e2d4f50488a39487b27fdd8ff7b7b76420fc3ebef2bce9e082ac15c9f2d6fe7d87fc
Now the login code on the C# side just needs to match a plain text hashed password along with the already hashed salt.
string password = "HelloWorld1";
string storedSalt = "fb5a0f741db0be2439dc14662aae3fc68eb5e16b446385d3ddd319b862d5e2d4f50488a39487b27fdd8ff7b7b76420fc3ebef2bce9e082ac15c9f2d6fe7d87fc";
using(SHA512 shaManaged = new SHA512Managed())
{
byte[] hashPassword = shaManaged.ComputeHash(Encoding.UTF8.GetBytes(password));
string hashPasswordString = BitConverter.ToString(hashPassword).Replace("-", "");
byte[] finalHash = shaManaged.ComputeHash(Encoding.UTF8.GetBytes(hashPasswordString + storedSalt));
Debug.WriteLine("Calculated Hash Password: " + BitConverter.ToString(finalHash).Replace("-", ""));
}
Essentially the idea is to
Hash the plain text password first (same as with the PHP code).
Convert the byte array to a string that matches the PHP format of hashing.
Then hash the hashed password and previously hashed salt together.
The result is as follows:
Stored Hash Password: AB3E648D69A71B33D0420FC3BFC9E2E8E3EF2A300385EA26BC22057A84CD9A5C359BD15C4A0A552122309E58938CE310839CD9D2ECAD5F294266015D823331DD
Calculated Hash Password: 189ABBA71AAEDDE5C8154558B68D59500A72E64D5F3F3C07EFA94F0126571FBB68C6ADD105E0C029BABF30CADD8A6A6B6E4749075854461A88EE1CE545E84507
Hopefully someone can spot where I'm going wrong :)
You have to tweak your code a little bit. Note the ToLowerInvariant(). C# returns upper case letters as string. As you see in your original code $salt and $password are returned with lower case letters, so your self calculated password hash hashPasswordString must also be lower case before concatenating with your storedSalt to gain the correct finalHash. Your shown expected result again uses upper case letters (maybe before stored it was converted in PHP?) so you don't need ToLowerCaseInvariant() on your final hash string.
Here is the code:
string password = "HelloWorld1";
string storedSalt = "fb5a0f741db0be2439dc14662aae3fc68eb5e16b446385d3ddd319b862d5e2d4f50488a39487b27fdd8ff7b7b76420fc3ebef2bce9e082ac15c9f2d6fe7d87fc";
using (SHA512 shaManaged = new SHA512Managed())
{
byte[] hashPassword = shaManaged.ComputeHash(Encoding.UTF8.GetBytes(password));
string hashPasswordString = BitConverter.ToString(hashPassword).Replace("-", "").ToLowerInvariant(); // Note the ToLowerInvariant();
byte[] finalHash = shaManaged.ComputeHash(Encoding.UTF8.GetBytes(hashPasswordString + storedSalt));
return BitConverter.ToString(finalHash).Replace("-", "");
}
I have an Oracle database. I would like to encrypt the incoming password in C# and compare that against the value that is stored in the built in oracle table called sys.user$ specifically the "spare4" column.
Is it possible to do this? I have tried to encrypt the password but it's not generating the same output as the value that is generated in the Oracle database table, so clearly i am missing logic that Oracle applies. Any ideas?
From what i understand the first 40 chars is the hashed password and the last 20 chars is the hashed Salt value.
In Oracle 11g onwards it uses the Salt (no idea how Oracle generates this) and applies to the password and then hashes it using SHA-1 to generate the S: part of the spare4 values according to the following blogs password hashes and Spare 4 tips
Here is the current code:
public static string GenerateSaltedSHA1(string plainTextString, int saltSize)
{
HashAlgorithm algorithm = new SHA1Managed();
var saltBytes = GenerateSalt(saltSize);
var plainTextBytes = Encoding.ASCII.GetBytes(plainTextString);
var plainTextWithSaltBytes = AppendByteArrays(plainTextBytes, saltBytes);
var saltedSHA1Bytes = algorithm.ComputeHash((byte[]) plainTextWithSaltBytes);
var saltedSHA1WithAppendedSaltBytes = AppendByteArrays(saltedSHA1Bytes, saltBytes);
return Convert.ToBase64String(saltedSHA1WithAppendedSaltBytes);
}
private static byte[] GenerateSalt(int saltSize)
{
var rng = new RNGCryptoServiceProvider();
var buff = new byte[saltSize];
rng.GetBytes(buff);
return buff;
}
private static byte[] AppendByteArrays(byte[] byteArray1, byte[] byteArray2)
{
var byteArrayResult =
new byte[byteArray1.Length + byteArray2.Length];
for (var i = 0; i < byteArray1.Length; i++)
byteArrayResult[i] = byteArray1[i];
for (var i = 0; i < byteArray2.Length; i++)
byteArrayResult[byteArray1.Length + i] = byteArray2[i];
return byteArrayResult;
}
I understand that in Oracle there are functions in DBMS_CRYPTO e.g hash that can be used to create a hash based on particular type e.g MD5, SHA-1 etc on the password etc.
If there are much better ways then i am open to suggestions.
In case my comments were not clear (or not believed), here is a little SQL statement that will do what you are looking for. This runs on 12c -- but won't swear it will work with all security options. If you have an S: part in sys.user$.spare4, it probably should work.
with trial as
(SELECT name,
-- The 1st 20 bytes (40 characters) of the "S" part are the hashed password
substr(substr(spare4,3,60),1,40) hashed_pwd,
-- The last 10 bytes (20 characters) of the "S" part are the plaintext salt
substr(substr(spare4,3,60),-20) salt,
-- I want to know if the password for user MYUSERNAME is "MYPASSWORDGUESS"...
'MYPASSWORDGUESS' trial_password
from sys.user$
where name = 'MYUSERNAME' ),
hashit as (
-- This SELECT adds the salt to our trial password and hashes it.
select name,
trial_password,
hashed_pwd,
salt,
-- The "3" is for the SH1 algorithm
sys.dbms_crypto.hash(
utl_raw.cast_to_raw(trial_password) || cast (salt as raw(10) ),3)
hashed_salted_trial
from trial )
-- If the resulting hash matches what Oracle has stored in spare4, it is good.
SELECT name,
trial_password,
case when hashed_pwd = hashed_salted_trial
then 'Y' ELSE 'N' END password_match
from hashit
;
Update "MYUSERNAME" and "MYPASSWORDGUESS" as needed for your database.
UPDATE
Based on my original code in C# is there an equivalent way of doing the following in C#? sys.dbms_crypto.hash(utl_raw.cast_to_raw(trial_password) || cast (salt as raw(10) ),3)
utl_raw.cast_to_raw(trial_password) is doing the same thing as Encoding.ASCII.GetBytes(plainTextString) in your post. The goal of each is to take your guess at the password and convert it into byte.
cast(salt as raw(10)) does not have an equivalent in your post. More on this later.
|| is the way you concatenate RAW values in Oracle. It serves the same purpose as AppendByteArrays in your post.
sys.dbms_crypto.hash(..., 3) is how you generate a 160-bit SHA-1 hash in Oracle. I guess that is what algorithm.ComputeHash does in your post.
So, the only thing you are missing in your code is an equivalent for cast(salt as raw(10)).
As we discussed, you cannot generate your own salt for this. You must use the salt that is encoded in user$.spare4. That would be the last 20 characters of the "S:" part of that field. It looks like this: FA338B110F78548CCB44. Every two characters form a hexadecimal BYTE that gives you one of the bytes of your salt. So, in my example, the 1st byte of the salt (given by "FA") is 15 (for "F") x16+10 (for "A") = 250.
That is hopefully enough logic for you to implement in C#. However, it is not really necessary that you do so, because the input value for the salt can only come from user$.spare4 in the Oracle database. Since you gave to go to Oracle anyway, it is just as simple to
SELECT cast(substr(substr(spare4,3,60),-20) as RAW) FROM user$
as it is to just
SELECT spare4 FROM user$
and the do the rest in C#. You might as well let Oracle do what it is good at.
This question already has answers here:
How can I unhash a hash using C#?
(4 answers)
Closed 8 years ago.
I have this asp.net project that I need to hash the password (preferably with salt) and save it in sql database then unhash it for comparing with the login password or sth like that....
the thing is I'm not sure what is the best way to do it in a most secure way and how can I code this in C#?
You do not unhash. That's the point of hashing: it cannot be reversed.
You look up the salt, then you hash the password that they entered together with the salt. If the hash is the same as the hash in the database, it's a valid login.
Maybe take a look here:
Salted password hashing
First of all you cannot recover the hashed data. Its one way process. But you can match hashed data. To do so check the code given below :
Do this inside your button click event
string salt = GetSalt(10); // 10 is the size of Salt
string hashedPass = HashPassword(salt, Password.Text);
This are the functions that will help your to hash the password
const string alphanumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
public static string GetSalt(int saltSize)
{
Random r = new Random();
StringBuilder strB = new StringBuilder("");
while ((saltSize--) > 0)
strB.Append(alphanumeric[(int)(r.NextDouble() * alphanumeric.Length)]);
return strB.ToString();
}
public static string HashPassword(string salt, string password)
{
string mergedPass = string.Concat(salt, password);
return EncryptUsingMD5(mergedPass);
}
public static string EncryptUsingMD5(string inputStr)
{
using (MD5 md5Hash = MD5.Create())
{
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(inputStr));
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
sBuilder.Append(data[i].ToString("x2"));
// Return the hexadecimal string.
return sBuilder.ToString();
}
}
Similarly, when you try to match the password to authenticate the user, perform the same method just fetch your hashed password from your database and compare them. If the entered hashed password matches the database hashed password, its an authorized user.
Updated :
When you hash the password of the user for the first time and then store into database in the same table store the salt for that user.
Next time when you try to compare the password, fetch that salt of the user from the database and hash it using to compare with the
hashed password in the database.
Hope that answers your Question.
I made an account on my web app (Symfony2 with FOSUserBundle) and registered with the password "lolwut" (without the quotes).
These are the settings in my security.yml config:
security:
encoders:
FOS\UserBundle\Model\UserInterface:
algorithm: sha512
iterations: 1
encode_as_base64: false
The resulting data:
Hashed password:
f57470574dbf29026821519b19539d1c2237e4315d881fa412da978af554740c6b284062a9a4af7d0295d18ebea8ef2e152cf674b283f792fe0b568a93f969cf
Salt:
kuc5bixg5u88s4k8ggss4osoksko0g8
Now, since the iterations are set on 1, I am assuming that encoding "lolwut" in SHA512 in C# will give me the same result, here's my logic:
string salt = "kuc5bixg5u88s4k8ggss4osoksko0g8";
string input = "lolwut";
string passAndSalt = String.Concat(input, salt);
System.String Hashed = System.BitConverter.ToString(((System.Security.Cryptography.SHA512)new System.Security.Cryptography.SHA512Managed()).ComputeHash(System.Text.Encoding.ASCII.GetBytes(passAndSalt))).Replace("-", "");
return passAndSalt + "<br>" + Hashed;
However, this returns the following value that doesn't match the FOSUserBundle hashed password at all:
82E8CA0408B23DB50EB654EDB50A7926AC73613184054DB82FB6D67CD4186B7A045D265AEDE6E3852CD85B981F15F6615C1C0C6FBF443B1672DF59DE23557BD9
I know I must be doing something wrong somewhere, but I can't for the life of me figure out what it is, and it's driving me nuts. Could anyone help me out here, please?
Symfony merges password and salt as password{salt}, so this code will return the same hash:
string salt = "kuc5bixg5u88s4k8ggss4osoksko0g8";
string input = "lolwut";
string passAndSalt = String.Format("{0}{{{1}}}", input, salt);
System.String Hashed = System.BitConverter.ToString(((System.Security.Cryptography.SHA512)new System.Security.Cryptography.SHA512Managed()).ComputeHash(System.Text.Encoding.ASCII.GetBytes(passAndSalt))).Replace("-", "");
// Hashed.ToLower()
I have created a web form in c# that accepts username and password and stores password in MSSQL 2005 db in 'image' format. The password is merged with salt, encoded in UTF8 and lastly it is applied with a SHA512 encryption. I want to be able to see the passwords in string format when I pull them up back from the database. How should my decrypt function be, if the following is how I encrypted the password? Is that possible? :
string loginID = "";//This will be stored in varchar format in MSSQL..(Unrelated to the question)
string password =""; //This is where I store password inputted by user.
Random r = new Random();
int salt = r.Next((int)Math.Pow(2, 16));
int verifyCode = r.Next((int)Math.Pow(2, 16));
string tmpPwd = password.ToLower() + salt.ToString();
UTF8Encoding textConverter = new UTF8Encoding();
byte[] passBytes = textConverter.GetBytes(tmpPwd);
byte[] hashedPWD = new SHA512Managed().ComputeHash(passBytes);
The value in hashedPWD is stored in MSSQL as image datatype and salt is stored as int.
You can't - that's what a hash function is, by definition - a one-way function. Up until the last line, you can get the password back, but after the hash function, all you can do is generate a second hash and compare the two to see if they've produced the same result, in which case you can presume that the source strings were the same.