1

'm trying to encrypt a string on a server and have the string decrypted on another machine, I'm using the technique for encrypting the string found on another thread linked below - .NET Built-in Encrypt(AES)-Then-MAC(HMAC) Encrypt and decrypt a string

I get a "Padding is invalid and cannot be removed" error message if the string is encrypted on one server and I try to decrypt it on another.

I can not use any nuget packages for this code.

I I'm then sending the IV and Keyfile to a base64 string to store it, passing the base64 String into an application and trying to decrypt the string. This will work if I do the process on one machine.

  1. Encrypt the string and generate an IV + Key (or two key pair)

  2. Convert all of the encrypted password information as a base64 string.

  3. Convert the information back into byte[] and decrypt the password.

    [TestMethod]
    public void EncryptAString()
    {
        byte[] ba_TestIV = AESThenHMAC.NewKey();
        byte[] ba_TestKey = AESThenHMAC.NewKey();
        string s_EncryptedPassword = AESThenHMAC.SimpleEncrypt("PassOrFailMessage", ba_TestIV, ba_TestKey);
        string s_TestIV = Convert.ToBase64String(ba_TestIV);
        string s_TestKey = Convert.ToBase64String(ba_TestKey);
    
    
    /*Everything Below This Point Is Run From A Different Application Which Is Passed The Three Base64 Strings */
        string s_DecryptedString = AESThenHMAC.SimpleDecrypt(s_EncryptedPassword, Convert.FromBase64String(s_TestIV), Convert.FromBase64String(s_TestKey));
        Assert.IsTrue(s_DecryptedString == "PassOrFailMessage");
    }
    

This Test method should help clarify the steps I'm trying to achieve.

using the class provided in the other thread does anyone know if I can encrypt the string and decrypt it elsewhere without having a padding issue error?

Apologies for not including the referenced code. Please find below found in thread Encrypt and decrypt a string

