1

I'm using openssl_encrypt to encrypt strings and decrypt them within powershell

In PHP

   $data = "helloWorld";

   $key  = 'CefaiNooH4oi6oje';
   $iv   = 'Choodub8ahd4choo';
   $data = openssl_encrypt($data, 'AES-256-CBC', $key, 0, $iv);

   echo base64_encode($data);

In Powershell

$data = "dnQvNEhEczBnQ0F5OEQ4Yi9tOUY4Zz09"
$data = [System.Convert]::FromBase64String($data)

$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
$aesManaged.Key = [system.Text.Encoding]::UTF8.GetBytes("CefaiNooH4oi6oje")
$aesManaged.IV = [system.Text.Encoding]::UTF8.GetBytes("Choodub8ahd4choo")
$decryptor = $aesManaged.CreateDecryptor();

$clear = $decryptor.TransformFinalBlock($data, 16, $data.Length - 16);

[System.Text.Encoding]::UTF8.GetString($clear).Trim([char]0)

Then I got an error:

Exception calling "TransformFinalBlock" with "3" argument(s): "The input data is not a complete block."

What could be wrong here?

daisy
  • 22,498
  • 29
  • 129
  • 265
  • This answer may help you - https://stackoverflow.com/a/19720218/416574 It appears (and I just checked the php source code to verify this) that the ssl_encrypt will 0 pad the key to make it the appropriate length. The key you have specified is 16 bytes, so it is going to add another 16 0 bytes to the key. I still can't get it to decode your result, but hoped that link may prove helpful. – pstrjds Dec 16 '17 at 09:09
  • I also would recommend switching from `TransformFinalBlock` which is a special function you would call on the very last block of data. Instead you can just use [TransformBlock](https://msdn.microsoft.com/en-us/library/system.security.cryptography.icryptotransform.transformblock(v=vs.110).aspx) – pstrjds Dec 16 '17 at 09:33

1 Answers1

1

Edit #2
In all my excitement of having figured out why garbage was coming out when you actually get the decryption code working, I neglected to answer the question as to why you were getting the error:
Exception calling "TransformFinalBlock" with "3" argument(s): "The input data is not a complete block."
The issue is that you are passing in a chunk of data that is not long enough to be a block. Using the original string that you were converting to bytes, you get an array of 24 bytes, but the block size is 16 bytes, so you would need to have an array with a length that is a multiple of 16. Using the correct string vt/4HDs0gCAy8D8b/m9F8g==, you will get an array of 16, which will work (although there is a minor correction to your call to TransformFinalBlock that needs to be made, you want to call it with an offset of 0 and with the length of your input array - I have updated my solution below to show this).

End Edit #2

I figured it out!!!!

So, first problem is here:

echo base64_encode($data);

The $data value you are getting back from openssl_encrypt is already base64 encoded, so there is no need to encode it again. If you remove that line, you get this string back vt/4HDs0gCAy8D8b/m9F8g== (see ideone link)

And just in case you need proof that that is what happened, here is another ideone link showing what happens if you convert your string from Base64 and then convert those bytes to a string, you get the original Base64 encoded string that came out of the call to openssl_encrypt.

Now for the decrypting part (note, I am going to post the code in C# but should be fairly trivial to convert to powershell, if there is an issue comment and I will try to help). I typically would create a decryptor and use a stream, but I wanted to fit with your code, so I am using the TransformBlock method.

string result = null;
using (var aes = new AesManaged())
{
    // openssl_encrypt will zero pad a key that is not the correct length
    var key = Encoding.UTF8.GetBytes("CefaiNooH4oi6oje");
    if (key.Length < 32)
    {
        var temp = new byte[key.Length + (32 - key.Length % 32)];
        Array.Copy(key, temp, key.Length);
        key = temp;
    }
    var iv = Encoding.UTF8.GetBytes("Choodub8ahd4choo");

    aes.Mode = CipherMode.CBC;
    aes.KeySize = 256;
    aes.Key = key;
    aes.IV = iv;
    aes.Padding = PaddingMode.Zeros;
    var cipher = Convert.FromBase64String("vt/4HDs0gCAy8D8b/m9F8g==");
    using (var decryptor = aes.CreateDecryptor())
    {
        var buffer = decryptor.TransformFinalBlock(cipher, 0, cipher.Length);
        result = Encoding.UTF8.GetString(buffer);
    }
}

Edit Adding decryption code using streams (how I normally do it in C#)

using (var aes = new AesManaged())
{
    // See sample above for setup code, omitted for brevity
    using (var dest = new MemoryStream())
    using (var input = new MemoryStream(cipher))
    using (var cs = new CryptoStream(input, aes.CreateDecryptor(), CryptoStreamMode.Read))
    {
        var buffer = new byte[1024];
        var read = cs.Read(buffer, 0, buffer.Length);
        while(read > 0)
        {
            dest.Write(buffer, 0, read);
            read = cs.Read(buffer, 0, buffer.Length);
        }
        dest.Flush();

        result = Encoding.UTF8.GetString(dest.ToArray());
    }
}
pstrjds
  • 16,840
  • 6
  • 52
  • 61