I'm not coming up with the same values (using a known password).
I suspect it may be something having to do with encodings, but all the things I've tried haven't worked thus far:
windows code (c#?):
private static string EncodePassword(string password, string salt)
{
string encodedPassword = password;
HMACSHA1 hash = new HMACSHA1 { Key = Convert.FromBase64String(salt) };
encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
return encodedPassword;
}
perl code run on linux:
use Modern::Perl '2015';
use Digest::SHA qw(hmac_sha1 hmac_sha1_base64);
use MIME::Base64 qw(decode_base64 encode_base64);
use Unicode::String qw(utf16be utf16le);
say encode_base64(hmac_sha1($password, decode_base64($salt)));
# (or, equivalently)
say hmac_sha1_base64($password, decode_base64($salt));
my $le16 = utf16le($password);
my $be16 = utf16be($password);
say "ok, try utf-16 (le, then be)...";
say encode_base64(hmac_sha1($le16, decode_base64($salt)));
say encode_base64(hmac_sha1($be16, decode_base64($salt)));
# try reversing the hmac output?
my $hmac_bytes = hmac_sha1($password, decode_base64($salt));
my $rev_bytes = reverse $hmac_bytes;
say encode_base64($rev_bytes);
In the original C# code, in this line:
encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
a call to Encoding.Unicode.GetBytes transforms the password to a byte array via a UTF-16LE encoder.
You have to do the same transformation to get the same hash in Perl:
use Digest::SHA qw(hmac_sha1);
use MIME::Base64 qw(decode_base64 encode_base64);
use Encode qw(encode);
$utf16LEPassword = encode("UTF-16LE", $password);
print encode_base64(hmac_sha1($utf16LEPassword, decode_base64($salt)));
Related
Below is the code of both C# and PHP, I need some help regarding it. I am trying to generate the authenticationKey which is in C# but want the convert in PHP. All is done but I don't know how to implement [System.Text] in PHP as there is hash_hmac() in PHP but what might be the $string in the same function.
C# Version
var hmac = new System.Security.Cryptography.HMACSHA256();
var buffer = userName + accessKey + timeStamp + originUrl;
var hash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(buffer));
var authenticationKey = Convert.ToBase64String(hash);
PHP version
$hmac = hash_hmac('sha256', $string, $buffer);
$encoded = base64_encode($hmac);
Can anyone help me with that. It will be very helpful. Thanks.
I was also searching for this and atlast it was just a mistake of 1 small varibale, in the hash_hmac function pass the last value as true, this is will return raw output and when you convert it to base64 it will give the same output as the c# code.
$buffer = $userName.$accessKey.$timeStamp.$originUrl;
$hmac = hash_hmac('sha256', $buffer, $tokenSecret, true);
$authenticationKey = base64_encode($hmac);
Just in your c# function use the key, as shown in the code below
var hmac = new System.Security.Cryptography.HMACSHA256();
hmac.Key = System.Text.Encoding.UTF8.GetBytes(tokenSecret);
In my code I have tokenSecret variable in bas64 form.
You may get the hex value, using unpack method:
$value = unpack('H*', buffer);
echo $value[1];
In your c# code, you used a randomly generated key. Check the documentation:
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.hmacsha256?view=netframework-4.8#constructors
HMACSHA256()
Initializes a new instance of the HMACSHA256 class with a randomly generated key.
HMACSHA256(Byte[])
Initializes a new instance of the HMACSHA256 class with the specified key data
Now the php documentation in comparison: https://www.php.net/manual/en/function.hash-hmac.php
hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = FALSE ] ) : string
You see, what you call buffer in php, is actually the key and what you call buffer in c# is the data you hash.
So the$string should contain the data to be hashed.
As per the conversion, you don't need to get bytes in php: What is the PHP equivalent of this C# encoding code?
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'm working on rewriting a piece of PHP code to C#. This code is used for password hashing. In the first step it produces a string like "password{salt}", than hashes it via sha512 hash algorithm. After that a loop is hashing the combination of the first hash and the salt again for 5000 iterations.
The PHP Code looks like this:
<?php
$password = 'abc';
$salt = 'def';
$salted = $password.'{'.$salt.'}';
$digest = hash('sha512', $salted, true);
for ($i=1; $i<5000; $i++) {
$digest = hash('sha512', $digest.$salted, true);
}
$encodedPassword = base64_encode($digest);
//$encodedPassword contains the final hash code
I was able to get it working without the loop (with just the first hash() call). So the main hashing and base64 encoding is done correctly. I found out that this part is what I cannot manage to rewrite in C#:
$digest.$salted
$digest seems to be a binary representation since PHP's hash() function was used with "true" as the last parameter (see PHP hash - manual). $salted is a string. Both get somehow magically combined by PHP's dot / concat operator. I guess there will be some sort of standard conversion from binary to string under the hood when using the dot operator with a non-string operand.
This is my code so far:
void Main()
{
string password = "abc";
string salt = "def";
string salted = String.Format("{0}{{{1}}}", password, salt);
byte[] digest = hash(salted);
for(int i = 1; i < 1; i++)
{
digest = hash(String.Format("{0}{1}", System.Text.Encoding.UTF8.GetString(digest), salted));
}
var encodedPassword = System.Convert.ToBase64String(digest);
//$encodedPassword should contain the final hash code
}
static byte[] hash(string toHash)
{
System.Security.Cryptography.SHA512 sha512 = new System.Security.Cryptography.SHA512Managed();
return sha512.ComputeHash(System.Text.Encoding.UTF8.GetBytes(toHash));
}
As you see I tried to convert the hash bytes back to a string with System.Text.Encoding.UTF8.GetString() and then append the salt but that doesn't produce the same output as the PHP code.
I would be very happy if someone could help me on this. Thank you very much.
In the PHP version you loop 4999 times, while in the C# version 0. The second problem is that the returned bytes from hash() have no encoding at all.
This should give you the same result as the PHP version:
System.Security.Cryptography.SHA512 sha512 = new System.Security.Cryptography.SHA512Managed();
var saltedUtf8Bytes = System.Text.Encoding.UTF8.GetBytes(salted);
for(int i = 1; i < 5000; i++)
{
digest = sha512.ComputeHash(digest.Concat(saltedUtf8Bytes).ToArray());
}
Below are 2 similar code blocks. They take a string, encrypt in SHA512, then convert to Base64, I had trouble getting the second code block to produce the same results as my manual test using online calculators and encoders. So I broke the process down step by step and discovered that it was capable of producing the same results as my manual test but only if it behaved like the first code block. Why do these two code blocks produce different results? Thanks!
private void EditText_AfterTextChanged(object sender, AfterTextChangedEventArgs e)
{
//This builds a string to encrypt.
string domain = txtDomain.Text;
string username = txtUsername.Text;
string pin = txtPin.Text;
txtPreview.Text = string.Format("{0}+{1}+{2}", domain, username, pin);
//This takes the above string, encrypts it.
StringBuilder Sb = new StringBuilder();
SHA512Managed HashTool = new SHA512Managed();
Byte[] PhraseAsByte = System.Text.Encoding.UTF8.GetBytes(string.Concat(txtPreview.Text));
Byte[] EncryptedBytes = HashTool.ComputeHash(PhraseAsByte);
HashTool.Clear();
//This rebuilds the calculated hash for manual comparison.
foreach (Byte b in EncryptedBytes)
Sb.Append(b.ToString("x2"));
txtHash.Text = Sb.ToString();
//This takes the rebuilt hash and re-converts it to bytes before encoding it in Base64
EncryptedBytes = System.Text.Encoding.UTF8.GetBytes(string.Concat(txtHash.Text));
txtResult.Text = Convert.ToBase64String(EncryptedBytes);
}
and
private void EditText_AfterTextChanged(object sender, AfterTextChangedEventArgs e)
{
//This builds a string to encrypt.
string domain = txtDomain.Text;
string username = txtUsername.Text;
string pin = txtPin.Text;
txtPreview.Text = string.Format("{0}+{1}+{2}", domain, username, pin);
//This takes the above string, encrypts it.
StringBuilder Sb = new StringBuilder();
SHA512Managed HashTool = new SHA512Managed();
Byte[] PhraseAsByte = System.Text.Encoding.UTF8.GetBytes(string.Concat(txtPreview.Text));
Byte[] EncryptedBytes = HashTool.ComputeHash(PhraseAsByte);
HashTool.Clear();
//This takes the EncryptedBytes and converts them to base64.
txtResult.Text = Convert.ToBase64String(EncryptedBytes);
//This reverses the EncryptedBytes into readable hash for manual comparison
foreach (Byte b in EncryptedBytes)
Sb.Append(b.ToString("x2"));
txtHash.Text = Sb.ToString();
}
Found the answer, no thanks to your less-than-useful downvotes..
Encoding.Unicode is Microsoft's misleading name for UTF-16 (a double-wide encoding, used in the Windows world for historical reasons but not used by anyone else). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx
If you inspect your bytes array, you'll see that every second byte is 0x00 (because of the double-wide encoding).
You should be using Encoding.UTF8.GetBytes instead.
But also, you will see different results depending on whether or not you consider the terminating '\0' byte to be part of the data you're hashing. Hashing the two bytes "Hi" will give a different result from hashing the three bytes "Hi". You'll have to decide which you want to do. (Presumably you want to do whichever one your friend's PHP code is doing.)
For ASCII text, Encoding.UTF8 will definitely be suitable. If you're aiming for perfect compatibility with your friend's code, even on non-ASCII inputs, you'd better try a few test cases with non-ASCII characters such as é and 家 and see whether your results still match up. If not, you'll have to figure out what encoding your friend is really using; it might be one of the 8-bit "code pages" that used to be popular before the invention of Unicode. (Again, I think Windows is the main reason that anyone still needs to worry about "code pages".)
Source: Hashing a string with Sha256
I'm currently migrating a CakePhp app to ASP.NET. One thing that is blocking me at this point is that I'm unable to get the right hashing method to get the right password fit so users are able to sign-in from the ASP.NET app.
I have the salt value that is set in config/core.php file.
I've googled to try to determined where to find which hashing algorithm is used, and was not able to find the right query or no result.
here is my C# method so far to hash the password.
public static string ToHash(this string password, string salt)
{
if (string.IsNullOrEmpty(password))
return "";
var provider = new SHA1CryptoServiceProvider();
var encoding = new UnicodeEncoding();
var bytes = provider.ComputeHash(encoding.GetBytes(salt + password));
return Encoding.UTF8.GetString(bytes);
}
I've tried to put the salt before or after the password, it's currently no matching at all, here is the hash password from the cakephp mysql database:
c7fb60ef77dbe3d1681a68e6741ee3a23cc1f41d
Here is what I have from my method
��3[v"���1�:�ѐ��
Not really sure where/how to solve this problem. Any help or hint would be appreciated.
Thanks
I have it!
At least it works for my configuration:
Find salt from Core.php (search for Security.salt in the file). Then, use this code (very similar to the question):
string input = textBox1.Text;
input = "your salt should be here" + input;
var provider = new SHA1CryptoServiceProvider();
var encoding = new UTF8Encoding();
var bytes = provider.ComputeHash(encoding.GetBytes(input));
sha1result.Text = Bin2Hex(bytes);
Helper for Bin2Hex is also here:
public static string Bin2Hex(byte[] ba)
{
string hex = BitConverter.ToString(ba);
return hex.Replace("-", "");
}
It wasn't easy finding it, I searched through some of internet (no results!) and finally resorted to source-digging.
Don't have the Cake sources with me right now, but you should easily be able to look up Cake's hashing and salting method in the source.
The above differences in data look like Cake transforms the hash bytes into a string with the hash's bytes in hex base. Whatever the difference in the hash method, you'll have to convert the C# hash's result into such a string as well before comparing them (or go the other way and parse Cake's hex string and build a byte array out of it).