Search code examples
c#encryptionaesopayo

c# AES Decryption


I am working with SagePay Forms and currently converting the VB examples they have to c#. I have made good progress so the encryption part of my project works fine (SagePay can decrypt it).

The issue I am having is that when I attempt to decrypt the string, it turns to garbage. If anyone has done this before I would really appreciate some help with my decryption code. I have included the encryption code which works and the first two lines are the setup and call from another method.

I haven't added the VB code but if this is required I could add it. Didn't want a huge post if not required.

Utility Methods:

public string byteArrayToHexString(byte[] ba)
    {
    return BitConverter.ToString(ba).Replace("-", "");
    }

public static byte[] StringToByteArray(string hex)
    {
        return Enumerable.Range(0, hex.Length)
                         .Where(x => x % 2 == 0)
                         .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                         .ToArray();
    }

Main Encryption Method with first couple of lines being the calling of it extracted from a larger method.

string crypt = "blahblahblah"
string EncryptAndEncode = "@" + byteArrayToHexString(aesEncrypt(crypt));


        private byte[] aesEncrypt(string inputText)
    {

        RijndaelManaged AES = new RijndaelManaged();

        //set the mode, padding and block size for the key
        AES.Padding = PaddingMode.PKCS7;
        AES.Mode = CipherMode.CBC;
        AES.KeySize = 128;
        AES.BlockSize = 128;

        //convert key and plain text input into byte arrays
        Byte[] keyAndIvBytes = UTF8Encoding.UTF8.GetBytes("tR7nR6wZHGjYMCuV");
        Byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText);//AbHLlc5uLone0D1q

        //create streams and encryptor object
        MemoryStream memoryStream = new MemoryStream();
        CryptoStream cryptoStream = new CryptoStream(memoryStream, AES.CreateEncryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Write);

        //perform encryption
        cryptoStream.Write(inputBytes, 0, inputBytes.Length);
        cryptoStream.FlushFinalBlock();

        //get encrypted stream into byte array
        Byte[] outBytes = memoryStream.ToArray();

        //close streams
        memoryStream.Close();
        cryptoStream.Close();
        AES.Clear();

        return outBytes;
    }

Decoding and Decrypting methods

public string DecodeAndDecrypt(string strIn)
    {
        //** HEX decoding then AES decryption, CBC blocking with PKCS5 padding - DEFAULT **


        string DecodeAndDecrypt = aesDecrypt(StringToByteArray(strIn.Substring(1)));
        return (DecodeAndDecrypt);
    }

    private string aesDecrypt(Byte[] inputBytes)
    {
    RijndaelManaged AES = new RijndaelManaged();
    Byte[] keyAndIvBytes = UTF8Encoding.UTF8.GetBytes("tR7nR6wZHGjYMCuV");
    Byte[] outputBytes = inputBytes;//Convert.FromBase64String(inputBytes);

    //set the mode, padding and block size
    AES.Padding = PaddingMode.PKCS7;
    AES.Mode = CipherMode.CBC;
    AES.KeySize = 128;
    AES.BlockSize = 128;

    //create streams and decryptor object
    MemoryStream memoryStream = new MemoryStream(outputBytes);
    CryptoStream cryptoStream = new CryptoStream(memoryStream, AES.CreateEncryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Read);
    //perform decryption
    cryptoStream.Read(outputBytes, 0, outputBytes.Length);
    Trace.WriteLine(outputBytes);
    //close streams
    memoryStream.Close();
    cryptoStream.Close();
    AES.Clear();
    //return System.Text.Encoding.UTF8.GetString(outputBytes);

    string plainText = Encoding.UTF8.GetString(outputBytes,
                                   0,
                                   outputBytes.Length);

    return plainText;
    }

Solution

  • There are actually multiple problems with your code. First in your decrypt method you're creating an encryptor, that should be a decryptor. Secondly you're reading the entire block including the padding of your algorithm into the buffer when you do the decryption. Below is a class with the items fixed and should be returning the proper result. I do however suggest you find a better way of storing the key, putting in your code and generating it the way you'r edoing it is a no no. You should generate your key with an RNG (RNGCryptoServiceProvider) then hash it with a secure hashing algorithm such as SHA512, use that output for your key. You then need to find a good place to store it, I would look into encrypting your web.config file.

    public static class EncryptionHelper
    {
        private static byte[] keyAndIvBytes;
    
        static EncryptionHelper()
        {
            // You'll need a more secure way of storing this, I hope this isn't
            // the real key
            keyAndIvBytes = UTF8Encoding.UTF8.GetBytes("tR7nR6wZHGjYMCuV");
        }
    
        public static string ByteArrayToHexString(byte[] ba)
        {
            return BitConverter.ToString(ba).Replace("-", "");
        }
    
        public static byte[] StringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }
    
        public static string DecodeAndDecrypt(string cipherText)
        {
            string DecodeAndDecrypt = AesDecrypt(StringToByteArray(cipherText));
            return (DecodeAndDecrypt);
        }
    
        public static string EncryptAndEncode(string plaintext)
        {
            return ByteArrayToHexString(AesEncrypt(plaintext));
        }
    
        public static string AesDecrypt(Byte[] inputBytes)
        {
            Byte[] outputBytes = inputBytes;
    
            string plaintext = string.Empty;
    
            using (MemoryStream memoryStream = new MemoryStream(outputBytes))
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateDecryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(cryptoStream))
                    {
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }
    
            return plaintext;
        }
    
        public static byte[] AesEncrypt(string inputText)
        {
            byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText);//AbHLlc5uLone0D1q
    
            byte[] result = null;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateEncryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Write))
                {
                    cryptoStream.Write(inputBytes, 0, inputBytes.Length);
                    cryptoStream.FlushFinalBlock();
    
                    result = memoryStream.ToArray();
                }
            }
    
            return result;
        }
    
    
        private static RijndaelManaged GetCryptoAlgorithm()
        {
            RijndaelManaged algorithm = new RijndaelManaged();
            //set the mode, padding and block size
            algorithm.Padding = PaddingMode.PKCS7;
            algorithm.Mode = CipherMode.CBC;
            algorithm.KeySize = 128;
            algorithm.BlockSize = 128;
            return algorithm;
        }
    }
    

    Calling it is easy:

    string crypt = "blahblahblah";
    string EncryptAndEncode = EncryptionHelper.EncryptAndEncode(crypt);            
    Console.WriteLine(EncryptAndEncode);
    
    Console.WriteLine(EncryptionHelper.DecodeAndDecrypt(EncryptAndEncode));
    
    Console.ReadLine();