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.
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?
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.
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.
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.
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.
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 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With