Avatar of Don VonderBurg
Don VonderBurgFlag 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
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

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

Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

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.
Avatar of 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.
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

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.
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

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.
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

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

Avatar of 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
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

Blurred text
THIS SOLUTION IS ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
Avatar of 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.
PHP
PHP

PHP is a widely-used server-side scripting language especially suited for web development, powering tens of millions of sites from Facebook to personal WordPress blogs. PHP is often paired with the MySQL relational database, but includes support for most other mainstream databases. By utilizing different Server APIs, PHP can work on many different web servers as a server-side scripting language.

125K
Questions
--
Followers
--
Top Experts
Get a personalized solution from industry experts
Ask the experts
Read over 600 more reviews

TRUSTED BY

IBM logoIntel logoMicrosoft logoUbisoft logoSAP logo
Qualcomm logoCitrix Systems logoWorkday logoErnst & Young logo
High performer badgeUsers love us badge
LinkedIn logoFacebook logoX logoInstagram logoTikTok logoYouTube logo