// Performance testing resources (not called in this base class, but called from derived classes)

        protected void RunPerformanceTest(CipherConfiguration config, byte[] overrideKey = null)
        {
            MemoryStream msInputPlaintext = LargeBinaryFile;

            byte[] key = overrideKey ?? CreateRandomByteArray(config.KeySizeBits);

            var msCiphertext = new MemoryStream((int)(msInputPlaintext.Length * 1.1));
            var sw           = new Stopwatch();

            // TEST STARTS HERE

            using (var cs = new CipherStream(msCiphertext, true, config, key, false)) {
                sw.Start();
                msInputPlaintext.CopyTo(cs, GetBufferSize());
            }
            sw.Stop();
            TimeSpan encryptionElapsed = sw.Elapsed;

            var msOutputPlaintext = new MemoryStream((int)msInputPlaintext.Length);

            msCiphertext.Seek(0, SeekOrigin.Begin);

            sw.Reset();
            using (var cs = new CipherStream(msCiphertext, false, config, key, false)) {
                sw.Start();
                cs.CopyTo(msOutputPlaintext, GetBufferSize());
            }
            sw.Stop();
            TimeSpan decryptionElapsed = sw.Elapsed;

            // TEST ENDS HERE

            // TEST OUTPUT PLAINTEXT VALIDITY

            msInputPlaintext.Seek(0, SeekOrigin.Begin);
            msOutputPlaintext.Seek(0, SeekOrigin.Begin);
            int failurePosition;

            Assert.IsTrue(StreamsContentMatches(msInputPlaintext, msOutputPlaintext, (int)msInputPlaintext.Length, out failurePosition),
                          "Input and output plaintext does not match. First failure observed at position # " + failurePosition);

            // OUTPUT SUCCESS STATISTICS

            double encSpeed = ((double)msInputPlaintext.Length / 1048576) / encryptionElapsed.TotalSeconds,
                   decSpeed =
                ((double)msInputPlaintext.Length / 1048576) / decryptionElapsed.TotalSeconds;

            Assert.Pass("{0:N0} ms ({1:N2} MB/s) : {2:N0} ms ({3:N2} MB/s)",
                        encryptionElapsed.TotalMilliseconds, encSpeed, decryptionElapsed.TotalMilliseconds, decSpeed);
        }
        private static void decrypt(String privateKeyPath,
                                    String encryptedCEK,
                                    String iv,
                                    String encryptedRecordingPath,
                                    String decryptedRecordingPath)
        {
            // 2) Retrieve customer private key corresponding to public_key_sid and use it to decrypt base 64 decoded
            // encrypted_cek via RSAES-OAEP-SHA256-MGF1
            Object pemObject;

            using (var txtreader = File.OpenText(@privateKeyPath))
                pemObject = new PemReader(txtreader).ReadObject();

            var privateKey = (RsaPrivateCrtKeyParameters)((pemObject.GetType() == typeof(AsymmetricCipherKeyPair)) ?
                                                          ((AsymmetricCipherKeyPair)pemObject).Private : pemObject);

            var rsaDecryptEngine = CipherUtilities.GetCipher("RSA/ECB/OAEPWITHSHA256ANDMGF1PADDING");

            rsaDecryptEngine.Init(false, privateKey);
            var encryptedCekArr = Convert.FromBase64String(encryptedCEK);
            var decryptedCekArr = rsaDecryptEngine.DoFinal(encryptedCekArr);

            // 3) Initialize a AES256-GCM SecretKey object with decrypted CEK and base 64 decoded iv
            var               aesDecryptEngine = CipherUtilities.GetCipher("AES/GCM/NOPADDING");
            KeyParameter      keyParameter     = ParameterUtilities.CreateKeyParameter("AES", decryptedCekArr);
            ICipherParameters cipherParameters = new ParametersWithIV(keyParameter, Convert.FromBase64String(iv));

            aesDecryptEngine.Init(false, cipherParameters);

            // 4) Decrypt encrypted recording using the SecretKey
            var          decryptedFile = File.Create(@decryptedRecordingPath);
            CipherStream cipherStream  = new CipherStream(File.OpenRead(@encryptedRecordingPath), aesDecryptEngine, null);

            cipherStream.CopyTo(decryptedFile);
            decryptedFile.Close();
        }