-1

Some time ago I implemented a C# web API to serve information.
This information is encrypted and is consumed by other either C# or Classic ASP websites.
Tis is how I am doing the encryption / decryption using the methods EncryptRijndael and DecryptRijndael.

using System;
using System.Text;

namespace Encryption_Test
{
    using System.IO;
    using System.Security.Cryptography;
    using System.Text.RegularExpressions;
    using System.Windows.Forms;

    class RijndaelManagedEncryption
    {
        //http://www.codeproject.com/Tips/704372/How-to-use-Rijndael-ManagedEncryption-with-Csharp

        #region Rijndael Encryption

        /// <summary>
        /// Encrypt the given text and give the byte array back as a BASE64 string
        /// </summary>
        /// <param name="text" />The text to encrypt
        /// <param name="salt" />The pasword salt
        /// <returns>The encrypted text</returns>
        public static string EncryptRijndael(string text, string salt, string inputKey)
        {
            if (string.IsNullOrEmpty(text))
                throw new ArgumentNullException("text");

            var aesAlg = NewRijndaelManaged(salt, inputKey);

            var blockSize = aesAlg.BlockSize;

            var strK = System.Text.Encoding.ASCII.GetString(aesAlg.Key);
            string s = strK;

            var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
            var msEncrypt = new MemoryStream();
            using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            using (var swEncrypt = new StreamWriter(csEncrypt))
            {
                swEncrypt.Write(text);
            }

            return Convert.ToBase64String(msEncrypt.ToArray());
        }
        #endregion

        #region Rijndael Dycryption
        /// <summary>
        /// Checks if a string is base64 encoded
        /// </summary>
        /// <param name="base64String" />The base64 encoded string
        /// <returns>
        public static bool IsBase64String(string base64String)
        {
            base64String = base64String.Trim();
            return (base64String.Length%4 == 0) &&
                   Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);

        }

        /// <summary>
        /// Decrypts the given text
        /// </summary>
        /// <param name="cipherText" />The encrypted BASE64 text
        /// <param name="salt" />
        /// <param name="inputKey"></param>
        /// The pasword salt
        /// <returns>De gedecrypte text</returns>
        public static string DecryptRijndael(string cipherText, string salt, string inputKey)
        {
            if (string.IsNullOrEmpty(cipherText))
                throw new ArgumentNullException("cipherText");

            if (!IsBase64String(cipherText))
                throw new Exception("The cipherText input parameter is not base64 encoded");

            string text;

            var aesAlg = NewRijndaelManaged(salt, inputKey);
            var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
            var cipher = Convert.FromBase64String(cipherText);

            using (var msDecrypt = new MemoryStream(cipher))
            {
                using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (var srDecrypt = new StreamReader(csDecrypt))
                    {
                        text = srDecrypt.ReadToEnd();
                    }
                }
            }
            return text;
        }
        #endregion

        #region NewRijndaelManaged

        /// <summary>
        /// Create a new RijndaelManaged class and initialize it
        /// </summary>
        /// <param name="salt" />
        /// <param name="inputKey"></param>
        /// The pasword salt
        /// <returns>
        private static RijndaelManaged NewRijndaelManaged(string salt, string inputKey)
        {
            if (salt == null) throw new ArgumentNullException("salt");
            var saltBytes = Encoding.ASCII.GetBytes(salt);
            var key = new Rfc2898DeriveBytes(inputKey, saltBytes);

            var aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);  //256 / 8 = 32
            aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8); //128 / 8 = 16
            //string k = System.Text.Encoding.Default.GetString(aesAlg.Key);
            //string i = System.Text.Encoding.Default.GetString(aesAlg.IV);
            //string l = k + i;

            #region testPHP
            ///*
            // So it would seem the week point in the chain for PHP is the Rfc2898DeriveBytes
            // */
            //aesAlg.Key = Encoding.UTF8.GetBytes(inputKey);
            //aesAlg.IV = Encoding.UTF8.GetBytes(salt);

            //k = System.Text.Encoding.Default.GetString(aesAlg.Key);
            //i = System.Text.Encoding.Default.GetString(aesAlg.IV);
            //l = k + i;
            #endregion testPHP

            return aesAlg;
        }
        #endregion
    }
}

