2

I want to decrypt in VB.net strings encrypted in PHP via openssl_encrypt function:

$encrypted_string = openssl_encrypt(
    $string,
    'AES256',
    'secret_password',
    0,
    'initialization_vector'
);

I tried with this class:

Public Class Aes256Encrypter
    Public Function Encrypt(ByVal plainText As String, ByVal secretKey As String) As Byte()
        Dim encryptedPassword As Byte()
        Using outputStream As MemoryStream = New MemoryStream()
            Dim algorithm As RijndaelManaged = getAlgorithm(secretKey)
            Using cryptoStream As CryptoStream = New CryptoStream(outputStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write)
                Dim inputBuffer() As Byte = Encoding.Unicode.GetBytes(plainText)
                cryptoStream.Write(inputBuffer, 0, inputBuffer.Length)
                cryptoStream.FlushFinalBlock()
                encryptedPassword = outputStream.ToArray()
            End Using
        End Using
        Return encryptedPassword
    End Function

    Public Function Decrypt(ByVal encryptedBytes As Byte(), ByVal secretKey As String) As String
        Dim plainText As String = Nothing
        Using inputStream As MemoryStream = New MemoryStream(encryptedBytes)
            Dim algorithm As RijndaelManaged = getAlgorithm(secretKey)
            Using cryptoStream As CryptoStream = New    CryptoStream(inputStream, algorithm.CreateDecryptor(), CryptoStreamMode.Read)
                Dim outputBuffer(0 To CType(inputStream.Length - 1, Integer)) As Byte
                Dim readBytes As Integer = cryptoStream.Read(outputBuffer, 0, CType(inputStream.Length, Integer))
                plainText = Encoding.Unicode.GetString(outputBuffer, 0, readBytes)
            End Using
        End Using
        Return plainText
    End Function

    Private Function getAlgorithm(ByVal secretKey As String) As RijndaelManaged
        Const salt As String = "put your salt here"
        Const keySize As Integer = 256

        Dim keyBuilder As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(secretKey, Encoding.Unicode.GetBytes(salt))
        Dim algorithm As RijndaelManaged = New RijndaelManaged()
        algorithm.KeySize = keySize
        algorithm.IV = keyBuilder.GetBytes(CType(algorithm.BlockSize / 8, Integer))
        algorithm.Key = keyBuilder.GetBytes(CType(algorithm.KeySize / 8, Integer))
        algorithm.Padding = PaddingMode.PKCS7
        Return algorithm
    End Function
End Class

I use IV "initialization_vector" as salt string, but at runtime an error is thrown: 'System.Security.Cryptography.CryptographicException' in mscorlib.dll. An error occurred: Padding is invalid and cannot be removed.

Where am I doing wrong?

18
  • 1
    Possible duplicate of Standard library for AES encryption for VB.NET? Commented Jan 9, 2017 at 13:23
  • Thank you, I'm trying with this right now. Commented Jan 9, 2017 at 13:28
  • Sorry @SaggingRufus but still nothing. It's not clear how should I use the initialization vector. If I put it as a salt, at runtime it throws an exception, 'System.Security.Cryptography.CryptographicException' in mscorlib.dll: Padding is invalid and cannot be removed Commented Jan 9, 2017 at 13:41
  • is the salt padded with spaces? Commented Jan 9, 2017 at 14:05
  • @SaggingRufus if with "salt" is meant the initialization vector, it is alphanumeric, with no spaces. I don't know what type of padding does openssl_encrypt function uses as default Commented Jan 9, 2017 at 14:10

2 Answers 2

4

Okay, so this is a C# answer, and I don't like it; but I've produced a thing which gives the same answer as php's openssl_encrypt.

  • PHP passes the string as if it were bytes, so Encoding.ASCII (not sure what it does with non-ASCII data, actually)
  • PHP/OpenSSL silently truncates long keys (so your 60 character password is a 32 character password)
  • Working with the same data in ASCII strings and hex bytes is a pain in the rear.

Code:

private static byte[] php_encode(string php_string, int expectedLength)
{
    byte[] temp = Encoding.ASCII.GetBytes(php_string);

    if (temp.Length == expectedLength)
    {
        return temp;
    }

    byte[] ret = new byte[expectedLength];
    Buffer.BlockCopy(temp, 0, ret, 0, Math.Min(temp.Length, expectedLength));
    return ret;
}

