I have a running C# app with user authentication. I encrypt the passwords with the SHA1Managed class using Encoding.Unicode. Now, we are developing a new PHP web app and it seems that PHP sha1 method uses ASCII, so the encrypted passwords are slightly different.
I cannot change my C# code as it is running in a number of different servers and all user passwords are already encrypted. This cannot be changed as it would imply asking all users their passwords (which is not viable).
I have read the following solutions, however I am not able to solve my problem with them:
- C# SHA-1 vs. PHP SHA-1...Different Results? In this one they suggest using ASCIIEncoding. However, as I stated before, this cannot be changed.
- SHA1: different results for PHP and C# and Generate a PHP UTF-16 SHA1 hash to match C# method these one use base64 encoding, and that is not compatible with my C# encoding (I think)
- php equivalent to following sha1 encryption c# code this one also uses ASCIIEncoding.
This is my C# code which CANNOT be changed:
byte[] msgBytes = Encoding.Unicode.GetBytes(data);
SHA1Managed sha1 = new SHA1Managed();
byte[] hashBytes = sha1.ComputeHash(msgBytes);
StringBuilder hashRet = new StringBuilder("");
foreach (byte b in hashBytes)
hashRet.AppendFormat("{0:X}", b);
return hashRet.ToString();
This is the PHP code implemented so far:
$str=mb_convert_encoding($password.$SALT, 'UTF-16LE');
$result=strtoupper(sha1($str));
The result obtained for an example string "1981" in C# D4CEDCADB729F37C30EBF41BC8F2929AF526AD3
In PHP I get: D4CEDCADB729F37C30EBF41BC8F29209AF526AD3
As can be seen, the only difference between each one is the 0 (zero) char. This happens in all strings, but the 0 appears in different locations.
EDIT
As https://stackoverflow.com/users/1839439/dharman stated, results can't be reproduced exactly as my string has a SALT. However, the results should be different even without the SALT.
SOLUTION A user gave a suggestion which solved my problem. Unfortunately, he deleted the answer before I could post that it worked. The problem is in C# when I am parsing the bytes to string in the StringBuilder. The format is {0:X} and any leading 0 in the byte is ignored. For example a 01 would be parsed to 1, a 0D would be parsed to D.
Therefore, I had to iterate over pairs of the result obtained in PHP, and remove leading zeros in there.
The final code in PHP is the following:
$str=mb_convert_encoding($password.$SALT, 'UTF-16LE');
$result=strtoupper(sha1($str));
$array = str_split($result, 2);
foreach ($array as &$valor) {
$valor = ltrim($valor, '0');
}
unset($valor);
$result = implode($array);