I'm struggling badly trying to decrypt some values in C# that are encrypted in PHP. The encryption in PHP is done using the following:
function encrypt($pure_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = 'fÔdñá1f¦';
$encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
$encrypted_string = base64_encode($encrypted_string);
return $encrypted_string;
}
Since ECB mode is used IV probably it's not used, but still that doesn't help. The biggest issue is that PHP documentation is so poor and it doesn't specify what encoding the functions are using! The string passed around have different byte values depending on the encoding and in the end encryption (Blowfish in this case) deals with bytes.
Without knowing the encoding, I'm just trying different encodings in my C# code, but without success. Somewhere I read that PHP is using internally "iso-8859-1" encoding, but even with that it's not working.
Has anyone been successful in decrypting in C# some value that was encrypted in PHP using the stupid function mcrypt_encrypt()?
Update
I did an example in PHP. Code:
define("ENCRYPTION_KEY", "1234asdf");
define("IV", "1#^ÊÁñÔ0");
$clearText = "abc";
function encrypt($pure_string, $encryption_key, $iv) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
$encrypted_string = base64_encode($encrypted_string);
return $encrypted_string;
}
$encrypted_string = encrypt($clearText, ENCRYPTION_KEY, IV);
echo "Key:" . ENCRYPTION_KEY . "<br />";
echo "IV:" . IV . "<br />";
echo "Clear Text:" . $clearText . "<br />";
echo "Encrypted Text:" . $encrypted_string . "<br />";
and the result is:
Key:1234asdf
IV:1#^ÊÁñÔ0
Clear Text:abc
Encrypted Text:OiZ6QIdhXYk=
Also I confirmed that IV is not used, any value I pass the result is the same.
Well, the problem in you case is not the blowfish decryption part in c#, it is the encryption part in php. And no, this is not about using mcrypt_encrypt, it is the mistake to call utf8_encode on an already utf8 encoded string...
The decryption function i've created uses BouncyCastle. There are two encrypted strings, the first has been created with the php function you've posted, for the second one i've removed the utf8_encode call inside mbcrypt_encrypt.
The first sample uses the (bad) php_utf8_encoded string, we need to convert the decrypted byte array back and forth to get the correct result.
Debug the second call of the c# decryption function and have a look at the result of the first str1 produced by Encoding.UTF8.GetBytes. Its correct, without the back-and-forth conversion of the charset.
public static string BlowfishDecrypt(string encrypted, string key)
{
var cipher = new BufferedBlockCipher(new BlowfishEngine());
var k = new KeyParameter(Encoding.UTF8.GetBytes(key));
cipher.Init(false, k);
var input = Convert.FromBase64String(encrypted);
var length = cipher.GetOutputSize(input.Length);
var block = new byte[length];
var len = cipher.ProcessBytes(input, 0, input.Length, block, 0);
var output = cipher.DoFinal(block, len);
// dont know how we get the real length of the content here... but this will do it. But I am sure there is a better way...
var idx = Array.IndexOf(block, (byte)0);
var str1 = Encoding.UTF8.GetString(block, 0, idx);
var raw1 = Encoding.GetEncoding("iso-8859-1").GetBytes(str1);
var str2 = Encoding.UTF8.GetString(raw1);
return str2;
}
static string original = "#€~>|";
static string encrypted_with_utf8_encode = "7+XyF+QGcA8lz5AQlLf1FA==";
static string encrypted_without = "3oWsAOEF+Kc=";
static string key = "t0ps3cr3t";
public static void Main()
{
var decrypted1 = BlowfishDecrypt(encrypted_with_utf8_encode, key);
var decrypted2 = BlowfishDecrypt(encrypted_without, key);
var same = original.Equals(decrypted1);
Debugger.Break();
}
In the end was able to do it. A few pointers:
Some Blowfish libraries in C# seems to have bad implementation. The one that worked correctly was https://github.com/b1thunt3r/blowfish-csharp
Never use methods that deal with strings directly. That's stupid in the first place to offer in any library (even the one above has overloads that work with string and it "supposes" that strings are in Unicode!)
When dealing with different platforms, try to convince the other party to use base64 encoding.
In the end, I am amazed that why so many developers (even the ones developing crypto libraries) don't get it that working with strings without specifying an encoding is stupid and does not make any sense!
Related
I have to replicate C# hash from the code below into PHP. I have been searching but didn't find a solution so far.
From this article on creating an md5 hash string:
using System;
using System.Text;
using System.Security.Cryptography;
// Create an md5 sum string of this string
static public string GetMd5Sum(string str)
{
// First we need to convert the string into bytes, which
// means using a text encoder.
Encoder enc = System.Text.Encoding.Unicode.GetEncoder();
// Create a buffer large enough to hold the string
byte[] unicodeText = new byte[str.Length * 2];
enc.GetBytes(str.ToCharArray(), 0, str.Length, unicodeText, 0, true);
// Now that we have a byte array we can ask the CSP to hash it
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(unicodeText);
// Build the final string by converting each byte
// into hex and appending it to a StringBuilder
StringBuilder sb = new StringBuilder();
for (int i=0;i<result.Length;i++)
{
sb.Append(result[i].ToString("X2"));
}
// And return it
return sb.ToString();
}
For input = "123", the above code gives me "5FA285E1BEBE0A6623E33AFC04A1FBD5"
I have tried the following PHP code but it does not give the same output.
From the SO question PHP MD5 not matching C# MD5:
$str = "123";
$strUtf32 = mb_convert_encoding($str, "UTF-32LE");
echo md5($strUtf32);
This code has the result = "a0d5c8a4d386f15284ec25fe1eeeb426". By the way, changing UTF-32LE to utf-8 or utf-16 still does not give me the same result.
Can anyone help?
Yep, as CodesInChaos suggests, you got the encodings wrong.
On php side try this:
$str = "123";
$strUtf32 = mb_convert_encoding($str, "UTF-16LE");
echo md5($strUtf32);
This will give you 5FA285E1BEBE0A6623E33AFC04A1FBD5. This will match System.Text.Encoding.Unicode on the c# side.
Otherwise change System.Text.Encoding.Unicode to System.Text.Encoding.UTF32 on the c# side. This will give you A0D5C8A4D386F15284EC25FE1EEEB426.
Uhh, the C# code creates a MD5 hash and the PHP mb_convert_encoding function just encodes the string...
Plus, this is NOT THE FULL CODE from the link you gave. You are missing the important MD5 function:
$str = "123";
$strUtf32 = mb_convert_encoding($str, "UTF-16");
echo md5($strUtf32); <=====
If that code matches there should be NO REASON why that shouldn't work, as the MD5 algorithm is still the same and does not vary from language to language.
I have something like that written in PHP:
$signature = md5($tosigndata);
Now, I am trying to replicate this in C#:
MD5.Create().ComputeHash(Tools.GetBytes(tosigndata))
But that gives me totally different result. How do I need to change my C# code in order to match PHP hash?
PS. Yeah, I know that .ComputeHash() returns byte[], but that doesn't change anything, i tried decoding it and it's still a different string.
Edit: Tools.GetBytes() returns Encoding.UTF8.GetBytes(tosigndata);
try this in C#:
byte[] asciiBytes = ASCIIEncoding.ASCII.GetBytes(tosigndata); // tosigndata is your string variable
byte[] hashedBytes = MD5CryptoServiceProvider.Create().ComputeHash(asciiBytes);
string hashedString = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
// hashString contains your hash data similar to php md5
I have looked and found code to take a PHP sha512 hash and match it inside C#. I am currently looking for a way to go from a hash that was made in C# and get the same result in PHP. We are slowly moving away from asp.net to PHP and need a way to check our passwords in the database. Here is the C# code used to make the hash.
// Create a hash from a pwd and salt using sha512
public static string CreatePasswordHash(string _password, string _salt)
{
string saltAndPwd = String.Concat(_password, _salt);
SHA512 sha512 = new System.Security.Cryptography.SHA512Managed();
byte[] sha512Bytes = System.Text.Encoding.Default.GetBytes(saltAndPwd);
byte[] cryString = sha512.ComputeHash(sha512Bytes);
string hashedPwd = string.Empty;
for (int i = 0; i < cryString.Length; i++)
{
hashedPwd += cryString[i].ToString("X");
}
return hashedPwd;
}
In PHP I have tried to get it to match but it is off by just a few bytes it seems.
function CreatePasswordHash($_password, $_salt)
{
$saltAndPwd = $_password . $_salt;
$hashedPwd = hash('sha512', $saltAndPwd);
return strtoupper($hashedPwd);
}
When using the above with the same salt and password here is the results I get.
The first result is from C#, and the second result is from PHP:
60BB73FDA3FF7A444870C6D0DBC7C6966F8D5AD632B0A02762E0283051D7C54A5F4B01571D1A5BC8C689DBC411FEB92158383A56AFC6AE6074696AF36E16
60BB73FDA3FF7A444870C6D0DBC7C609066F8D5AD632B0A02762E0283051D7C54A5F4B001571D1A5BC8C689DBC411FEB092158383A56AFC6AE6074696AF36E16
Any ideas on why these are not matching up? Does it have to do with endian byte order?
try
hashedPwd += cryString[i].ToString("X2");
Editing PHP:
function CreatePasswordHash($_password, $_salt)
{
$saltAndPwd = $_password . $_salt;
$hashedPwd = hash('sha512', $saltAndPwd);
$hex_strs = str_split($hashedPwd,2);
foreach($hex_strs as &$hex) {
$hex = preg_replace('/^0/', '', $hex);
}
$hashedPwd = implode('', $hex_strs);
return strtoupper($hashedPwd);
}
The C# print out is not including leading zeros.
Replace
hashedPwd += cryString[i].ToString("X");
with
hashedPwd += cryString[i].ToString("X2");
Double check that you use the same character encoding in C# and PHP. GetBytes returns different results, depending on the encoding. System.Text.Encoding.Default depends on the localization of the OS.
You can try open_ssl_digest
echo openssl_digest($saltAndPwd, 'sha512');
if you have PHP >= 5.3
You could also use the hash_algos function to see which algorithms are supported in your system.
HTH
I try to hash a string using SHA256, I'm using the following code:
using System;
using System.Security.Cryptography;
using System.Text;
public class Hash
{
public static string getHashSha256(string text)
{
byte[] bytes = Encoding.Unicode.GetBytes(text);
SHA256Managed hashstring = new SHA256Managed();
byte[] hash = hashstring.ComputeHash(bytes);
string hashString = string.Empty;
foreach (byte x in hash)
{
hashString += String.Format("{0:x2}", x);
}
return hashString;
}
}
However, this code gives significantly different results compared to my friends php, as well as online generators (such as This generator)
Does anyone know what the error is? Different bases?
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".)
I also had this problem with another style of implementation but I forgot where I got it since it was 2 years ago.
static string sha256(string randomString)
{
var crypt = new SHA256Managed();
string hash = String.Empty;
byte[] crypto = crypt.ComputeHash(Encoding.ASCII.GetBytes(randomString));
foreach (byte theByte in crypto)
{
hash += theByte.ToString("x2");
}
return hash;
}
When I input something like abcdefghi2013 for some reason it gives different results and results in errors in my login module.
Then I tried modifying the code the same way as suggested by Quuxplusone and changed the encoding from ASCII to UTF8 then it finally worked!
static string sha256(string randomString)
{
var crypt = new System.Security.Cryptography.SHA256Managed();
var hash = new System.Text.StringBuilder();
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
foreach (byte theByte in crypto)
{
hash.Append(theByte.ToString("x2"));
}
return hash.ToString();
}
Thanks again Quuxplusone for the wonderful and detailed answer! :)
public static string ComputeSHA256Hash(string text)
{
using (var sha256 = new SHA256Managed())
{
return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
}
}
The reason why you get different results is because you don't use the same string encoding. The link you put for the on-line web site that computes SHA256 uses UTF8 Encoding, while in your example you used Unicode Encoding. They are two different encodings, so you don't get the same result. With the example above you get the same SHA256 hash of the linked web site. You need to use the same encoding also in PHP.
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/
public string EncryptPassword(string password, string saltorusername)
{
using (var sha256 = SHA256.Create())
{
var saltedPassword = string.Format("{0}{1}", salt, password);
byte[] saltedPasswordAsBytes = Encoding.UTF8.GetBytes(saltedPassword);
return Convert.ToBase64String(sha256.ComputeHash(saltedPasswordAsBytes));
}
}
New .NET 5+ solution:
If you're using .NET 5 or above, you can use the new Convert.ToHexString method to convert the hash byte array into a hexadecimal string; eliminating the hassle of using string builders and so on.
The following method also uses the using block so that the SHA256 instance gets disposed.
It also turns the password (which is passed in as a string) into a byte array using UTF-8 encoding, which was recommended by the accepted answer. Furthermore, we're also using the new SHA256 class as opposed to the old (now obselete) SHA256Managed class.
public string QuickHash(string secret)
{
using var sha256 = SHA256.Create();
var secretBytes = Encoding.UTF8.GetBytes(secret);
var secretHash = sha256.ComputeHash(secretBytes);
return Convert.ToHexString(secretHash);
}
Note: You should NOT use this method for hashing user passwords. General-purpose hashing functions such as SHA-256 aren't suited for use for passwords anymore, even if you add salts. This is useful for hashing strings that you know have high entropy, such as long randomly generated session tokens and whatnot. For storing passwords, you must look into slower hashing functions that were specifically designed for this purpose, such as Bcrypt, Scrypt, or PBKDF2 (the latter is available natively in .NET — see this)
The shortest and fastest way ever. Only 1 line!
public static string StringSha256Hash(string text) =>
string.IsNullOrEmpty(text) ? string.Empty : BitConverter.ToString(new System.Security.Cryptography.SHA256Managed().ComputeHash(System.Text.Encoding.UTF8.GetBytes(text))).Replace("-", string.Empty);
In the PHP version you can send 'true' in the last parameter, but the default is 'false'. The following algorithm is equivalent to the default PHP's hash function when passing 'sha256' as the first parameter:
public static string GetSha256FromString(string strData)
{
var message = Encoding.ASCII.GetBytes(strData);
SHA256Managed hashString = new SHA256Managed();
string hex = "";
var hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
I was looking and testing theses answers, and Visual Studio showed me that SHA256Managed is now Obsolete (here)
So, I used the SHA256 class instead:
Encoding enc = Encoding.UTF8;
var hashBuilder = new StringBuilder();
using var hash = SHA256.Create();
byte[] result = hash.ComputeHash(enc.GetBytes(yourStringToHash));
foreach (var b in result)
hashBuilder.Append(b.ToString("x2"));
string result = hashBuilder.ToString();
This work for me in .NET Core 3.1.
But not in .NET 5 preview 7.
using System;
using System.Security.Cryptography;
using System.Text;
namespace PortalAplicaciones.Shared.Models
{
public class Encriptar
{
public static string EncriptaPassWord(string Password)
{
try
{
SHA256Managed hasher = new SHA256Managed();
byte[] pwdBytes = new UTF8Encoding().GetBytes(Password);
byte[] keyBytes = hasher.ComputeHash(pwdBytes);
hasher.Dispose();
return Convert.ToBase64String(keyBytes);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
}
}
i have a hashing algorithm in C#, in a nutshell, it is:
string input = "asd";
System.Security.Cryptography.MD5 alg = System.Security.Cryptography.MD5.Create();
System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
byte[] hash = alg.ComputeHash(enc.GetBytes(input));
string output = Convert.ToBase64String(hash);
// outputs: eBVpbsvxyW5olLd5RW0zDg==
Console.WriteLine(output);
Now I need to replicate this behaviour in php,
$input = "asd";
$output = HashSomething($input);
echo $output;
How can I achieve it?
I checked
md5
utf8_decode
utf8_encode
base64_encode
base64_decode
url_decode
but i noted the php md5 doesn't get the == on the end... what am I missing?
NOTE: I cannot change C# behaviour because it's already implemented and passwords saved in my db with this algorithm.
The issue is PHP's md5() function by default returns the hex variation of the hash where C# is returning the raw byte output that must then be made text safe with base64 encoding. If you are running PHP5 you can use base64_encode(md5('asd', true)). Notice the second parameter to md5() is true which makes md5() return the raw bytes instead of the hex.
Did you remember to base64 encode the md5 hash in php?
$result = base64_encode(md5($password, true));
The second parameter makes md5 return raw output, which is the same as the functions you're using in C#
Your C# code takes the UTF8 bytes from the string; calculates md5 and stores as base64 encoded. So you should do the same in php, which should be:
$hashValue = base64_encode(md5(utf8_decode($inputString)))
it should be like as below for php
php -r "echo base64_encode(md5(utf8_encode('asd'),true));"
I had the same issue...using just md5($myvar) it worked. I am getting the same result C# and PHP.
Gavin Kendall posted helped me. I hope this helps others.
http://jachman.wordpress.com/2006/06/06/md5-hash-keys-with-c/
public static string MD5Hash(string text)
{
System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
return System.Text.RegularExpressions.Regex.Replace(BitConverter.ToString(md5.ComputeHash(ASCIIEncoding.Default.GetBytes(text))), “-”, “”);
}