0

I need to decrypt a string as part of an authorization process.

The documentation specifies that the authorization string was encrypted with the following settings:

  • Padding of input data: PKCS*7
  • Password byte array is 32 bytes long. Password string is converted to UTF-16 encoded byte array and byte array is then padded with zeroes up to length of 32 bytes. Longer passwords are truncated.

The C# example:

    /// <summary>
    /// Decrypts a string.
    /// </summary>
    /// <param name="content">The string to decrypt.</param>
    /// <param name="password">The password to use.</param>
    /// <returns>The decrypted string.</returns>
    private static string DecryptString(string content, string password)
    {
        Rijndael aes;
        byte[] retVal = null;
        byte[] contentBytes;
        byte[] passwordBytes;
        byte[] ivBytes;

        try
        {
            contentBytes = Convert.FromBase64String(content);

            //Create the password and initial vector bytes
            passwordBytes = new byte[32];
            ivBytes = new byte[16];
            Array.Copy(Encoding.Unicode.GetBytes(password), passwordBytes, Encoding.Unicode.GetBytes(password).Length);
            Array.Copy(passwordBytes, ivBytes, 16);

            //Create the cryptograpy object
            using (aes = Rijndael.Create())
            {
                aes.Key = passwordBytes;
                aes.IV = ivBytes;
                aes.Padding = PaddingMode.PKCS7;

                //Decrypt
                retVal = aes.CreateDecryptor().TransformFinalBlock(contentBytes, 0, contentBytes.Length);
            }
        }
        catch
        {
        }

        return Encoding.Unicode.GetString(retVal);
    }

The same function was discussed here, but for JAVA: Decrypt C# RIJNDAEL encoded text

I tried to decrypt it with the following function but the result is different than expected:

function decrypt($string, $pass){
    $iv = substr($pass, 0, 16);
    $data =  mcrypt_decrypt(MCRYPT_RIJNDAEL_256,
                        $pass,
                        base64_decode($string),
                        MCRYPT_MODE_CBC,
                        $iv);
    $pad = ord($data[strlen($data) - 1]);
    return substr($data, 0, -$pad);
}

The ecrypted string "7iTdZnp0DtGnIfwwqY4W/glbLLVZ0+asVLAuz13PzrW0wM6HC7rNuQvcG8JDSehyYeBJARdXHgLo9hRL9sBz3fN5LJ8cro3o0kFnAao2YRU="

should decrypt to

"ldYWMFlSbcki6LMl3rkNfGavnt8VqmZd" 

using the password "GAT"

I think it has something to do with the password / iv / encoding

Community
  • 1
  • 1
Alex
  • 3
  • 2
  • You aren't showing how you encode the $pass :-) – xanatos Apr 22 '15 at 10:17
  • But I can confirm that the block mode is CBC, so `MCRYPT_MODE_CBC` is right. – xanatos Apr 22 '15 at 10:19
  • This is the part i don't get: `Password string is converted to UTF-16 encoded byte array and byte array is then padded with zeroes up to length of 32 bytes.` For mcrypt_decrypt, for the key the php manual explains that - `If it's smaller than the required keysize, it is padded with '\0'.` How should I encode the password before using it in mcrypt_decrypt? – Alex Apr 22 '15 at 11:08
  • You only need to do the Encoding to UTF16 and if necessary the cut to 32 bytes – xanatos Apr 22 '15 at 11:13
  • Already tried that: $password = mb_convert_encoding('GAT', 'utf-16le'); – Alex Apr 22 '15 at 11:24
  • if you are using php >= 5.6 read http://php.net/manual/en/function.mcrypt-decrypt.php *5.6.0 Invalid key and iv sizes are no longer accepted. mcrypt_decrypt() will now throw a warning and return FALSE if the inputs are invalid. Previously keys and IVs were padded with '\0' bytes to the next valid size.* – xanatos Apr 22 '15 at 11:30
  • PHP 5.3.3. The only warning I get is `Warning: mcrypt_decrypt(): The IV parameter must be as long as the blocksize`. Could the cipher be MCRYPT_RIJNDAEL_128 and not MCRYPT_RIJNDAEL_256? (Because the IV is 16 bytes in the C# example) – Alex Apr 22 '15 at 11:52
  • Yes... Just wrote the correct piece of code below – xanatos Apr 22 '15 at 11:53
  • Thank you. It works as expected. – Alex Apr 22 '15 at 11:58

1 Answers1

0
function decrypt($string, $pass)
{
    $encodedPass = mb_convert_encoding($pass, 'utf-16le');
    $encodedPass = substr($encodedPass, 0, 32);
    $encodedPass = str_pad($encodedPass, 32, "\0", STR_PAD_RIGHT);
    $iv = substr($encodedPass, 0, 16);

    $data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
                        $encodedPass,
                        base64_decode($string),
                        MCRYPT_MODE_CBC,
                        $iv);
    $pad = ord($data[strlen($data) - 1]);
    return substr($data, 0, -$pad);
}

In PHP, the MCRYPT_RIJNDAEL_128/MCRYPT_RIJNDAEL_256 isn't the size of the key, but the size of the block (see https://www.chilkatsoft.com/p/php_aes.asp). C# normally uses 16 bytes blocks, so MCRYPT_RIJNDAEL_128. The size of the key is auto-detected. Note that I've moved the encoding/resizing of the password inside the method.

xanatos
  • 109,618
  • 12
  • 197
  • 280