3

I'm writing a simple encryption system for logging in but I've got small issue. C# encrypt function:

public static string EncryptString(string Message, string Passphrase)
{
    byte[] Results;
    System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();

    // Step 1. We hash the passphrase using MD5
    // We use the MD5 hash generator as the result is a 128 bit byte array
    // which is a valid length for the TripleDES encoder we use below

    MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
    byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));

    // Step 2. Create a new TripleDESCryptoServiceProvider object
    TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

    // Step 3. Setup the encoder
    TDESAlgorithm.Key = TDESKey;
    TDESAlgorithm.Mode = CipherMode.ECB;
    TDESAlgorithm.Padding = PaddingMode.PKCS7;

    // Step 4. Convert the input string to a byte[]
    byte[] DataToEncrypt = UTF8.GetBytes(Message);

    // Step 5. Attempt to encrypt the string
    try
    {
        ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
        Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
    }
    finally
    {
        // Clear the TripleDes and Hashprovider services of any sensitive information
        TDESAlgorithm.Clear();
        HashProvider.Clear();
    }

    // Step 6. Return the encrypted string as a base64 encoded string
    return Convert.ToBase64String(Results);
}

EncryptString("test", "123456") returns "Yjaqhc7RFds=".

The same code in php:

  <?php
    $key = "123456";
    function pkcs7_pad($text, $blocksize)
    {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    $input = pkcs7_pad("test", 16);
    $key = md5(utf8_encode($key), true);
    $td = mcrypt_module_open('tripledes', '', 'ecb', '');
    $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    mcrypt_generic_init($td, $key, $iv);
    $encrypted_data = mcrypt_generic($td, $input);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);

    echo base64_encode($encrypted_data);
?>

returns "dybhiZYdKG8pNCgCFkbV6g=="? What am I doing wrong?

NullUserException
  • 83,810
  • 28
  • 209
  • 234
Charlie Hopperson
  • 415
  • 1
  • 9
  • 23
  • Wait a minute, why are you creating an IV if you are using ECB? And what exactly is the purpose of this? Please don't tell me you're using it to encrypt passwords. – NullUserException Nov 15 '12 at 17:26
  • I'm using it to encrypt paswords, but it is only part of many-steps crypting. If it is possible, I don't want to change C# code, I prefere making changes in php. – Charlie Hopperson Nov 15 '12 at 17:32
  • 1
    If I may ask, why are you *encrypting* your passwords instead of hashing them? – NullUserException Nov 15 '12 at 17:33
  • Cause I'm planning to encrypt the whole data (login,password,some extra infos) into one decryptable message – Charlie Hopperson Nov 15 '12 at 17:41
  • 3
    Which would mean that you (and by extension, anyone who manages to hack you) would have access to the plaintext password, right? – NullUserException Nov 15 '12 at 17:45
  • only user of the app would have access to plaintext, but that mean that crackers too. – Charlie Hopperson Nov 15 '12 at 17:57
  • Do users of the app really need access to plaintext? – NullUserException Nov 15 '12 at 18:12
  • they must log in... I belive that blowfish + 3DES + some more will be enought to protect the app for a few weeks – Charlie Hopperson Nov 15 '12 at 18:15
  • Are they logging into your application? If so, you don't need the plaintext password. – NullUserException Nov 15 '12 at 18:31
  • The password/login isn't needed to be well protect, but extra infos are needed to. The sever response also should by encrypted in the same system (perhaps with another keys) – Charlie Hopperson Nov 15 '12 at 18:52
  • 2
    It sounds like you are re-inventing a secure transport layer. What's wrong with just using SSL? – NullUserException Nov 15 '12 at 18:53
  • You don't need to purchase SSL certificates. You can generate them yourself. The only drawback is that you aren't able to use the truststores that come with e.g. browsers and operating systems to perform the certificate chain validation. You may just use self signed certifices or - if you are really into it - create your own certificate PKI. – Maarten Bodewes Nov 18 '12 at 13:04
  • 1
    Can you just answer the questions ? I'm googling this page, searching a way to encrypt and decrypt with C# on a side and PHP on another. The question is a technical one, why asking other questions ? – Nicolas Thery Aug 11 '16 at 12:42

1 Answers1

8

You're running into this problem because Triple DES's key size is 168 bits (21 bytes), but MD5 generates hashes that are only 16 bytes (128 bits) long.

This means the key will have to extended to 168 bits so Triple DES can work. It turns out that this derivation from 128 bits to 168 bits works differently in C# than it does in PHP, so the key that's effectively used is different, resulting in different encrypted data.

Now you have two options:


Option 1: Pick a cipher that supports 128-bit keys outright

You can avoid all the problems related to key size differences if you use a cipher that supports 128-bit keys. This would require minimal changes to your code. You can use Rijndael (AES), for example.

C#: Change TripleDESCryptoServiceProvider to RijndaelManaged.
Everything else can stay pretty much the same. (Demo)

PHP: Use MCRYPT_RIJNDAEL_128 instead of tripledes (Demo):

function encrypt_pkcs7($str, $key)
{
    $key = md5(utf8_encode($key), true);
    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
    $pad = $block - (strlen($str) % $block);
    $str .= str_repeat(chr($pad), $pad);

    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
    return base64_encode($ciphertext);
}

echo encrypt_pkcs7('test', '123456');

Note that the effective key size of AES is larger than Triple DES. Although Triple DES' key is 168-bit long, it only offers 112 bits of security. I would pick this option if I were you.


Option 2: Use a 192-bit key instead of a 128-bit key

If you use a larger key than Triple DES actually uses, C# and PHP seem to agree on how to reduce it to 168 bits. You can do this by using a hash function like SHA-256, which generates a 256-bit hash and trim it to 192 bits (24 bytes):

C#: Use SHA256CryptoServiceProvider and Array.Copy to get a 192-bit key, and use that with the rest of your program: (Demo)

SHA256CryptoServiceProvider HashProvider = new SHA256CryptoServiceProvider();
byte[] temp = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
byte[] key = new byte[24];
Array.Copy(temp, key, 24);

PHP: Use hash() with SHA-256 and substr() to get the 192-bit key

function encrypt_pkcs7($str, $key)
{
    // derive 192-bit key using SHA-256
    $key = substr(hash('sha256', $key, true), 0, 24);
    $block = mcrypt_get_block_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
    $pad = $block - (strlen($str) % $block);
    $str .= str_repeat(chr($pad), $pad);

    $ciphertext = mcrypt_encrypt(MCRYPT_3DES, $key, $str, MCRYPT_MODE_ECB);
    return base64_encode($ciphertext);
}

echo encrypt_pkcs7('test', '123456');

It doesn't look like I can supply a 168-bit key to TripleDESCryptoServiceProvider, I don't know why.


Other considerations:

  • Consider using SSL; even if it's a self-signed cert. It beats re-inventing the wheel, a particularly dangerous task when cryptography is involved.

  • Consider using a mode of operation other than ECB (eg: CBC). Using ECB increases security risks. Read the Wikipedia article on block cipher modes of operation.

  • Unless you absolutely need to have a plaintext password (rarely the case), you should hash your passwords. Read this article on securing passwords.

  • Consider using a proper password-based key derivation function, like PBKDF2 instead of a general purpose hash function like MD5 or the SHA family. This will make cracking the key harder. For more information, read the article in the previous bullet point.

Community
  • 1
  • 1
NullUserException
  • 83,810
  • 28
  • 209
  • 234