https://gist.github.com/jbtule/4336842#file-aesgcm-cs

    /*
      * This work (Modern Encryption of a String C#, by James Tuley), 
     * identified by James Tuley, is free of known copyright restrictions.
      * https://gist.github.com/4336842
     * http://creativecommons.org/publicdomain/mark/1.0/ 
     */

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

    namespace Encryption
    {
        public static class AESThenHMAC
        {
            private static readonly RandomNumberGenerator Random =
            RandomNumberGenerator.Create();

            //Preconfigured Encryption Parameters
            public static readonly int BlockBitSize = 128;
            public static readonly int KeyBitSize = 256;

            //Preconfigured Password Key Derivation Parameters
            public static readonly int SaltBitSize = 64;
            public static readonly int Iterations = 10000;
            public static readonly int MinPasswordLength = 12;

            /// <summary>
            /// Helper that generates a random key on each call.
            /// </summary>
            /// <returns></returns>
            public static byte[] NewKey()
            {
                var key = new byte[KeyBitSize / 8];
                Random.GetBytes(key);
                return key;
            }

            /// <summary>
            /// Simple Encryption (AES) then Authentication (HMAC) for a UTF8 Message.
            /// </summary>
            /// <param name="secretMessage">The secret message.</param>
            /// <param name="cryptKey">The crypt key.</param>
            /// <param name="authKey">The auth key.</param>
            /// <param name="nonSecretPayload">(Optional) Non-Secret Payload.</param>
            /// <returns>
            /// Encrypted Message
            /// </returns>
            /// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception>
            /// <remarks>
            /// Adds overhead of (Optional-Payload + BlockSize(16) + Message-Padded-To-Blocksize +  HMac-Tag(32)) * 1.33 Base64
            /// </remarks>
            public static string SimpleEncrypt(string secretMessage, byte[] cryptKey, byte[] authKey,
                               byte[] nonSecretPayload = null)
            {
                if (string.IsNullOrEmpty(secretMessage))
                    throw new ArgumentException("Secret Message Required!", "secretMessage");

                var plainText = Encoding.UTF8.GetBytes(secretMessage);
                var cipherText = SimpleEncrypt(plainText, cryptKey, authKey, nonSecretPayload);
                return Convert.ToBase64String(cipherText);
            }

            /// <summary>
            /// Simple Authentication (HMAC) then Decryption (AES) for a secrets UTF8 Message.
            /// </summary>
            /// <param name="encryptedMessage">The encrypted message.</param>
            /// <param name="cryptKey">The crypt key.</param>
            /// <param name="authKey">The auth key.</param>
            /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param>
            /// <returns>
            /// Decrypted Message
            /// </returns>
            /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
            public static string SimpleDecrypt(string encryptedMessage, byte[] cryptKey, byte[] authKey,
                               int nonSecretPayloadLength = 0)
            {
                if (string.IsNullOrWhiteSpace(encryptedMessage))
                    throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

                var cipherText = Convert.FromBase64String(encryptedMessage);
                var plainText = SimpleDecrypt(cipherText, cryptKey, authKey, nonSecretPayloadLength);
                return plainText == null ? null : Encoding.UTF8.GetString(plainText);
            }

            /// <summary>
            /// Simple Encryption (AES) then Authentication (HMAC) of a UTF8 message
            /// using Keys derived from a Password (PBKDF2).
            /// </summary>
            /// <param name="secretMessage">The secret message.</param>
            /// <param name="password">The password.</param>
            /// <param name="nonSecretPayload">The non secret payload.</param>
            /// <returns>
            /// Encrypted Message
            /// </returns>
            /// <exception cref="System.ArgumentException">password</exception>
            /// <remarks>
            /// Significantly less secure than using random binary keys.
            /// Adds additional non secret payload for key generation parameters.
            /// </remarks>

            public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
            {
                //User Error Checks
                if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
                    throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "cryptKey");

                if (authKey == null || authKey.Length != KeyBitSize / 8)
                    throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "authKey");

                if (secretMessage == null || secretMessage.Length < 1)
                    throw new ArgumentException("Secret Message Required!", "secretMessage");

                //non-secret payload optional
                nonSecretPayload = nonSecretPayload ?? new byte[] { };

                byte[] cipherText;
                byte[] iv;

                using (var aes = new AesManaged
                {
                    KeySize = KeyBitSize,
                    BlockSize = BlockBitSize,
                    Mode = CipherMode.CBC,
                    Padding = PaddingMode.PKCS7
                })
                {

                    //Use random IV
                    aes.GenerateIV();
                    iv = aes.IV;

                    using (var encrypter = aes.CreateEncryptor(cryptKey, iv))
                    using (var cipherStream = new MemoryStream())
                    {
                        using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
                        using (var binaryWriter = new BinaryWriter(cryptoStream))
                        {
                            //Encrypt Data
                            binaryWriter.Write(secretMessage);
                        }

                        cipherText = cipherStream.ToArray();
                    }

                }

                //Assemble encrypted message and add authentication
                using (var hmac = new HMACSHA256(authKey))
                using (var encryptedStream = new MemoryStream())
                {
                    using (var binaryWriter = new BinaryWriter(encryptedStream))
                    {
                        //Prepend non-secret payload if any
                        binaryWriter.Write(nonSecretPayload);
                        //Prepend IV
                        binaryWriter.Write(iv);
                        //Write Ciphertext
                        binaryWriter.Write(cipherText);
                        binaryWriter.Flush();

                        //Authenticate all data
                        var tag = hmac.ComputeHash(encryptedStream.ToArray());
                        //Postpend tag
                        binaryWriter.Write(tag);
                    }
                    return encryptedStream.ToArray();
                }

            }

            public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0)
            {

                //Basic Usage Error Checks
                if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
                    throw new ArgumentException(String.Format("CryptKey needs to be {0} bit!", KeyBitSize), "cryptKey");

                if (authKey == null || authKey.Length != KeyBitSize / 8)
                    throw new ArgumentException(String.Format("AuthKey needs to be {0} bit!", KeyBitSize), "authKey");

                if (encryptedMessage == null || encryptedMessage.Length == 0)
                    throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

                using (var hmac = new HMACSHA256(authKey))
                {
                    var sentTag = new byte[hmac.HashSize / 8];
                    //Calculate Tag
                    var calcTag = hmac.ComputeHash(encryptedMessage, 0, encryptedMessage.Length - sentTag.Length);
                    var ivLength = (BlockBitSize / 8);

                    //if message length is to small just return null
                    if (encryptedMessage.Length < sentTag.Length + nonSecretPayloadLength + ivLength)
                        return null;

                    //Grab Sent Tag
                    Array.Copy(encryptedMessage, encryptedMessage.Length - sentTag.Length, sentTag, 0, sentTag.Length);

                    //Compare Tag with constant time comparison
                    var compare = 0;
                    for (var i = 0; i < sentTag.Length; i++)
                        compare |= sentTag[i] ^ calcTag[i];

                    //if message doesn't authenticate return null
                    if (compare != 0)
                        return null;

                    using (var aes = new AesManaged
                    {
                        KeySize = KeyBitSize,
                        BlockSize = BlockBitSize,
                        Mode = CipherMode.CBC,
                        Padding = PaddingMode.PKCS7
                    })
                    {

                        //Grab IV from message
                        var iv = new byte[ivLength];
                        Array.Copy(encryptedMessage, nonSecretPayloadLength, iv, 0, iv.Length);

                        using (var decrypter = aes.CreateDecryptor(cryptKey, iv))
                        using (var plainTextStream = new MemoryStream())
                        {
                            using (var decrypterStream = new CryptoStream(plainTextStream, decrypter, CryptoStreamMode.Write))
                            using (var binaryWriter = new BinaryWriter(decrypterStream))
                            {
                                //Decrypt Cipher Text from Message
                                binaryWriter.Write(
                                  encryptedMessage,
                                  nonSecretPayloadLength + iv.Length,
                                  encryptedMessage.Length - nonSecretPayloadLength - iv.Length - sentTag.Length
                                );
                            }
                            //Return Plain Text
                            return plainTextStream.ToArray();
                        }
                    }
                }
            }
        }
    }
