0

I want to encrypt a text in PHP and to decrypt in in C#, but i can't.

This is my PHP code:

define('AES_256_ECB', 'aes-256-ecb');
$encryption_key = "SomeSimpleTest";
$data = "Test123";
$encryptedData = openssl_encrypt($data, AES_256_ECB, $encryption_key, 0);

..and this is my C# code:

(AESEncryption.cs class)

   using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using System.Security.Cryptography;

    namespace AESCrypto
    {
        class AESEncryption
        {
            public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
            {
                byte[] decryptedBytes = null;
                // Set your salt here to meet your flavor:
                byte[] saltBytes = passwordBytes;
                // Example:
                //saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

                using (MemoryStream ms = new MemoryStream())
                {
                    using (RijndaelManaged AES = new RijndaelManaged())
                    {
                        AES.KeySize = 256;
                        AES.BlockSize = 256;

                        var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
                        AES.Key = key.GetBytes(AES.KeySize / 8);
                        AES.IV = key.GetBytes(AES.BlockSize / 8);

                        AES.Mode = CipherMode.ECB;
                        //AES.Padding = PaddingMode.PKCS7;

                        using (CryptoStream cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
                        {
                            cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                            cs.Close();
                        }
                        decryptedBytes = ms.ToArray();
                    }
                }

                return decryptedBytes;
            }

            public static string Decrypt(string decryptedText, byte[] passwordBytes)
            {
                byte[] bytesToBeDecrypted = Convert.FromBase64String(decryptedText);

                // Hash the password with SHA256
                passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

                byte[] decryptedBytes = AES_Decrypt(bytesToBeDecrypted, passwordBytes);

                // Getting the size of salt
                int saltSize = GetSaltSize(passwordBytes);

                // Removing salt bytes, retrieving original bytes
                byte[] originalBytes = new byte[decryptedBytes.Length - saltSize];
                for (int i = saltSize; i < decryptedBytes.Length; i++)
                {
                    originalBytes[i - saltSize] = decryptedBytes[i];
                }

                return Encoding.UTF8.GetString(originalBytes);
            }

            public static int GetSaltSize(byte[] passwordBytes)
            {
                var key = new Rfc2898DeriveBytes(passwordBytes, passwordBytes, 1000);
                byte[] ba = key.GetBytes(2);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < ba.Length; i++)
                {
                    sb.Append(Convert.ToInt32(ba[i]).ToString());
                }
                int saltSize = 0;
                string s = sb.ToString();
                foreach (char c in s)
                {
                    int intc = Convert.ToInt32(c.ToString());
                    saltSize = saltSize + intc;
                }

                return saltSize;
            }

            public static byte[] GetRandomBytes(int length)
            {
                byte[] ba = new byte[length];
                RNGCryptoServiceProvider.Create().GetBytes(ba);
                return ba;
            }
        }

    }

Usage of it:

    using AESCrypto;

    ...
 public string DecryptText(string input, string password)
            {
                // Get the bytes of the string
                byte[] bytesToBeDecrypted = Convert.FromBase64String(input);
                byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
                passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

                byte[] bytesDecrypted = AESEncryption.AES_Decrypt(bytesToBeDecrypted, passwordBytes);

                string result = Encoding.UTF8.GetString(bytesDecrypted);

                return result;
            }

     private void btn1_Click(object sender, EventArgs e)
            {
                textBox1.Text = DecryptText("KEY_ENCRYPTED_WITH_PHP", "SomeSimpleTest");
            }

I even tried with CBC but does not work...The mode of encryption is not important. I only want to make it work as it should. Thanks.

