Search code examples
php.netencryptionaesphp-openssl

Convert .net aes256 encryption code into php


The code below is a simple .NET snippet, having test on the input it returns p+cTm2VODfvQnreAl02wUQ== as an output.

Dim aesEncryptObj As New System.Security.Cryptography.RijndaelManaged()
Dim encoder As System.Text.ASCIIEncoding = New System.Text.ASCIIEncoding()

Dim tempKey As Byte() = encoder.GetBytes("00000011111111111111111111111111")
aesEncryptObj.Key = tempKey
aesEncryptObj.BlockSize = 128
aesEncryptObj.Mode = System.Security.Cryptography.CipherMode.ECB
aesEncryptObj.Padding = System.Security.Cryptography.PaddingMode.PKCS7
aesEncryptObj.GenerateIV()

Dim EncryptedBytes As Byte()
Dim encryptor As System.Security.Cryptography.ICryptoTransform = aesEncryptObj.CreateEncryptor(aesEncryptObj.Key, aesEncryptObj.IV)

Using msEncrypt As New System.IO.MemoryStream()
    Using csEncrypt As New System.Security.Cryptography.CryptoStream(msEncrypt, encryptor, System.Security.Cryptography.CryptoStreamMode.Write)
        Using swEncrypt As New System.IO.StreamWriter(csEncrypt)
            swEncrypt.Write(txtInput.Text)
        End Using
        EncryptedBytes = msEncrypt.ToArray()
    End Using
End Using

txtOutput.Text = Convert.ToBase64String(EncryptedBytes)

Now, here is the PHP code:

const ENCRYPT_METHOD = 'aes-256-ecb';

$aesKey = pack('H*', '00000011111111111111111111111111');
$ivSize = openssl_cipher_iv_length(ENCRYPT_METHOD);

$plainText = "test";

$iv = openssl_random_pseudo_bytes($ivSize);

$cipherText = openssl_encrypt(
    $plainText,
    ENCRYPT_METHOD,
    $aesKey,
    OPENSSL_RAW_DATA,
    $iv
);

$encryptedText = $iv . $cipherText;

echo base64_encode($encryptedText);

It returns 1W3UvYVNKWEoFrpPZPd+Qw== which differs from the .NET one. I've tried both aes-256-ecb and aes-128-ecb and the result is always different from the .NET's one.

As far as I know, openssl_encrypt do the PKCS7 padding by default, is that right? Can you see the reason why PHP is giving different result?


Solution

  • Your code isn't working because:

    • GetBytes in .NET returns you the byte values of that string. That is, you are going to get a byte array with a length of 32 (AES-256). However, pack in PHP with H* decodes a hex string, which is going to give you a key of length 16 (AES-128). This whole time you've been encrypting with not only two different keys, but two different key sizes.

    Fixing the above will make your code work, but it will be far from actually secure and should not be used. If you want to make your code secure, you need to:

    • Stop using ECB mode. Use GCM mode if you can, CBC otherwise. You are most likely going to end up using CBC. If you do, you need to prepend the IV to your ciphertext so that the decrypting party can retrieve it and use it when decrypting. IVs don't need to be secret, they just need to be random, so your use of OpenSSL random bytes and the GenerateIV method is good.
    • Since you are likely going to be using CBC mode, you will need to apply an HMAC to ensure integrity. Otherwise anyone can modify your ciphertext and you won't know. When applying an HMAC, always encrypt-then-mac. Remember to include your IV before applying the HMAC.
    • Lastly, and I don't think you actually meant to do this, but never use the ASCII values of a string as a key. If you want to create a key from a passphrase, you should apply a KDF. PBKDF2 is probably the best suited and has implementations in both PHP and .NET.