Converting PHP triple des decrypt to c#

I have the following code written for a PHP page. I send it a text key and text encryption string and I get the correct answer. But when I attempt it in c# function I get an error stating "Specified key is not a valid size for this algorithm." I am banging my head against the wall on this. Any suggestions?

PHP code currently using that works:
function decrypt($str, $key)
{   
    $keysize = mcrypt_get_key_size(MCRYPT_3DES, MCRYPT_MODE_CFB);
    $enc = base64_decode($str);
    $k = hash(SHA256, $key, true);
    $iv_size = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_CBC);
    $empty_iv = str_repeat(chr(0), $iv_size);
    $iv = mcrypt_encrypt(MCRYPT_3DES, substr($k, 0, $keysize), "", MCRYPT_MODE_CBC, $empty_iv);
    $dec = mcrypt_decrypt(MCRYPT_3DES, substr($k, 0, $keysize), $enc, MCRYPT_MODE_CFB, $iv);

    return $dec;

}

Open in new window


c# code that throws the error:
public static string Decrypt(string input, string key)
        {
            byte[] inputArray = Convert.FromBase64String(input);
            TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
            tripleDES.Key = UTF8Encoding.UTF8.GetBytes(key);
            tripleDES.Mode = CipherMode.CFB;
            tripleDES.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = tripleDES.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
            tripleDES.Clear();
            return UTF8Encoding.UTF8.GetString(resultArray);
        }

Open in new window

LVL 1
Don VonderBurgPresident/CEOAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Ray PaseurCommented:
This is not really an answer, just an observation.  MCrypt has been efffectively dead for years.
http://php.net/manual/en/intro.mcrypt.php

Today an appropriate choice is OpenSSL.  Here is a PHP code snippet that shows both MCrypt and OpenSSL, in parallel concrete implementations of an encryption interface.
https://iconoun.com/demo/encrypt_decrypt_both.php
<?php // demo/encrypt_decrypt_both.php
/**
 * Show how to encrypt and decrypt information
 * with binary-safe (base64) transport over the internet
 *
 * http://php.net/manual/en/book.openssl.php
 *
 * https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
 * https://en.wikipedia.org/wiki/Base64
 *
 * https://bugs.php.net/bug.php?id=67304
 *
 * Parallel construction in the encrypt() decrypt() methods
 */
error_reporting(E_ALL);


/**
 * The Interface defines the two main data transformation activities
 */
Interface Encryption_Interface
{
    public function encrypt($text, $key);
    public function decrypt($text, $key);
}


class Mcrypt Implements Encryption_Interface
{
    protected $cipher;
    protected $mode;

    public function __construct($cipher = MCRYPT_RIJNDAEL_256, $mode = MCRYPT_MODE_ECB)
    {
        $this->cipher = $cipher;
        $this->mode   = $mode;
    }

    public function encrypt($text, $key)
    {
        $data = mcrypt_encrypt($this->cipher, $key, $text, $this->mode);
        return base64_encode($data);
    }

    public function decrypt($text, $key)
    {
        $text = base64_decode($text);
        $data = mcrypt_decrypt($this->cipher, $key, $text, $this->mode);
        return $data;
    }
}


class SSLCrypt Implements Encryption_Interface
{
    protected $cipher_algorithm;
    protected $digest_method;
    protected $vector_length;

    public function __construct($cipher_algorithm = 'aes-256-ctr', $digest_method = 'sha256')
    {
        $this->cipher_algorithm = $cipher_algorithm;
        $this->digest_method = $digest_method;

        if (!in_array($cipher_algorithm, openssl_get_cipher_methods(TRUE))) {
            throw new \Exception(__METHOD__ . " Unknown cipher $cipher_algorithm");
        }

        if (!in_array($digest_method, openssl_get_md_methods(TRUE))) {
            throw new \Exception(__METHOD__ . " Unknown digest $digest_method");
        }

        $this->ivector_length = openssl_cipher_iv_length($cipher_algorithm);
    }

    public function encrypt($text, $key)
    {
        $keyhash = openssl_digest($key, $this->digest_method, TRUE);
        $ivector = mcrypt_create_iv($this->ivector_length, MCRYPT_DEV_URANDOM);
        $crypted = openssl_encrypt($text, $this->cipher_algorithm, $keyhash, OPENSSL_RAW_DATA, $ivector);
        if ($crypted === FALSE) {
            throw new \Exception(__METHOD__ . ' Failed: ' . openssl_error_string());
        }

        // RETURN THE IV AND THE ENCRYPTED DATA
        return base64_encode($ivector . $crypted);
    }

