/// <summary>
            /// Initializes a new instance of the SessionKey class.
            /// </summary>
            /// <param name="keyData">Delegate that procures key data on demand, cannot be null.</param>
            /// <exception cref="ArgumentNullException">The keyData argument is null.</exception>
            /// <exception cref="CryptographicException">The encryption key could not be created, probably due to malformed key data.</exception>
            public SessionKey(Func <string> keyData)
                : this()
            {
                // omitting arg validation since this type in private
                this.keyData = keyData;

                this.algorithm = AsymmetricDataEncryptor.InstantiateAlgorithm(this.keyData);
            }
        /// <summary>
        /// Decrypts a cipher into the original data.
        /// </summary>
        /// <param name="algorithm">The algorithm to use for decryption, cannot be null.</param>
        /// <param name="cipher">The cipher to decrypt, cannot be null but can be empty.</param>
        /// <returns>The resulting data, cannot be null but can be empty.</returns>
        /// <exception cref="ArgumentNullException">The key or cipher argument is null.</exception>
        /// <exception cref="CryptographicException">The encryption operation failed, probably due to malformed key or key mismatch.</exception>
        private static byte[] Decrypt(RSACryptoServiceProvider algorithm, byte[] cipher)
        {
            // omitting argument validation in private methods
            // KeySize is in bits, array lengths count bytes
            int blockSize = algorithm.KeySize / 8;

            // Concatenate the result of breaking the data into chunks that are individually decrypted
            // calling 'ToArray' to avoid decrypting twice
            return(AsymmetricDataEncryptor.Concatenate(AsymmetricDataEncryptor.Break(blockSize, cipher).Select(array => algorithm.Decrypt(array, true)).ToArray()));
        }
        /// <summary>
        /// Decrypts a cipher into the original data.
        /// </summary>
        /// <param name="key">The key to the data to be used as entropy, cannot be null but can be empty.</param>
        /// <param name="cipher">The cipher to decrypt, cannot be null but can be empty.</param>
        /// <returns>The resulting data, cannot be null but can be empty.</returns>
        /// <exception cref="ArgumentNullException">The key or cipher argument is null.</exception>
        /// <exception cref="CryptographicException">The encryption operation failed, probably due to malformed key or key mismatch.</exception>
        public byte[] Decrypt(string key, byte[] cipher)
        {
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }

            if (cipher == null)
            {
                throw new ArgumentNullException("cipher", string.Format(CultureInfo.InvariantCulture, "Object cannot be null for key {0}", key));
            }

            using (RSACryptoServiceProvider algorithm = AsymmetricDataEncryptor.InstantiateAlgorithm(this.keyData))
            {
                return(AsymmetricDataEncryptor.Decrypt(algorithm, cipher));
            }
        }
            /// <summary>
            /// Decrypts a cipher into the original data.
            /// </summary>
            /// <param name="key">The key to the data to be used as entropy, cannot be null but can be empty.</param>
            /// <param name="cipher">The cipher to decrypt, cannot be null but can be empty.</param>
            /// <returns>The resulting data, cannot be null but can be empty.</returns>
            /// <exception cref="ArgumentNullException">The key or cipher argument is null.</exception>
            /// <exception cref="CryptographicException">The encryption operation failed, probably due to malformed key or key mismatch.</exception>
            public byte[] Decrypt(string key, byte[] cipher)
            {
                if (key == null)
                {
                    throw new ArgumentNullException("key");
                }

                if (cipher == null)
                {
                    throw new ArgumentNullException("cipher", string.Format(CultureInfo.InvariantCulture, "Object cannot be null for key {0}", key));
                }

                if (this.disposed)
                {
                    throw new ObjectDisposedException("This instance has been disposed.");
                }

                return(AsymmetricDataEncryptor.Decrypt(this.algorithm, cipher));
            }
        /// <summary>
        /// This method constructs an Asymmetric data encryptor from the public key of a certificate found in the certificate store.
        /// Use this method when you strictly intend to encrypt data.
        /// </summary>
        /// <param name="storeLocation">The store location.</param>
        /// <param name="storeName">The store name.</param>
        /// <param name="certificateSelector">The certificate predicate selector, cannot be null.</param>
        /// <exception cref="ArgumentException">The storeLocation or storeName argument is not properly defined or the certificateName argument is null or empty or white spaces.</exception>
        /// <exception cref="InvalidOperationException">The certificate could not be found or loaded.</exception>
        /// <exception cref="SecurityException">The calling context is denied access to the certificate store.</exception>
        /// <exception cref="CryptographicException">The encryption operation failed.</exception>
        /// <returns>The asymmetric key, cannot be null.</returns>
        public static AsymmetricDataEncryptor LoadCertificatePublicKey(StoreLocation storeLocation, StoreName storeName, Predicate <X509Certificate2> certificateSelector)
        {
            if (!Enum.IsDefined(typeof(StoreLocation), storeLocation))
            {
                throw new ArgumentOutOfRangeException("storeLocation");
            }

            if (!Enum.IsDefined(typeof(StoreName), storeName))
            {
                throw new ArgumentOutOfRangeException("storeName");
            }

            if (certificateSelector == null)
            {
                throw new ArgumentNullException("certificateSelector");
            }

            return(AsymmetricDataEncryptor.LoadCertificateKey(storeLocation, storeName, certificateSelector, certificate => certificate.PublicKey.Key.ToXmlString(false)));
        }
        /// <summary>
        /// Creates an AsymmetricDataEncryptor instance with a new RSA key.
        /// </summary>
        /// <returns>The asymmetric key, cannot be null.</returns>
        public static AsymmetricDataEncryptor GenerateRandomEncryptor()
        {
            // capture keydata once so that multiple calls to encrypt/decrypt for a single instance of AsymmetricDataEncryptor always behaves the same
            // but keep the key under DPAPI encryption to protect from crash dump attacks
            IStringEncryptor encryptor     = StringEncryptor.Create(CurrentUserDataEncryptor.Instance);
            string           keyDataCipher = encryptor.Encrypt(AsymmetricDataEncryptor.InMemoryKeyName, AsymmetricDataEncryptor.GenerateKey());

            return(new AsymmetricDataEncryptor(() => encryptor.Decrypt(AsymmetricDataEncryptor.InMemoryKeyName, keyDataCipher)));
        }