Exemplo n.º 1
0
 /// <summary>
 /// Verifies request arguments against a signature using a shared <see cref="SymmetricKey" />
 /// and the current time.
 /// </summary>
 /// <param name="sharedKey">The shared <see cref="SymmetricKey" />.</param>
 /// <param name="signature">The base-64 encoded signature.</param>
 /// <param name="args">The request argument collection.</param>
 /// <param name="signatureKey">
 /// The name of the signature key within the event arguments or <c>null</c>
 /// if the signature is not present.
 /// </param>
 /// <param name="graceInterval">Specifies the time period used for verifying the signature request time.</param>
 /// <exception cref="SecurityException">Thrown if the signature could not be verified.</exception>
 /// <remarks>
 /// <para>
 /// <paramref name="graceInterval" /> is used when comparing the request time (UTC) embedded
 /// in the signature with the current machine time (UTC).  Request times within the range of:
 /// </para>
 /// <code language="none">
 /// DateTime.UtcNow - graceInterval &lt;= requestTime &lt;= DateTime.UtcNow + graceInterval
 /// </code>
 /// <para>
 /// will be considered to be valid.  Request times outside this range will be invalid.
 /// Larger grace intervals provide help avoid the problems of incorrect system times or
 /// long request delivery delays but at the price of the increased exposure to replay
 /// attacks.
 /// </para>
 /// <note>
 /// If the signature is present in the arguments, then the <paramref name="signatureKey" /> <b>must</b>
 /// be passed as its name or else the verification will always fail.  The reason for this is
 /// that the hash was originally computed before the signature was added so the hash will be
 /// different if it includes the signature.
 /// </note>
 /// </remarks>
 public static void Verify(SymmetricKey sharedKey, string signature, ArgCollection args, string signatureKey, TimeSpan graceInterval)
 {
     if (!TryVerify(sharedKey, signature, args, signatureKey, graceInterval))
     {
         throw new SecurityException("Access denied.");
     }
 }
