Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encrypt string with Bouncy Castle AES/CBC/PKCS7

I have been looking everywhere for some sample code on how to encrypt a simple string with the encryption in the title using the Bouncy Castle Framework.

This code will run on a Windows Universal project. My previous attempts to encrypt using the build in API's failed to decrypt on the server.

I tried this: which gives me a string like:

4pQUfomwVVsl68oQqWoWYNRmRM+Cp+vNFXBNdkN6dZPQ34VZ35vsKn9Q7QGTDVOj+w5mqVYHnGuAOFOgdgl8kA==

s = String.Format("{0}_{1}", s, DateTime.Now.ToString("ddMMyyyyHmmss")); SymmetricKeyAlgorithmProvider algorithm = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); IBuffer keymaterial = CryptographicBuffer.ConvertStringToBinary("[Key]", BinaryStringEncoding.Utf8); CryptographicKey KEY = algorithm.CreateSymmetricKey(keymaterial); IBuffer IV = CryptographicBuffer.ConvertStringToBinary("[IV]", BinaryStringEncoding.Utf8); IBuffer data = CryptographicBuffer.ConvertStringToBinary(s, BinaryStringEncoding.Utf8); IBuffer output = CryptographicEngine.Encrypt(KEY, data, IV); return CryptographicBuffer.EncodeToBase64String(output); 

The server does encryption/decryption with