TLCONE
  • 104
  • 1
  • 8
  • Unfortunately, you'll need to post the entire code if you want us to be able to help you. Please read how to create a [mcve]. The fact that "it doesn't work when decrypted elsewhere" makes me think that you are not sending all the data correctly. – Camilo Terevinto Jan 30 '18 at 10:20
  • Check that the encrypted string on the source machine (at the point where you encrypt it) is identical to the encrypted string on the target machine (at the point where are trying to decrypt it). – Polyfun Jan 30 '18 at 10:24
  • the code I'm using is pretty similar to the test method, I'm using the first 5 lines (up to the point of decrypting it ) on the server and the last two lines, the simpledecrypt and assert on the application side. The base64 strings are stored in different locations (sql, sharepoint, DynamicsCRM) If I were to set up a process to send the 3 base 64 strings to one location manually copy (crtl+c) and paste them into a test method I would get the same padding issue. – TLCONE Jan 30 '18 at 10:26
  • @TLCONE That's because at least one method is wrong. Again, we cannot help you if we have no idea what your code does, you only posted the names of the methods – Camilo Terevinto Jan 30 '18 at 10:30
  • Apologies , I have edited the question to include the referenced code. – TLCONE Jan 30 '18 at 10:52
  • Creating an MCVE would mean stripping all unnecessary code including the ones performing password based encryption. Sorry, I thought I saw an answer but I was wrong. I didn't expect that you would regenerate the stream all the time, your stream handling requires large buffers; that should not be necessary. – Maarten Bodewes Jan 30 '18 at 11:37
  • Good Point, I have cleared them out. I was reading online about how flushing the CryptoStream was the fix for similar issues however the issue persists for me whether I flush the stream post writing or not. – TLCONE Jan 30 '18 at 12:13

0 Answers0