Exemplo n.º 2
0
        /// <summary>
        /// Extends <see cref="ArgCollection" /> by adding a method to retrieve an encrypted string value.
        /// </summary>
        /// <param name="args">The current collection.</param>
        /// <param name="name">The argument name.</param>
        /// <param name="key">The symmetric encryption key.</param>
        /// <returns>The decrypted value.</returns>
        public static string GetEncrypted(this ArgCollection args, string name, SymmetricKey key)
        {
            var encrypted = args.Get(name, (byte[])null);

            if (encrypted == null)
            {
                return(null);
            }

            return(Crypto.DecryptStringWithSalt8(encrypted, key));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Generates a request signature using a shared <see cref="SymmetricKey" />
        /// for request arguments as well as the current time.
        /// </summary>
        /// <param name="sharedKey">The shared <see cref="SymmetricKey" />.</param>
        /// <param name="args">The request argument collection.</param>
        /// <returns>The base-64 encoded signature.</returns>
        public static string Generate(SymmetricKey sharedKey, ArgCollection args)
        {
            using (var ms = new EnhancedMemoryStream(512))
            {
                ms.WriteBytesNoLen(Crypto.GetSalt8());
                ms.WriteInt32(Magic);
                ms.WriteInt64(DateTime.UtcNow.Ticks);
                ms.WriteBytesNoLen(ComputeHash(args, null));

                return(Convert.ToBase64String(Crypto.Encrypt(ms.ToArray(), sharedKey)));
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Encrypts a byte array using a combination of an asymmetric RSA key and the
        /// specified symmetric encryption algorithm and a one-time key generated by
        /// the method.
        /// </summary>
        /// <param name="rsaKey">The encrypting RSA key as XML or as a secure key container name.</param>
        /// <param name="plainText">The data to be encrypted.</param>
        /// <param name="algorithm">The symmetric encryption algorithm name.</param>
        /// <param name="keySize">The one-time symmetric key size to generate in bits.</param>
        /// <param name="paddedSize">Specifies the minimum padded size of the encrypted content.</param>
        /// <returns>The encrypted result.</returns>
        /// <remarks>
        /// <para>
        /// The current supported cross platform encryption algorithms
        /// are: "DES", "RC2", "TripleDES", and "AES" (Rijndael).
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentException">Thrown if the requested encryption algorithm is unknown.</exception>
        public static byte[] Encrypt(string rsaKey, byte[] plainText, string algorithm, int keySize, int paddedSize)
        {
            SymmetricKey symmetricKey = null;

            try
            {
                return(Encrypt(rsaKey, plainText, algorithm, keySize, paddedSize, out symmetricKey));
            }
            finally
            {
                if (symmetricKey != null)
                {
                    symmetricKey.Dispose();
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Decrypts a byte array encrypted using <see cref="Encrypt(string,byte[],string,int,int,out SymmetricKey)" />.
        /// </summary>
        /// <param name="rsaKey">The decrypting RSA key as XML or as a secure key container name.</param>
        /// <param name="cipherText">The encrypted data.</param>
        /// <returns>The decrypted data.</returns>
        /// <exception cref="CryptographicException">Thrown is the encrypted data block is incorrectly formatted.</exception>
        public static byte[] Decrypt(string rsaKey, byte[] cipherText)
        {
            SymmetricKey symmetricKey = null;

            try
            {
                return(Decrypt(rsaKey, cipherText, out symmetricKey));
            }
            finally
            {
                if (symmetricKey != null)
                {
                    symmetricKey.Dispose();
                }
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Decrypts data encrypted using <see cref="Encrypt(SymmetricKey,byte[],int)" />.
        /// </summary>
        /// <param name="symmetricKey">The symmetric algorithm arguments.</param>
        /// <param name="cipherText">The encrypted data.</param>
        /// <returns>The decrypted result.</returns>
        public static byte[] Decrypt(SymmetricKey symmetricKey, byte[] cipherText)
        {
            EnhancedMemoryStream input     = new EnhancedMemoryStream(cipherText);
            EnhancedMemoryStream ms        = new EnhancedMemoryStream(cipherText.Length);
            BlockDecryptor       decryptor = null;

            try
            {
                // Read the header fields

                if (input.ReadInt32() != Magic)
                {
                    throw new CryptographicException(BadFormatMsg);
                }

                if (input.ReadInt32() != 0)
                {
                    throw new CryptographicException("Unsupported secure data format version.");
                }

                decryptor = new BlockDecryptor(symmetricKey);

                // Decrypt the contents

                ms.WriteBytesNoLen(decryptor.Decrypt(input.ReadBytes32()));
                ms.Position = 0;

                if (ms.ReadInt32() != Magic)
                {
                    throw new CryptographicException("Secure data content is corrupt.");
                }

                ms.Position += 8;   // Skip over the salt

                return(ms.ReadBytes32());
            }
            finally
            {
                if (decryptor != null)
                {
                    decryptor.Dispose();
                }

                input.Close();
                ms.Close();
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Encrypts the keys using the symmetric key passed.
        /// </summary>
        /// <returns>The encrypted key chain.</returns>
        public byte[] Encrypt(SymmetricKey key)
        {
            using (var ms = new EnhancedMemoryStream())
            {
                ms.WriteInt32(Magic);
                ms.WriteInt32(keys.Count);

                foreach (string privateKey in keys.Values)
                {
                    ms.WriteString16(privateKey);
                }

                ms.WriteBytesNoLen(Crypto.GetSalt8());

                return(Crypto.Encrypt(ms.ToArray(), key));
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Used by an issuer to construct a ticket by decrypting one from a byte array.
        /// </summary>
        /// <param name="key">The symmetric encryption key.</param>
        /// <param name="encrypted">The encrypted ticket.</param>
        /// <exception cref="CryptographicException">Thrown if the ticket is improperly formatted or has been tampered with.</exception>
        /// <remarks>
        /// The <see cref="ClientExpirationUtc" /> property will be computed by adding
        /// <see cref="Lifespan" /> to the local UTC time.
        /// </remarks>
        public SecureTicket(SymmetricKey key, byte[] encrypted)
        {
            try
            {
                int pos;

                pos       = 0;
                encrypted = Helper.ReadBytes16(encrypted, ref pos);
                args      = ArgCollection.Parse(Crypto.DecryptStringWithSalt8(encrypted, key), '=', '\t');

                clientExpirationUtc = DateTime.UtcNow + this.Lifespan;
            }
            catch (Exception e)
            {
                throw new CryptographicException("Invalid ticket", e);
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Verifies request arguments against a signature using a shared <see cref="SymmetricKey" />
        /// and the current time.
        /// </summary>
        /// <param name="sharedKey">The shared <see cref="SymmetricKey" />.</param>
        /// <param name="signature">The base-64 encoded signature.</param>
        /// <param name="args">The request argument collection.</param>
        /// <param name="signatureKey">
        /// The name of the signature key within the event arguments or <c>null</c>
        /// if the signature is not present.
        /// </param>
        /// <param name="graceInterval">Specifies the time period used for verifying the signature request time.</param>
        /// <returns><c>true</c> if the signature is valid.</returns>
        /// <remarks>
        /// <para>
        /// <paramref name="graceInterval" /> is used when comparing the request time (UTC) embedded
        /// in the signature with the current machine time (UTC).  Request times within the range of:
        /// </para>
        /// <code language="none">
        /// DateTime.UtcNow - graceInterval &lt;= requestTime &lt;= DateTime.UtcNow + graceInterval
        /// </code>
        /// <para>
        /// will be considered to be valid.  Request times outside this range will be invalid.
        /// Larger grace intervals provide help avoid the problems of incorrect system times or
        /// long request delivery delays but at the price of the increased exposure to replay
        /// attacks.
        /// </para>
        /// <note>
        /// If the signature is present in the arguments, then the <paramref name="signatureKey" /> <b>must</b>
        /// be passed as its name or else the verification will always fail.  The reason for this is
        /// that the hash was originally computed before the signature was added so the hash will be
        /// different if it includes the signature.
        /// </note>
        /// </remarks>
        public static bool TryVerify(SymmetricKey sharedKey, string signature, ArgCollection args, string signatureKey, TimeSpan graceInterval)
        {
            try
            {
                byte[]   encrypted = Convert.FromBase64String(signature);
                byte[]   decrypted;
                byte[]   requestHash;
                byte[]   hash;
                DateTime requestTime;
                DateTime now;

                decrypted = Crypto.Decrypt(encrypted, sharedKey);
                if (decrypted.Length != Size)
                {
                    return(false);
                }

                using (var ms = new EnhancedMemoryStream(decrypted))
                {
                    ms.Position = 8;
                    if (ms.ReadInt32() != Magic)
                    {
                        return(false);
                    }

                    requestTime = new DateTime(ms.ReadInt64());
                    requestHash = ms.ReadBytes(SHA1Hasher.DigestSize);
                }

                now = DateTime.UtcNow;
                if (!Helper.Within(requestTime, now, graceInterval))
                {
                    return(false);
                }

                hash = ComputeHash(args, signatureKey);
                return(Helper.ArrayEquals(requestHash, hash));
            }
            catch
            {
                return(false);
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Returns a deep clone of the key.
        /// </summary>
        /// <returns>The cloned <see cref="SymmetricKey" />.</returns>
        /// <remarks>
        /// This method is useful in situations where a copy of a key needs
        /// to be made before the original key is disposed and its
        /// <see cref="Key" /> and <see cref="IV" /> properties are zeroed.
        /// </remarks>
        public SymmetricKey Clone()
        {
            var clone = new SymmetricKey();

            clone.Algorithm = this.Algorithm;

            if (this.Key != null)
            {
                clone.Key = new byte[this.Key.Length];
                Array.Copy(this.Key, clone.Key, this.Key.Length);
            }

            if (this.IV != null)
            {
                clone.IV = new byte[this.IV.Length];
                Array.Copy(this.IV, clone.IV, this.IV.Length);
            }

            return(clone);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Encrypts the ticket using a symmetric key.
        /// </summary>
        /// <param name="key">The symmetric encryption key.</param>
        /// <returns>The encrypted ticket.</returns>
        public byte[] ToArray(SymmetricKey key)
        {
            byte[]        privateData = Crypto.EncryptStringWithSalt8(args.ToString(), key);
            ArgCollection publicArgs  = new ArgCollection('=', '\t');

            byte[] publicData;
            byte[] encrypted;
            int    pos;

            publicArgs.Set("_resource", this.Resource);
            publicArgs.Set("_lifespan", this.Lifespan);
            publicData = Helper.ToUTF8(publicArgs.ToString());

            encrypted = new byte[2 + privateData.Length + 2 + publicData.Length];

            pos = 0;
            Helper.WriteBytes16(encrypted, ref pos, privateData);
            Helper.WriteBytes16(encrypted, ref pos, publicData);

            return(encrypted);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Performs a secure symmetric encryption including cryptographic salt, padding, and
        /// data validation.
        /// </summary>
        /// <param name="symmetricKey">The symmetric algorithm arguments.</param>
        /// <param name="plainText">The unencrypted data.</param>
        /// <param name="paddedSize">Specifies the minimum padded size of the encrypted content.</param>
        /// <returns>The encrypted result.</returns>
        public static byte[] Encrypt(SymmetricKey symmetricKey, byte[] plainText, int paddedSize)
        {
            EnhancedMemoryStream output    = new EnhancedMemoryStream(Math.Max(plainText.Length, paddedSize) + 512);
            EnhancedMemoryStream ms        = new EnhancedMemoryStream(512);
            BlockEncryptor       encryptor = new BlockEncryptor(symmetricKey);

            try
            {
                // Write header fields

                output.WriteInt32(Magic);
                output.WriteInt32(0);

                // Write encrypted contents

                ms.WriteInt32(Magic);
                ms.WriteBytesNoLen(Crypto.GetSalt8());
                ms.WriteBytes32(plainText);

                for (int i = plainText.Length; i < paddedSize; i++)
                {
                    ms.WriteByte((byte)i);     // Padding bytes
                }
                output.WriteBytes32(encryptor.Encrypt(ms.ToArray()));

                // That's it, we're done.

                return(output.ToArray());
            }
            finally
            {
                if (encryptor != null)
                {
                    encryptor.Dispose();
                }

                output.Close();
                ms.Close();
            }
        }
Exemplo n.º 13
0
 /// <summary>
 /// Initializes the encryptor to use the specified
 /// <see cref="SymmetricKey" />.
 /// </summary>
 /// <param name="symmetricKey">The symmetric key.</param>
 public BlockDecryptor(SymmetricKey symmetricKey)
     : this(symmetricKey.Algorithm, symmetricKey.Key, symmetricKey.IV)
 {
 }
Exemplo n.º 14
0
 /// <summary>
 /// Encrypts the ticket using a combination of a symmetric key.
 /// </summary>
 /// <param name="key">The symmetric encryption key.</param>
 /// <returns>The base-64 encoded string of the encrypted ticket.</returns>
 public string ToBase64String(SymmetricKey key)
 {
     return(Convert.ToBase64String(ToArray(key)));
 }
Exemplo n.º 15
0
 /// <summary>
 /// Used by an issuer to construct a ticket by decrypting one from a base 64 encoded byte array.
 /// </summary>
 /// <param name="key">The symmetric encryption key.</param>
 /// <param name="base64Encrypted">The base 64 encoded encrypted ticket.</param>
 /// <exception cref="CryptographicException">Thrown if the ticket is improperly formatted or has been tampered with.</exception>
 public SecureTicket(SymmetricKey key, string base64Encrypted)
     : this(key, Convert.FromBase64String(base64Encrypted))
 {
 }
Exemplo n.º 16
0
 /// <summary>
 /// Encrypts a byte array using the specified <see cref="SymmetricKey" />.
 /// </summary>
 /// <param name="input">The clear text array.</param>
 /// <param name="symmetricKey">The symmetric key.</param>
 /// <returns>The encrypted output.</returns>
 public static byte[] Encrypt(byte[] input, SymmetricKey symmetricKey)
 {
     return(Encrypt(input, symmetricKey.Algorithm, symmetricKey.Key, symmetricKey.IV));
 }
Exemplo n.º 17
0
 /// <summary>
 /// Used by issues to decrypt and parse a ticket from a base-64 encoded byte array.
 /// </summary>
 /// <param name="key">The symmetric encryption key.</param>
 /// <param name="base64Encrypted">The base 64 encoded encrypted ticket.</param>
 /// <returns>The decrypted ticket.</returns>
 /// <exception cref="CryptographicException">Thrown if the ticket is improperly formatted or has been tampered with.</exception>
 public static SecureTicket Parse(SymmetricKey key, string base64Encrypted)
 {
     return(new SecureTicket(key, base64Encrypted));
 }
Exemplo n.º 18
0
        //---------------------------------------------------------------------
        // Static members

        /// <summary>
        /// Used by issuers to decrypt and parse a ticket from a byte array.
        /// </summary>
        /// <param name="key">The symmetric encryption key.</param>
        /// <param name="encrypted">The encrypted ticket.</param>
        /// <returns>The decrypted ticket.</returns>
        /// <exception cref="CryptographicException">Thrown if the ticket is improperly formatted or has been tampered with.</exception>
        public static SecureTicket Parse(SymmetricKey key, byte[] encrypted)
        {
            return(new SecureTicket(key, encrypted));
        }
Exemplo n.º 19
0
 /// <summary>
 /// Encrypts a byte array with four bytes of cryptogrpahic salt
 /// using the specified <see cref="SymmetricKey" />.
 /// </summary>
 /// <param name="input">The encrypted array.</param>
 /// <param name="symmetricKey">The symmetric key.</param>
 /// <returns>The encrypted output.</returns>
 public static byte[] DecryptWithSalt4(byte[] input, SymmetricKey symmetricKey)
 {
     return(DecryptWithSalt4(input, symmetricKey.Algorithm, symmetricKey.Key, symmetricKey.IV));
 }
Exemplo n.º 20
0
 /// <summary>
 /// Extends <see cref="ArgCollection" /> by adding a method to set an encrypted string value.
 /// </summary>
 /// <param name="args">The current collection.</param>
 /// <param name="name">The argument name.</param>
 /// <param name="value">The plain text value.</param>
 /// <param name="key">The symmetric encryption key.</param>
 public static void SetEncrypted(this ArgCollection args, string name, string value, SymmetricKey key)
 {
     if (value == null)
     {
         args.Set(name, (byte[])null);
     }
     else
     {
         args.Set(name, Crypto.EncryptStringWithSalt8(value, key));
     }
 }
Exemplo n.º 21
0
 /// <summary>
 /// Encrypts a string with eight bytes of cryptogrpahic salt
 /// using the specified <see cref="SymmetricKey" />.
 /// </summary>
 /// <param name="input">The clear text string.</param>
 /// <param name="symmetricKey">The symmetric key.</param>
 /// <returns>The encrypted output.</returns>
 public static byte[] EncryptStringWithSalt8(string input, SymmetricKey symmetricKey)
 {
     return(EncryptStringWithSalt8(input, symmetricKey.Algorithm, symmetricKey.Key, symmetricKey.IV));
 }
Exemplo n.º 22
0
 /// <summary>
 /// Decrypts a string with eight bytes of cryptogrpahic salt using
 /// the specified <see cref="SymmetricKey" />.
 /// </summary>
 /// <param name="input">The encrypted string.</param>
 /// <param name="symmetricKey">The symmetric key.</param>
 /// <returns>The encrypted output.</returns>
 public static string DecryptStringWithSalt8(byte[] input, SymmetricKey symmetricKey)
 {
     return(DecryptStringWithSalt8(input, symmetricKey.Algorithm, symmetricKey.Key, symmetricKey.IV));
 }
Exemplo n.º 23
0
 /// <summary>
 /// Initializes the decryptor to use the specified
 /// <see cref="SymmetricKey" />.
 /// </summary>
 /// <param name="symmetricKey">The symmetric key.</param>
 public StreamDecryptor(SymmetricKey symmetricKey)
     : this(symmetricKey.Algorithm, symmetricKey.Key, symmetricKey.IV)
 {
 }
Exemplo n.º 24
0
 /// <summary>
 /// Static constructor.
 /// </summary>
 static SymmetricKey()
 {
     SymmetricKey.PlainText = new SymmetricKey();
 }
Exemplo n.º 25
0
        /// <summary>
        /// Encrypts a byte array using a combination of an asymmetric RSA key and the
        /// specified symmetric encryption algorithm and a one-time key generated by
        /// the method.
        /// </summary>
        /// <param name="rsaKey">The encrypting RSA key as XML or as a secure key container name.</param>
        /// <param name="plainText">The data to be encrypted.</param>
        /// <param name="algorithm">The symmetric encryption algorithm name.</param>
        /// <param name="keySize">The one-time symmetric key size to generate in bits.</param>
        /// <param name="paddedSize">Specifies the minimum padded size of the encrypted content.</param>
        /// <param name="symmetricKey">Returns as the symmetric encryption algorithm arguments.</param>
        /// <returns>The encrypted result.</returns>
        /// <remarks>
        /// <para>
        /// Note that applications should take some care to ensure that the <paramref name="symmetricKey" />
        /// value return is disposed so that the symmetric encryption key will be cleared.
        /// </para>
        /// <para>
        /// The current supported cross platform encryption algorithms
        /// are: "DES", "RC2", "TripleDES", and "AES" (Rijndael).
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentException">Thrown if the requested encryption algorithm is unknown.</exception>
        public static byte[] Encrypt(string rsaKey, byte[] plainText, string algorithm, int keySize, int paddedSize,
                                     out SymmetricKey symmetricKey)
        {
            EnhancedMemoryStream output    = new EnhancedMemoryStream(Math.Max(plainText.Length, paddedSize) + 512);
            EnhancedMemoryStream ms        = new EnhancedMemoryStream(512);
            BlockEncryptor       encryptor = null;

            byte[] symKey;
            byte[] symIV;

            Crypto.GenerateSymmetricKey(algorithm, keySize, out symKey, out symIV);

            encryptor    = new BlockEncryptor(algorithm, symKey, symIV);
            symmetricKey = new SymmetricKey(algorithm, (byte[])symKey.Clone(), (byte[])symIV.Clone());

            try
            {
                // Write header fields

                output.WriteInt32(Magic);
                output.WriteInt32(0);

                // Write encryption Info

                ms.WriteString16(algorithm);
                ms.WriteBytes16(symKey);
                ms.WriteBytes16(symIV);
                ms.WriteBytesNoLen(Crypto.GetSalt8());
                output.WriteBytes16(AsymmetricCrypto.Encrypt(CryptoAlgorithm.RSA, rsaKey, ms.ToArray()));

                // Write encrypted contents

                ms.SetLength(0);
                ms.WriteInt32(Magic);
                ms.WriteBytesNoLen(Crypto.GetSalt8());
                ms.WriteBytes32(plainText);

                for (int i = plainText.Length; i < paddedSize; i++)
                {
                    ms.WriteByte((byte)i);     // Padding bytes
                }
                output.WriteBytes32(encryptor.Encrypt(ms.ToArray()));

                // That's it, we're done.

                return(output.ToArray());
            }
            finally
            {
                if (symKey != null)
                {
                    Array.Clear(symKey, 0, symKey.Length);
                }

                if (symIV != null)
                {
                    Array.Clear(symIV, 0, symIV.Length);
                }

                if (encryptor != null)
                {
                    encryptor.Dispose();
                }

                output.Close();
                ms.Close();
            }
        }
Exemplo n.º 26
0
        /// <summary>
        /// Decrypts a byte array encrypted using <see cref="Encrypt(string ,byte[],string,int,int,out SymmetricKey)" />.
        /// </summary>
        /// <param name="rsaKey">The decrypting RSA key as XML or as a secure key container name.</param>
        /// <param name="cipherText">The encrypted data.</param>
        /// <param name="symmetricKey">Returns as the symmetric encryption algorithm arguments.</param>
        /// <returns>The decrypted data.</returns>
        /// <exception cref="CryptographicException">Thrown is the encrypted data block is incorrectly formatted.</exception>
        /// <remarks>
        /// Note that applications should take some care to ensure that the <paramref name="symmetricKey" />
        /// value return is disposed so that the symmetric encryption key will be cleared.
        /// </remarks>
        public static byte[] Decrypt(string rsaKey, byte[] cipherText, out SymmetricKey symmetricKey)
        {
            EnhancedMemoryStream input     = new EnhancedMemoryStream(cipherText);
            EnhancedMemoryStream ms        = new EnhancedMemoryStream(cipherText.Length);
            BlockDecryptor       decryptor = null;

            byte[] symKey;
            byte[] symIV;
            string algorithm;

            try
            {
                // Read the header fields

                if (input.ReadInt32() != Magic)
                {
                    throw new CryptographicException(BadFormatMsg);
                }

                if (input.ReadInt32() != 0)
                {
                    throw new CryptographicException("Unsupported secure data format version.");
                }

                // Decrypt the encryption info

                ms.WriteBytesNoLen(AsymmetricCrypto.Decrypt(CryptoAlgorithm.RSA, rsaKey, input.ReadBytes16()));
                ms.Position = 0;

                algorithm    = ms.ReadString16();
                symKey       = ms.ReadBytes16();
                symIV        = ms.ReadBytes16();
                symmetricKey = new SymmetricKey(algorithm, symKey, symIV);
                decryptor    = new BlockDecryptor(algorithm, symKey, symIV);

                // Decrypt the contents

                ms.SetLength(0);
                ms.WriteBytesNoLen(decryptor.Decrypt(input.ReadBytes32()));
                ms.Position = 0;

                if (ms.ReadInt32() != Magic)
                {
                    throw new CryptographicException("Secure data content is corrupt.");
                }

                ms.Position += 8;   // Skip over the salt

                return(ms.ReadBytes32());
            }
            finally
            {
                if (decryptor != null)
                {
                    decryptor.Dispose();
                }

                input.Close();
                ms.Close();
            }
        }