Avatar of Don VonderBurg
Don VonderBurg
Flag for United States of America asked on

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

EncryptionPHPC#

Avatar of undefined
Last Comment
Don VonderBurg

8/22/2022 - Mon
Ray Paseur

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

gr8gonzo

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.
Don VonderBurg

ASKER
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.
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
gr8gonzo

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.
gr8gonzo

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.
gr8gonzo

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

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Don VonderBurg

ASKER
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.
ASKER CERTIFIED SOLUTION
gr8gonzo

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
Don VonderBurg

ASKER
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.