    public function decrypt($text, $key)
    {
        $keyhash = openssl_digest($key, $this->digest_method, TRUE);
        $rawdata = base64_decode($text);
        if (strlen($rawdata) < $this->ivector_length) {
            throw new \Exception(__METHOD__ . ' Data is too short');
        }

        // SEPARATE THE IV AND THE ENCRYPTED DATA
        $ivector = substr($rawdata, 0, $this->ivector_length);
        $rawtext = substr($rawdata, $this->ivector_length);
        $decrypt = openssl_decrypt($rawtext, $this->cipher_algorithm, $keyhash, OPENSSL_RAW_DATA, $ivector);

        if ($decrypt === FALSE) {
            throw new \Exception(__METHOD__ . ' Failed: ' . openssl_error_string());
        }

        return $decrypt;
    }
}







// INSTANTIATE ENCRYPTION OBJECTS FROM THE CLASS
$m = new MCrypt;
$s = new SSLCrypt;

// INITIALIZE VARS FOR LATER USE IN THE HTML FORM
$m_encoded = $m_decoded = NULL;
$s_encoded = $s_decoded = NULL;
$m_secret  = !empty($_POST['m_secret']) ? $_POST['m_secret'] : NULL;
$s_secret  = !empty($_POST['s_secret']) ? $_POST['s_secret'] : NULL;

// IF ANYTHING WAS POSTED SHOW THE DATA
if (!empty($_POST))
{
    if ($_POST['choice'] == 'mcr')
    {
        if (!empty($_POST["m_clearstring"]))
        {
            $m_encoded = $m->encrypt($_POST["m_clearstring"], $_POST["m_secret"]);
            echo "<br/>{$_POST["m_clearstring"]} YIELDS MCrypt ENCODED ";
            var_dump($m_encoded);
        }

        if (!empty($_POST["m_cryptstring"]))
        {
            $m_decoded = $m->decrypt($_POST["m_cryptstring"], $_POST["m_secret"]);
            echo "<br/>{$_POST["m_cryptstring"]} YIELDS MCrypt DECODED ";
            var_dump($m_decoded);
        }
    }

    if ($_POST['choice'] == 'ssl')
    {
        if (!empty($_POST["s_clearstring"]))
        {
            $s_encoded = $s->encrypt($_POST["s_clearstring"], $_POST["s_secret"]);
            echo "<br/>{$_POST["s_clearstring"]} YIELDS OpenSSL ENCODED ";
            var_dump($s_encoded);
        }

        if (!empty($_POST["s_cryptstring"]))
        {
            $s_decoded = $s->decrypt($_POST["s_cryptstring"], $_POST["s_secret"]);
            echo "<br/>{$_POST["s_cryptstring"]} YIELDS OpenSSL DECODED ";
            var_dump($s_decoded);
        }
    }
}
// CREATE THE FORM USING HEREDOC NOTATION
$form = <<<FORM

<form method="post">
<h3>Using MCrypt</h3>
<input type="hidden" name="choice" value="mcr" />
Secret Password (Key):
<br>
<input name="m_secret" value="$m_secret" autocomplete="off" />
<br>
<textarea name="m_clearstring">$m_decoded</textarea>
<input type="submit" value="ENCRYPT MCrypt" />
<br>
<textarea name="m_cryptstring">$m_encoded</textarea>
<input type="submit" value="DECRYPT MCrypt" />
</form>

<form method="post">
<h3>Using OpenSSL</h3>
<input type="hidden" name="choice" value="ssl" />
Secret Password (Key):
<br>
<input name="s_secret" value="$s_secret" autocomplete="off" />
<br>
<textarea name="s_clearstring">$s_decoded</textarea>
<input type="submit" value="ENCRYPT OpenSSL" />
<br>
<textarea name="s_cryptstring">$s_encoded</textarea>
<input type="submit" value="DECRYPT OpenSSL" />
<br>
</form>

FORM;

echo $form;

Open in new window

0
gr8gonzoConsultantCommented:
The error is pretty straightforward. Basically this line:

tripleDES.Key = UTF8Encoding.UTF8.GetBytes(key);

...is resulting in a key that is probably too large. My guess is that the UTF-8 encoding isn't returning the expected results. Try using ASCII encoding for this particular task:

tripleDES.Key = Encoding.ASCII.GetBytes(key);

If that still doesn't work, you might want to attach a debugger to the code, add a breakpoint on this line, and then check the resulting length of that byte array (the result of the GetBytes() call). Maybe something unintentional is in your key, like a line break or something.
1
Don VonderBurgPresident/CEOAuthor Commented:
gr8gonzo - I have tried changing the line to ASCII and still get the same error. The key is the same one I use in the PHP code that works. I can hand off the key which is 5 characters long and encrypted text to the PHP that looks something like "Rah0BJEi39qwAeICy6La4gE9jGNBggfnkeVx" and it will work.
0
Exploring ASP.NET Core: Fundamentals

Learn to build web apps and services, IoT apps, and mobile backends by covering the fundamentals of ASP.NET Core and  exploring the core foundations for app libraries.

gr8gonzoConsultantCommented:
Aha - I missed that tidbit about the key size in your original PHP code. Your original code doesn't use a 5-character key. It hashes those 5 characters using SHA-256 (into raw byte form) and then uses the first 16 bytes of the raw hash as your key.

So you'd need to do the same steps in your C# code.
0
gr8gonzoConsultantCommented:
It's also worth noting that you don't handle the IV in your C# code either. Once you have your 16-byte key, you'll need to use it to generate the IV.

According to your PHP code, you'll need to create a byte array contains 8 null bytes (8 bytes is the block size for 3DES). Then encrypt a single space character (using 3DES, your key, and then the 8-null-byte-array as your IV) and use THAT 8-byte result as your decryption IV.

I think that approach gives you the same IV every time, though, which weakens your encryption. It's why most people just generate a random IV at encryption -time and prepend it to your encrypted result instead. Then at decryption time, you just shift the first 8 bytes off of the beginning of the encrypted bytes and you have your IV.
0
gr8gonzoConsultantCommented:
I haven't heard any updates on this in a while. If you're still struggling, some code to help:

To get the raw byte array for SHA-256 in C#, first add these "using" statements:
using System.Security.Cryptography;
using System.IO;

Open in new window

...then this code should help you fill in the blanks:
 // Generate the key from your 5-character string
string originalKey = "Hello!";
byte[] hashed_key = new SHA256Managed().ComputeHash(new MemoryStream(Encoding.UTF8.GetBytes(originalKey))).Take(16).ToArray();

// Build the IV
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = hashed_key;
tdes.Mode = CipherMode.CBC;
tdes.IV = new byte[8];
byte[] encrypted_iv = tdes.CreateEncryptor().TransformFinalBlock(new byte[0], 0, 0);
tdes.Clear();

// Now when decrypting, set:
// tripleDES.IV = encrypted_iv;
// tripleDES.Key = hashed_key;

Open in new window

0
Don VonderBurgPresident/CEOAuthor Commented:
yes I am struggling still. Thanks for the code example. Now I am getting a different error.

"Length of the data to decrypt is invalid."

It throws at:

byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);

Open in new window


The weird thing is the inputArray is 27. So I get the same error when I change the inputArray.Length to 27 or any number less than that.

More head banging.
0
gr8gonzoConsultantCommented:
In your current C# code, you have padding set to:
tripleDES.Padding = PaddingMode.PKCS7;

However, mcrypt will use null padding, so you could TRY changing that to PaddingMode.Zeros and see if that works.

If it still throws the same error, it probably means that something along the way tried to trim() the encrypted data and trimmed off the padded null bytes. Basically, the input data to decrypt SHOULD be an even multiple of the 8-byte block size (e.g. 8, 16, 24, 32, 40, 48, 56, 64, etc...), so if the inputArray length is 27, then you're either missing 5 bytes or you have 3 bytes too many.

If that's truly the case, then you could try just padding the input array yourself with null bytes (take the current length, find the next length that divides evenly by 8, then create a new byte array of that length, and use the source array's "CopyTo" method to copy the data over into the bigger array).

It's a little hard to know exactly for sure what the problem with the input data is without knowing the original encryption code and how the data makes its way from PHP over to C#.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Don VonderBurgPresident/CEOAuthor Commented:
You led me in the right direction.

Also another 3rd party provided at /n software found a solution at the same time. Since I am a red carpet subscriber there I use many of their tools.  The IPWorks!Encrypt tool is what I wound up using.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Encryption

From novice to tech pro — start learning today.