djxm2m
  • 61
  • 1
  • 8
  • You define your constant as `AES_256_ECB` but reference it as `AES_256_ecb` (This is why it's good to pay attention to notice-level messages). – alanlittle Apr 19 '17 at 19:47
  • I realised that. Now the data is encrypted, but when I decrypt it in C# I get exceptions. (padding and length errors) – djxm2m Apr 19 '17 at 20:02
  • Sorry, can't help you with the C#. How are you sending the encrypted data? Have you checked that what you're receiving is what was sent? Perhaps you should post the error messages. – alanlittle Apr 19 '17 at 20:20
  • 2
    1. The encryption mode is only important if the encryption needs to be secure. 2.The encryption mode must be the same for both and if CBC mode the IV must be the same. 3. The encryption key should be an exact supported length and also the same. 4. You should explicitly specify the padding for both, PKCS#7 (née PKCS#5) is a good choice 5. Verify the encodings, if any, of all inputs/outputs. – zaph Apr 19 '17 at 20:50
  • 1
    As zaph said. Your key stuff is completely different. AES-256 expects a 256 bit key. Your `"SomeSimpleTest"` key is only 112 bits long. This is not a valid key. Additionally, your PHP code isn't doing any key derivation, so you don't need `SHA256` or `Rfc2898DeriveBytes` in C#. Then setting `AES.BlockSize = 256;` doesn't mean that it is AES. This is Rijndael and not AES anymore. – Artjom B. Apr 19 '17 at 21:48
  • 1
    **Never use [ECB mode](http://crypto.stackexchange.com/q/14487/13022)**. It's deterministic and therefore not semantically secure. You should at the very least use a randomized mode like [CBC](http://crypto.stackexchange.com/q/22260/13022) or [CTR](http://crypto.stackexchange.com/a/2378/13022). It is better to authenticate your ciphertexts so that attacks like a [padding oracle attack](http://crypto.stackexchange.com/q/18185/13022) are not possible. This can be done with authenticated modes like GCM or EAX, or with an [encrypt-then-MAC](http://crypto.stackexchange.com/q/202/13022) scheme. – Artjom B. Apr 19 '17 at 21:49
  • So, AES.BlockSize should be 128? @Artjom B. , i don't need random output. I want to encrypt a string with PHP, and to decrypt it with C# manually. How can I set the padding using openssl_encrypt? – djxm2m Apr 20 '17 at 08:42
  • 1
    Yes, `AES.BlockSize = 128` is the default and necessary in order to do actual AES. You don't need to change the padding. OpenSSL and C# are doing PKCS#7 padding by default. – Artjom B. Apr 20 '17 at 17:22
  • I suggest you take a look at this thread https://stackoverflow.com/a/75846684/5258809 where I shared similar classes for encryption that I wrote in C# and PHP. – MiMFa Mar 26 '23 at 12:41

1 Answers1

3

php code:

define('AES_128_ECB', 'aes-128-ecb');
$encryption_key = "MY_16_CHAR_KEY:)";
$data = "MyOwnEncryptedSecretText";
$encryptedData = openssl_encrypt($data, AES_128_ECB, $encryption_key, 0);

C# code:

public String Decrypt(String text, String key)
{
    //decode cipher text from base64
    byte[] cipher = Convert.FromBase64String(text);
    //get key bytes
    byte[] btkey = Encoding.ASCII.GetBytes(key);

    //init AES 128
    RijndaelManaged aes128 = new RijndaelManaged();
    aes128.Mode = CipherMode.ECB;
    aes128.Padding = PaddingMode.PKCS7;

    //decrypt
    ICryptoTransform decryptor = aes128.CreateDecryptor(btkey, null);
    MemoryStream ms = new MemoryStream(cipher);
    CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);

    byte[] plain = new byte[cipher.Length];
    int decryptcount = cs.Read(plain, 0, plain.Length);

    ms.Close();
    cs.Close();

    //return plaintext in String
    return Encoding.UTF8.GetString(plain, 0, decryptcount);
}

and usage of it:

string DecryptedText = Decrypt("GENERATED_KEY", "MY_16_CHAR_KEY:)");

Now it works great :) Thanks.

djxm2m
  • 61
  • 1
  • 8