You can see the commented out when towards the end where I just set the Key and IV from the supplied parameters by just converting those to byte[]. That seems OK for PHP but I'd rather not omit the Rfc2898DeriveBytes.

It works just fine and the consuming sites are able to decrpt the information.

Now here is my (well someone else's problem but I want to help), a PHP site now needs to consume my Web API. They seem unable to do it. They site that it is due to the way the IV is created.

Now this makes me wonder if

  1. They are not up to the job
  2. My implementation has made it impossible for them to do it.

Now I know very little about PHP but can generally follow the flow of a block of its code. I'd appreciate it if anyone could firstly tell me if it SHOULD be possible to achieve the goal with PHP, and if yes, maybe some pointers on how to do so.

Note - this is utilizing Rfc2898DeriveBytes which I belie is the crux of the issue and DISTINGUISHES this question from others similar.

An example

  • String to Encrypt : Co-operation is the key to success!
  • Salt: This_is_the_password_salt
  • Input key: This_is_the_input_key
  • Encrypted string: pLgIEjhNGDMfI0IynoAdbey3NKbOJzgUzYAlU14OWOpuZy7/lr7HRtFhiRKfjbZz
9
  • No this is not a duplicate. The one you refer to is not utilising Rfc2898DeriveBytes. And it would seem that this piece in particular is the issue. Commented Sep 1, 2016 at 10:58
  • @RiggsFolly - please reopen Commented Sep 1, 2016 at 11:19
  • You do not give enough information about How you actually encrypt data so helping is almost impossible. You are also not the person with the issue so we cannot see the other guys attempt at de-crypting your data. So this question is kind of un answerable Commented Sep 1, 2016 at 11:46
  • I've now inlcuded the code rather than a link. Commented Sep 1, 2016 at 12:25
  • 2
    Tell you what! You encrypt a know string using your C# code. Post the string you started with and the resultant encrypted string, Someone here might write some PHP to unencrypt it. That would at least prove that it was possible. You can then return to your user and tell them how to do it. Does that sound like a plan Commented Sep 1, 2016 at 12:33

1 Answer 1

0

Well - after finding a sandbox that could take hash_hmac I seem to have sussed it out spurred on by you guys and your comments......

Using this site.

and the following code in it (I just hope it behaves the same in a real situation)

<?php

class Foo {

public function decrypt_full($key, $iv, $encrypted)
{
$dev = $this->pbkdf2("sha1", $key, $iv, 1000, 48, true);
$derived_key = substr($dev, 0, 32); //Keylength: 32
$derived_iv = substr($dev, 32, 16); // IV-length: 16
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $derived_key, base64_decode($encrypted), MCRYPT_MODE_CBC, $derived_iv);
}

private function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
die('PBKDF2 ERROR: Invalid hash algorithm.');
if($count <= 0 || $key_length <= 0)
die('PBKDF2 ERROR: Invalid parameters.');

$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);

$output = "";
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
return substr($output, 0, $key_length);
}

}
//###########################################################################################
$encrypted = "pLgIEjhNGDMfI0IynoAdbey3NKbOJzgUzYAlU14OWOpuZy7/lr7HRtFhiRKfjbZz";
$iv = "This_is_the_password_salt";
$key = "This_is_the_input_key";

$foo = new foo;
echo "<br/>";
echo "Encrypted String: ".$encrypted."<br/>";
echo "Decrypted string: ".$foo->decrypt_full($key, $iv, $encrypted )."<br/>";
?>

The output is...

Key: .g���13f^sI>M��j$\�+�od�mY# �!
IV: �2]��&y�q� WJ��
Decrypted: Co-operation is the key to success!

Can't wait to tell the PHP guys ;)

Sign up to request clarification or add additional context in comments.

8 Comments

The reason the output is displayes gibberish is because encrypted data is not a string, it is an array of seemingly 8-bit btes including values that have no printable representation and are not even legal encodings in many encoding systems such as UTF-8. This is why hexadecimal is generally used to display data and Base64 for transmitting data as a string.
What you are calling an IV $iv is really a salt for pbkdf2().
It amazes me that people can just copy and paste code from the internet that "seems to work" when dealing with the security of their applications. How do you sleep at night?
You'd do well to guess the password salt and key. So I do sleep well :)
Cheers @Zaph - noted
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.