public static string Encrypt(string text, byte[] key, byte[] iv, int keysize = 128, int blocksize = 128, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7) {     AesCryptoServiceProvider aes = new AesCryptoServiceProvider();     aes.BlockSize = blocksize;     aes.KeySize = keysize;     aes.Mode = cipher;     aes.Padding = padding;      byte[] src = Encoding.UTF8.GetBytes(text);     using (ICryptoTransform encrypt = aes.CreateEncryptor(key, iv))     {         byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);         encrypt.Dispose();         return Convert.ToBase64String(dest);     } }  public static string Decrypt(string text, byte[] key, byte[] iv, int keysize = 128, int blocksize = 128, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7) {     AesCryptoServiceProvider aes = new AesCryptoServiceProvider();     aes.BlockSize = blocksize;     aes.KeySize = keysize;     aes.Mode = cipher;     aes.Padding = padding;      byte[] src = Convert.FromBase64String(text);     using (ICryptoTransform decrypt = aes.CreateDecryptor(key, iv))     {         byte[] dest = decrypt.TransformFinalBlock(src, 0, src.Length);         decrypt.Dispose();         return Encoding.UTF8.GetString(dest); //Padding is invalid and cannot be removed.      } } 

But it fails becasue:

Padding is invalid and cannot be removed.

That's why I want to try Bouncy Castle, but I can't find any suitable example code.

EDIT

I tried using Bouncy Castle with the code provided in the answer. Now I'm getting the error:

initialisation vector must be the same length as block size

byte[] inputBytes = Encoding.UTF8.GetBytes(s); byte[] IV = Encoding.UTF8.GetBytes("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); byte[] key = Encoding.UTF8.GetBytes("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");  //Set up AesEngine engine = new AesEngine(); CbcBlockCipher blockCipher = new CbcBlockCipher(engine); PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding()); Debug.WriteLine(IV.Length); //32 Debug.WriteLine(cipher.GetBlockSize()); //16 KeyParameter keyParam = new KeyParameter(key); ParametersWithIV keyParamWithIv = new ParametersWithIV(keyParam, IV);   cipher.Init(true, keyParamWithIv); //Error Message thrown byte[] outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)]; //cip int length = cipher.ProcessBytes(inputBytes, outputBytes, 0); cipher.DoFinal(outputBytes, length); //Do the final block string encryptedInput = Convert.ToBase64String(outputBytes); 

The length on the server is 128. How can I force it to be equal and same length?

like image 256
timr Avatar asked Apr 17 '15 14:04

timr


People also ask

Can AES work in CBC mode?

The Cipher Block Chaining (CBC) mode is a typical block cipher mode of operation using block cipher algorithm. In this version, we provide Data Encryption Standard (DES) and Advanced Encryption Standard (AES) processing ability, the cipherkey length for DES should be 64 bits, and 128/192/256 bits for AES.

How encrypt AES-CBC?

To encrypt using AES-CBC: Instantiate the CBC block cipher class with the AES implementation class. Initialize it with the key and Initialization Vector (IV) for encryption. Process each block of the padded plaintext being encrypted.

How do you use PKCS 7 padding?

When you specify PKCS7, BC will add the padding to the data before encrypting, and remove it again when decrypting. PKCS7 with AES would always add at least 1 byte of padding, and will add enough data to make the input a multiple of the AES block size.

Is AES 256 CBC secure?

The AES-GCM mode of operation can actually be carried out in parallel both for encryption and decryption. The additional security that this method provides also allows the VPN to use only a 128-bit key, whereas AES-CBC typically requires a 256-bit key to be considered secure. CBC ciphers were removed in May of 2021.


1 Answers

Here are snippets I use. It uses the default built-in System.Security.Cryptography. It doesn't need to be BC

    /// <summary>     /// Encrypt a byte array using AES 128     /// </summary>     /// <param name="key">128 bit key</param>     /// <param name="secret">byte array that need to be encrypted</param>     /// <returns>Encrypted array</returns>     public static byte[] EncryptByteArray(byte[] key, byte[] secret)     {         using (MemoryStream ms = new MemoryStream())         {             using (AesManaged cryptor = new AesManaged())             {                 cryptor.Mode = CipherMode.CBC;                 cryptor.Padding = PaddingMode.PKCS7;                 cryptor.KeySize = 128;                 cryptor.BlockSize = 128;                  //We use the random generated iv created by AesManaged                 byte[] iv = cryptor.IV;                  using (CryptoStream cs = new CryptoStream(ms, cryptor.CreateEncryptor(key, iv), CryptoStreamMode.Write))                 {                     cs.Write(secret, 0, secret.Length);                 }                 byte[] encryptedContent = ms.ToArray();                  //Create new byte array that should contain both unencrypted iv and encrypted data                 byte[] result = new byte[iv.Length + encryptedContent.Length];                  //copy our 2 array into one                 System.Buffer.BlockCopy(iv, 0, result, 0, iv.Length);                 System.Buffer.BlockCopy(encryptedContent, 0, result, iv.Length, encryptedContent.Length);                  return result;             }         }     }      /// <summary>     /// Decrypt a byte array using AES 128     /// </summary>     /// <param name="key">key in bytes</param>     /// <param name="secret">the encrypted bytes</param>     /// <returns>decrypted bytes</returns>     public static byte[] DecryptByteArray(byte[] key, byte[] secret)     {         byte[] iv = new byte[16]; //initial vector is 16 bytes         byte[] encryptedContent = new byte[secret.Length - 16]; //the rest should be encryptedcontent          //Copy data to byte array         System.Buffer.BlockCopy(secret, 0, iv, 0, iv.Length);         System.Buffer.BlockCopy(secret, iv.Length, encryptedContent, 0, encryptedContent.Length);          using (MemoryStream ms = new MemoryStream())         {             using (AesManaged cryptor = new AesManaged())             {                 cryptor.Mode = CipherMode.CBC;                 cryptor.Padding = PaddingMode.PKCS7;                 cryptor.KeySize = 128;                 cryptor.BlockSize = 128;                  using (CryptoStream cs = new CryptoStream(ms, cryptor.CreateDecryptor(key, iv), CryptoStreamMode.Write))                 {                     cs.Write(encryptedContent, 0, encryptedContent.Length);                  }                 return ms.ToArray();             }         }     } 

If you really need BC, here is a quick test I manage to write based on the test suit from https://github.com/bcgit/bc-csharp/blob/master/crypto/test/src/crypto/test/AESFastTest.cs You can tailor it for your need

    private static void TestBC()     {         //Demo params         string keyString = "jDxESdRrcYKmSZi7IOW4lw==";             string input = "abc";         byte[] inputBytes = Encoding.UTF8.GetBytes(input);                     byte[] iv = new byte[16]; //for the sake of demo          //Set up         AesEngine engine = new AesEngine();         CbcBlockCipher blockCipher = new CbcBlockCipher(engine); //CBC         PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(blockCipher); //Default scheme is PKCS5/PKCS7         KeyParameter keyParam = new KeyParameter(Convert.FromBase64String(keyString));         ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, iv, 0, 16);          // Encrypt         cipher.Init(true, keyParamWithIV);         byte[] outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)];         int length = cipher.ProcessBytes(inputBytes, outputBytes, 0);         cipher.DoFinal(outputBytes, length); //Do the final block         string encryptedInput = Convert.ToBase64String(outputBytes);          Console.WriteLine("Encrypted string: {0}", encryptedInput);          //Decrypt                     cipher.Init(false, keyParamWithIV);         byte[] comparisonBytes = new byte[cipher.GetOutputSize(outputBytes.Length)];         length = cipher.ProcessBytes(outputBytes, comparisonBytes, 0);         cipher.DoFinal(comparisonBytes, length); //Do the final block          Console.WriteLine("Decrypted string: {0}",Encoding.UTF8.GetString(comparisonBytes)); //Should be abc     } 
like image 63
Eledra Nguyen Avatar answered Oct 14 '22 12:10

Eledra Nguyen