2

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.

Ozair Kafray
  • 13,351
  • 8
  • 59
  • 84
Albert
  • 1,015
  • 2
  • 10
  • 28
  • Since `mcrypt` is deprecated, due to be removed in PHP 7.2, I'd probably scrap this and start over... – CD001 Nov 08 '17 at 15:43
  • Seems like this thread could help you out! https://stackoverflow.com/questions/18681526/php-mcrypt-encrypt-to-net – ZarX Nov 08 '17 at 15:52
  • Could you add an full example (encrypted base64 text, password and original plain text)? – Michael Nov 08 '17 at 15:54
  • 1
    Many people were succesful - I see variations of this question at least couple times per week (not specifically blowfish). – Evk Nov 08 '17 at 15:58
  • @CD001 I have no choice on what is used on the PHP side. I can only control the C# side – Albert Nov 09 '17 at 08:40
  • @ZarX that deals with doing encryption in C# similar to PHP. I found this one https://stackoverflow.com/questions/4329260/cross-platform-php-to-c-sharp-net-encryption-decryption-with-rijndael/ which may help (although it's taking for granted that PHP encoding in UTF8). All the different examples don't deal with the core of the issue, that is what encoding is PHP using? – Albert Nov 09 '17 at 08:46
  • @Michael I have some examples, but those are with the real key, so I can't share it. – Albert Nov 09 '17 at 08:47
  • @Michael Then run the php-function with key you've choosen to create a sample that you can share. Without sample data, theres nothing I (and probably the others too) can do for you... – Michael Nov 09 '17 at 15:18
  • @Michael Updated the question to include a test I did in PHP – Albert Nov 09 '17 at 15:48
  • It is best not to use PHP mcrypt, it is abandonware, has not been updated in years and does not support standard PKCS#7 (née PKCS#5) padding, only non-standard null padding that can't even be used with binary data. mcrypt has many outstanding [bugs](https://sourceforge.net/p/mcrypt/bugs/) dating back to 2003. The mcrypt-extension is deprecated will be removed in PHP 7.2. Instead consider using [defuse](https://github.com/defuse/php-encryption) or [RNCryptor](https://github.com/RNCryptor), they provide a complete solution and are being maintained and is correct. – zaph Nov 09 '17 at 17:24
  • @zaph As I said I don't have control over the PHP part – Albert Nov 09 '17 at 17:57

2 Answers2

0

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();
}
Michael
  • 1,931
  • 2
  • 8
  • 22
  • Actually the call to utf8_encode() doesn't change anything. I tested that in PHP and the result was the same with and without that call. And as I said I DO NOT have control over the php part. – Albert Nov 10 '17 at 09:04
  • 1
    @Albert Well, in my test environment, with the removal of `utf8_encode` i was able to decrypt the data without this encoding-dance (utf8 -> iso8859-1 -> utf8). This will only have an effect if the data to encrypt contains higher characters like the euro sign '€' in my example. And yes, I know that you DO NOT control the php part (you've said more than once ;)), I only wanted to show that its not your fault or a problem with the crypto library that decrypting the data is that hard since the encryption process could have been better... – Michael Nov 10 '17 at 09:23
  • It's not my fault, but I had to solve the issue. Agree that encryption could have been better, but it's also true that some of the libraries that I tried are bad and have problems (at least that they use wrongly strings in crypto libraries) – Albert Nov 10 '17 at 13:36
  • As a clarification (what is in my answer below): - Be careful of the library you choose. I did use https://github.com/b1thunt3r/blowfish-csharp - If the library works directly with strings it's probably a bad one. In any case use only overloads that work with bytes. - When dealing with different platforms, try to convince the other party to use base64 encoding. – Albert Nov 10 '17 at 13:38
-1

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!

Albert
  • 1,015
  • 2
  • 10
  • 28
  • 1. Do not use PHP mcrypt, it is deprecated and does not support standard padding methods. 2. Do not use Blowfish in new code, it is not particularly secure and note that the author does not use it and uses AES. 3. Many CPUs have instructions ([AES-NI](https://en.wikipedia.org/wiki/AES_instruction_set)) that directly support AES. – zaph Nov 09 '17 at 17:24
  • @zaph Explained it several times that I don't control the PHP part. I have to decrypt what I get in .NET – Albert Nov 09 '17 at 17:58
  • Solves the problem? Seems to be. Knowing the real reason whats wrong? Might be. But I don't know a good encryption/decryption library that accepts strings as parameters. Neither BouncyCastle or the Microsoft ones do. – Michael Nov 09 '17 at 18:25
  • 1
    I down voted because of the unsubstantiated statement: "Some Blowfish libraries in C# seems to have bad implementation." 1. "Some" is vague and unhelpful. 2. "seems to have bad implementation" is vague and the best guess is that you could not make them work in your situation. 3. There is no description of what the problem was that was resolved. 4. Other than that there are some good points in the answer. 5. The actual answer is a link to an implementation that worked for you. 6. The information against using mcrypt and Blowfish are directed toward future readers. – zaph Nov 09 '17 at 18:30
  • @LW001 First I should be accurate and then nice. In any case point taken – Albert Nov 10 '17 at 08:55
  • @Michael The fact that you don't know, does not mean that there aren't. Do a search on Google for blowfish encryption in C# and you'll see. And I gave you the reason: Some of them are dealing with strings without taking into account encoding. – Albert Nov 10 '17 at 08:57
  • @zaph Perhaps you could first read the question accurately and then give a better answer yourself – Albert Nov 10 '17 at 08:58
  • @Albert You're right, but an crypto library that takes strings as arguments is (imho) not a *good* crypto library. The microsoft crypto libraries should be suitable for ~70% of the normal use-cases and I am pretty sure most of the other use-cases could be solved using BouncyCastle. – Michael Nov 10 '17 at 09:31
  • @Michael That's a valid point that I agree with. Unfortunately if you're not familiar with crypto libraries out there and do a search, most of the ones you get are not good. – Albert Nov 10 '17 at 13:33