1

I am trying to solve an encryption issue I am having between php and c#.

I have encrypted data using the following php and openssl operation.

$encrypt_method = "AES-256-CBC";
$secret_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
$secret_iv = 'XXXXXXXXXXXXXXXX';

$key = hash ('sha256', $secret_key);
$iv = substr (hash ('sha256', $secret_iv), 0, 16);

$output = openssl_encrypt ($string, $encrypt_method, $key, 0, $iv);
$output = base64_encode ($output);

I have tried a couple of methods in C# to decrypt but this is what I am trying now.

public string Encrypt_Decrypt(string action, string value) {
    string secretKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
    string secretIV = "XXXXXXXXXXXXXXXX";

    string key = Hash(secretKey);

    string iv = Hash(secretIV).Substring(0,16);

    string retValue = "";

    if (action == "encrypt") {
        retValue = EncryptString(value, key, iv);
    }
    else if (action == "decrypt") {
        retValue = DecryptString(value, key, iv);
    }
}

// Hash to match php hash function
public static string Hash(string unhashedString) {
    return BitConverter.ToString(new SHA256CryptoServiceProvider().ComputeHash(Encoding.Default.GetBytes(unhashedString))).Replace("-", String.Empty).ToLower();
}

    public static string DecryptString(string cipherData, string keyString, string ivString) {
        byte[] key = Encoding.UTF8.GetBytes(keyString);
        Console.WriteLine(key.Length);
        byte[] iv = Encoding.UTF8.GetBytes(ivString);
        Console.WriteLine(iv.Length);
        byte[] cipherCrypt = Convert.FromBase64String(cipherData);


        for (int i = 0; i < cipherCrypt.Length; i++) {
            Console.Write(cipherCrypt[i] + " ");
        }


        try {
            RijndaelManaged crypto = new RijndaelManaged();
            crypto.Key = key;
            crypto.IV = iv;
            crypto.Mode = CipherMode.CBC;
            crypto.KeySize = 256;
            crypto.BlockSize = 128;
            crypto.Padding = PaddingMode.None;

            ICryptoTransform decryptor = crypto.CreateDecryptor(crypto.Key, crypto.IV);

            using (MemoryStream memStream = new MemoryStream(cipherCrypt)) {
                using (CryptoStream cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Read)) {
                    using (StreamReader streamReader = new StreamReader(cryptoStream)) {
                        return streamReader.ReadToEnd();
                    }
                }
            }
        }
        catch (CryptographicException e) {
            Console.WriteLine("A Cryptographic error occurred: {0}", e.Message);
            return null;
        }
    }

I have tried a couple different encoding types when getting the byte[] for the operation.

I keep getting the following error:

Specified key is not a valid size for this algorithm.

Not sure what I am missing. Any help is appreciated.

Also, I already read through this and tried what the solution suggestion recommended. I got the same resulting error.

UPDATE - 01 I have updated the code here to reflect the code I have changed.

The key length is 32,

The iv length is 16,

The data coming in at "cipherData" is length 32,

When "cipherData" goes through "FromBase64String(cipherData)" it comes out as a 24 byte array. This is causing an issue for the decryptor which wants a 32 byte array.

Community
  • 1
  • 1
StewVanB
  • 117
  • 2
  • 8
  • Well, how long is the key? – Artjom B. May 23 '15 at 16:44
  • I believe that it is 256 from the hashing function but I am all new to this. The Hash code above is from: http://stackoverflow.com/a/1419092/1481280 I edited that code to produce a sha256 hash that matched the php hash. – StewVanB May 23 '15 at 18:01

1 Answers1

0

There are obviously problems with the key size. The code between PHP and C# seem to match. The problem seems to be that the code is wrong in both cases.

Let's see how long the key actually is:

  1. Start with a 32 byte key (non-encoded).
  2. Hash the key with SHA-256: 32 bytes (non-encoded).
  3. Encode to hex (integrated into PHP's hash() function by default): 64 bytes.

AES only supports the following key sizes: 16, 24 and 32 bytes. openssl_encrypt() will only use the first 32 bytes of the hex key silently. So, you need to use the first 32 bytes in C#.

Note that openssl_encrypt() takes an options argument which denotes that the output is Base64 when OPENSSL_RAW_DATA is not set. It means that the PHP output was encoded twice with Base64. So you need to decode it twice in C#.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Do you have a reference for openssl_encrypt using the first 32 bytes? – StewVanB May 23 '15 at 18:58
  • No. I've read that somewhere on StackOverflow. But that's usually the case for most PHP and JavaScript crypto implementations. – Artjom B. May 23 '15 at 19:00
  • I have tried using the first 32 bytes from the key. I still get the same key length error. – StewVanB May 23 '15 at 19:05
  • Ok, I'll ask again. How long is the key actually? I'm sure you can print the length to the console. – Artjom B. May 23 '15 at 19:08
  • 1
    Key is 32 length, iv is 16 length, the base64 cipherData is 32 length. When cipherData goes through FromBase64String() it becomes a 24 byte array. This is causing a new error: **A Cryptographic error occurred: Length of the data to decrypt is invalid.** This is because RijndaelManaged wants a 32 byte data array but is only getting 24. I have tried all the padding options in RijndaelManaged and have also tried manually padding the 24 bytes up to 32. – StewVanB May 24 '15 at 15:02
  • Note that `openssl_encrypt()` takes an options argument which denotes that the output is Base64 when `OPENSSL_RAW_DATA` is not set. It means that the PHP output was encoded twice with Base64. So you need to decode it twice in C#. – Artjom B. May 24 '15 at 15:58
  • Thanks, that fixed my issue. I marked yours as the answer but I can't up vote. – StewVanB May 25 '15 at 01:03