private static string php_encrypt_aes256(string php_data, string php_password, string php_iv)
{
    byte[] key = php_encode(php_password, 32);
    byte[] iv = php_encode(php_iv, 16);
    byte[] data = Encoding.ASCII.GetBytes(php_data);

    using (Aes aes = Aes.Create())
    using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv))
    {
        return Convert.ToBase64String(encryptor.TransformFinalBlock(data, 0, data.Length));
    }
}

Test inputs:

PHP:

$ /usr/bin/php -r "print(openssl_encrypt('potato', 'AES256', '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 0, '0123456789ABCDEF') . \"\n\");"
wiZGvfABfairN4gKcnqD2Q==
$ /usr/bin/php -r "print(openssl_encrypt('potato', 'AES256', '0123456789abcdefghijklmnopqrstuv', 0, '0123456789ABCDEF') . \"\n\");"
wiZGvfABfairN4gKcnqD2Q==
$ /usr/bin/php -r "print(openssl_encrypt('potato', 'AES256', '0123456789abcdefghijklmnopqrstu', 0, '0123456789ABCDEF') . \"\n\");"
XUkYwaomkIXYstlOQlNoBQ==
$ /usr/bin/php -r "print(openssl_encrypt('potato', 'AES256', '0123456789abcdefghijklmnopqrstuv', 0, '0123456789ABCDE') . \"\n\");"
PHP Warning:  openssl_encrypt(): IV passed is only 15 bytes long, cipher expects an IV of precisely 16 bytes, padding with \0 in Command line code on line 1
ah+yXvQEocgLNES2nh2kXA==

openssl enc

$ openssl enc -aes-256-cbc -K 303132333435363738396162636465666768696a6b6c6d6e6f70717273747576 -in enc.data -a -iv 30313233343536373839414243444546
wiZGvfABfairN4gKcnqD2Q==
$ openssl enc -aes-256-cbc -K 303132333435363738396162636465666768696a6b6c6d6e6f70717273747500 -in enc.data -a -iv 30313233343536373839414243444546
XUkYwaomkIXYstlOQlNoBQ==
$ openssl enc -aes-256-cbc -K 303132333435363738396162636465666768696a6b6c6d6e6f70717273747576 -in enc.data -a -iv 30313233343536373839414243444500
ah+yXvQEocgLNES2nh2kXA==

My code

Console.WriteLine(php_encrypt_aes256("potato", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789ABCDEF"));
Console.WriteLine(php_encrypt_aes256("potato", "0123456789abcdefghijklmnopqrstuv", "0123456789ABCDEF"));
Console.WriteLine(php_encrypt_aes256("potato", "0123456789abcdefghijklmnopqrstu", "0123456789ABCDEF"));
Console.WriteLine(php_encrypt_aes256("potato", "0123456789abcdefghijklmnopqrstuv", "0123456789ABCDE"));

Output:

wiZGvfABfairN4gKcnqD2Q==
wiZGvfABfairN4gKcnqD2Q==
XUkYwaomkIXYstlOQlNoBQ==
ah+yXvQEocgLNES2nh2kXA==
Sign up to request clarification or add additional context in comments.

2 Comments

Great, this works fine! I get the same results as openssl_encrypt. But in order to achieve the reverse operation, what should I do?
Reverse the operations: Convert.FromBase64String the string to data, then use CreateDecryptor instead of CreateEncryptor, and Encoding.ASCII.GetString the bytes back to the original string. Same key/IV handling code.
1

I modified it slightly, but I got decryption working in VB.net if anyone needs it.

Private Shared Function php_decrypt_aes256(ByVal php_password As String, ByVal php_iv As String, ByVal php_data As String) As String
    Dim key As Byte() = php_encode(php_password, 32)
    Dim iv As Byte() = php_encode(php_iv, 16)
    Dim buff As Byte() = Encoding.ASCII.GetBytes(php_data)

    Using aes As Aes = Aes.Create()
        Using decryptor As ICryptoTransform = aes.CreateDecryptor(key, iv)
            buff = Convert.FromBase64String(php_data)
            Return ASCIIEncoding.ASCII.GetString(decryptor.TransformFinalBlock(buff, 0, buff.Length))
        End Using
    End Using
End Function

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.