File: System\IdentityModel\CryptoHelper.cs
Project: ndp\cdf\src\WCF\IdentityModel\System.IdentityModel.csproj (System.IdentityModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.IdentityModel
{
    using System.Collections.Generic;
    using System.IdentityModel.Tokens;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Cryptography.Xml;
    using System.ServiceModel.Security;
 
    static class CryptoHelper
    {
        static byte[] emptyBuffer;
        static RandomNumberGenerator random;
        static Rijndael rijndael;
        static TripleDES tripleDES;
 
        static Dictionary<string, Func<object>> algorithmDelegateDictionary = new Dictionary<string, Func<object>>();
        static object AlgorithmDictionaryLock = new object();
        public const int WindowsVistaMajorNumber = 6;
        const string SHAString = "SHA";
        const string SHA1String = "SHA1";
        const string SHA256String = "SHA256";
        const string SystemSecurityCryptographySha1String = "System.Security.Cryptography.SHA1";
 
 
        /// <summary>
        /// The helper class which helps user to compute the combined entropy as well as the session
        /// key
        /// </summary>
        public static class KeyGenerator
        {
            static RandomNumberGenerator _random = CryptoHelper.RandomNumberGenerator;
 
            //
            // 1/(2^32) keys will be weak.  20 random keys will never happen by chance without the RNG being messed up.
            //
            const int _maxKeyIterations = 20;
            /// <summary>
            /// Computes the session key based on PSHA1 algorithm.
            /// </summary>
            /// <param name="requestorEntropy">The entropy from the requestor side.</param>
            /// <param name="issuerEntropy">The entropy from the token issuer side.</param>
            /// <param name="keySizeInBits">The desired key size in bits.</param>
            /// <returns>The computed session key.</returns>
            public static byte[] ComputeCombinedKey( byte[] requestorEntropy, byte[] issuerEntropy, int keySizeInBits )
            {
                if ( null == requestorEntropy )
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "requestorEntropy" );
                }
 
                if ( null == issuerEntropy )
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "issuerEntropy" );
                }
 
                int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
 
                byte[] key = new byte[keySizeInBytes]; // Final key
 
                // The symmetric key generation chosen is 
                // http://schemas.xmlsoap.org/ws/2005/02/trust/CK/PSHA1
                // which per the WS-Trust specification is defined as follows:
                //
                //   The key is computed using P_SHA1
                //   from the TLS specification to generate
                //   a bit stream using entropy from both
                //   sides. The exact form is:
                //
                //   key = P_SHA1 (EntREQ, EntRES)
                //
                // where P_SHA1 is defined per http://www.ietf.org/rfc/rfc2246.txt 
                // and EntREQ is the entropy supplied by the requestor and EntRES 
                // is the entrophy supplied by the issuer.
                //
                // From http://www.faqs.org/rfcs/rfc2246.html:
                // 
                // 8<------------------------------------------------------------>8
                // First, we define a data expansion function, P_hash(secret, data)
                // which uses a single hash function to expand a secret and seed 
                // into an arbitrary quantity of output:
                // 
                // P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                //                        HMAC_hash(secret, A(2) + seed) +
                //                        HMAC_hash(secret, A(3) + seed) + ...
                //
                // Where + indicates concatenation.
                //
                // A() is defined as:
                //   A(0) = seed
                //   A(i) = HMAC_hash(secret, A(i-1))
                //
                // P_hash can be iterated as many times as is necessary to produce
                // the required quantity of data. For example, if P_SHA-1 was 
                // being used to create 64 bytes of data, it would have to be 
                // iterated 4 times (through A(4)), creating 80 bytes of output 
                // data; the last 16 bytes of the final iteration would then be 
                // discarded, leaving 64 bytes of output data.
                // 8<------------------------------------------------------------>8
 
                // Note that requestorEntrophy is considered the 'secret'.
                using ( KeyedHashAlgorithm kha = CryptoHelper.NewHmacSha1KeyedHashAlgorithm() )
                {
                    kha.Key = requestorEntropy;
 
                    byte[] a = issuerEntropy; // A(0), the 'seed'.
                    byte[] b = new byte[kha.HashSize / 8 + a.Length]; // Buffer for A(i) + seed
                    byte[] result = null;
 
                    try
                    {
 
                        for ( int i = 0; i < keySizeInBytes; )
                        {
                            // Calculate A(i+1).                
                            kha.Initialize();
                            a = kha.ComputeHash( a );
 
                            // Calculate A(i) + seed
                            a.CopyTo( b, 0 );
                            issuerEntropy.CopyTo( b, a.Length );
                            kha.Initialize();
                            result = kha.ComputeHash( b );
 
                            for ( int j = 0; j < result.Length; j++ )
                            {
                                if ( i < keySizeInBytes )
                                {
                                    key[i++] = result[j];
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                    catch
                    {
                        Array.Clear( key, 0, key.Length );
                        throw;
                    }
                    finally
                    {
                        if ( result != null )
                        {
                            Array.Clear( result, 0, result.Length );
                        }
 
                        Array.Clear( b, 0, b.Length );
 
                        kha.Clear();
                    }
                }
 
                return key;
            }
 
            /// <summary>
            /// Generates a symmetric key with a given size.
            /// </summary>
            /// <remarks>This function should not be used to generate DES keys because it does not perform an IsWeakKey check.
            /// Use GenerateDESKey() instead.</remarks>
            /// <param name="keySizeInBits">The key size in bits.</param>
            /// <returns>The symmetric key.</returns>
            /// <exception cref="ArgumentException">When keySizeInBits is not a whole number of bytes.</exception>
            public static byte[] GenerateSymmetricKey( int keySizeInBits )
            {
                int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
 
                byte[] key = new byte[keySizeInBytes];
 
                CryptoHelper.GenerateRandomBytes( key );
 
                return key;
            }
 
            /// <summary>
            /// Generates a combined-entropy key.
            /// </summary>
            /// <remarks>This function should not be used to generate DES keys because it does not perform an IsWeakKey check.
            /// Use GenerateDESKey() instead.</remarks>
            /// <param name="keySizeInBits">The key size in bits.</param>
            /// <param name="senderEntropy">Requestor's entropy.</param>
            /// <param name="receiverEntropy">The issuer's entropy.</param>
            /// <returns>The computed symmetric key based on PSHA1 algorithm.</returns>
            /// <exception cref="ArgumentException">When keySizeInBits is not a whole number of bytes.</exception>
            public static byte[] GenerateSymmetricKey( int keySizeInBits, byte[] senderEntropy, out byte[] receiverEntropy )
            {
                if ( senderEntropy == null )
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "senderEntropy" );
                }
 
                int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
 
                //
                // Generate proof key using sender entropy and receiver entropy
                //
                receiverEntropy = new byte[keySizeInBytes];
 
                _random.GetNonZeroBytes( receiverEntropy );
 
                return ComputeCombinedKey( senderEntropy, receiverEntropy, keySizeInBits );
            }
 
            /// <summary>
            /// Generates a symmetric key for use with the DES or Triple-DES algorithms.  This function will always return a key that is
            /// not considered weak by TripleDES.IsWeakKey().
            /// </summary>
            /// <param name="keySizeInBits">The key size in bits.</param>
            /// <returns>The symmetric key.</returns>
            /// <exception cref="ArgumentException">When keySizeInBits is not a proper DES key size.</exception>
            public static byte[] GenerateDESKey( int keySizeInBits )
            {
                int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
 
                byte[] key = new byte[keySizeInBytes];
                int tries = 0;
 
                do
                {
                    if ( tries > _maxKeyIterations )
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new CryptographicException( SR.GetString( SR.ID6048, _maxKeyIterations ) ) );
                    }
 
                    CryptoHelper.GenerateRandomBytes( key );
                    ++tries;
 
                } while ( TripleDES.IsWeakKey( key ) );
 
                return key;
            }
 
            /// <summary>
            /// Generates a combined-entropy key for use with the DES or Triple-DES algorithms.  This function will always return a key that is
            /// not considered weak by TripleDES.IsWeakKey().
            /// </summary>
            /// <param name="keySizeInBits">The key size in bits.</param>
            /// <param name="senderEntropy">Requestor's entropy.</param>
            /// <param name="receiverEntropy">The issuer's entropy.</param>
            /// <returns>The computed symmetric key based on PSHA1 algorithm.</returns>
            /// <exception cref="ArgumentException">When keySizeInBits is not a proper DES key size.</exception>
            public static byte[] GenerateDESKey( int keySizeInBits, byte[] senderEntropy, out byte[] receiverEntropy )
            {
                int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
 
                byte[] key = new byte[keySizeInBytes];
                int tries = 0;
 
                do
                {
                    if ( tries > _maxKeyIterations )
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new CryptographicException( SR.GetString( SR.ID6048, _maxKeyIterations ) ) );
                    }
 
                    receiverEntropy = new byte[keySizeInBytes];
                    _random.GetNonZeroBytes( receiverEntropy );
                    key = ComputeCombinedKey( senderEntropy, receiverEntropy, keySizeInBits );
                    ++tries;
 
                } while ( TripleDES.IsWeakKey( key ) );
 
                return key;
            }
 
            static int ValidateKeySizeInBytes( int keySizeInBits )
            {
                int keySizeInBytes = keySizeInBits / 8;
 
                if ( keySizeInBits <= 0 )
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ArgumentOutOfRangeException( "keySizeInBits", SR.GetString( SR.ID6033, keySizeInBits ) ) );
                }
                else if ( keySizeInBytes * 8 != keySizeInBits )
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ArgumentException( SR.GetString( SR.ID6002, keySizeInBits ), "keySizeInBits" ) );
                }
 
                return keySizeInBytes;
            }
 
            /// <summary>
            /// Gets a security key identifier which contains the BinarySecretKeyIdentifierClause or 
            /// EncryptedKeyIdentifierClause if the wrapping credentials is available.
            /// </summary>
            public static SecurityKeyIdentifier GetSecurityKeyIdentifier(byte[] secret, EncryptingCredentials wrappingCredentials)
            {
                if (secret == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("secret");
                }
 
                if (secret.Length == 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("secret", SR.GetString(SR.ID6031));
                }
 
                if (wrappingCredentials == null || wrappingCredentials.SecurityKey == null)
                {
                    //
                    // BinarySecret case
                    //
                    return new SecurityKeyIdentifier(new BinarySecretKeyIdentifierClause(secret));
                }
                else
                {
                    //
                    // EncryptedKey case
                    //
                    byte[] wrappedKey = wrappingCredentials.SecurityKey.EncryptKey(wrappingCredentials.Algorithm, secret);
 
                    return new SecurityKeyIdentifier(new EncryptedKeyIdentifierClause(wrappedKey, wrappingCredentials.Algorithm, wrappingCredentials.SecurityKeyIdentifier));
                }
            }
 
        }
 
        /// <summary>
        /// Provides an integer-domain mathematical operation for 
        /// Ceiling( dividend / divisor ). 
        /// </summary>
        /// <param name="dividend"></param>
        /// <param name="divisor"></param>
        /// <returns></returns>
        public static int CeilingDivide( int dividend, int divisor )
        {
            int remainder, quotient;
 
            remainder = dividend % divisor;
            quotient = dividend / divisor;
 
            if ( remainder > 0 )
            {
                quotient++;
            }
 
            return quotient;
        }
 
        internal static byte[] EmptyBuffer
        {
            get
            {
                if (emptyBuffer == null)
                {
                    byte[] tmp = new byte[0];
                    emptyBuffer = tmp;
                }
                return emptyBuffer;
            }
        }
 
        internal static Rijndael Rijndael
        {
            get
            {
                if (rijndael == null)
                {
                    Rijndael tmp = SecurityUtils.RequiresFipsCompliance ? (Rijndael)new RijndaelCryptoServiceProvider() : new RijndaelManaged();
                    tmp.Padding = PaddingMode.ISO10126;
                    rijndael = tmp;
                }
                return rijndael;
            }
        }
 
        internal static TripleDES TripleDES
        {
            get
            {
                if (tripleDES == null)
                {
                    TripleDESCryptoServiceProvider tmp = new TripleDESCryptoServiceProvider();
                    tmp.Padding = PaddingMode.ISO10126;
                    tripleDES = tmp;
                }
                return tripleDES;
            }
        }
 
        internal static RandomNumberGenerator RandomNumberGenerator
        {
            get
            {
                if (random == null)
                {
                    random = new RNGCryptoServiceProvider();
                }
                return random;
            }
        }
 
        /// <summary>
        /// Creates the default encryption algorithm.
        /// </summary>
        /// <returns>A SymmetricAlgorithm instance that must be disposed by the caller after use.</returns>
        internal static SymmetricAlgorithm NewDefaultEncryption()
        {
            return GetSymmetricAlgorithm(null, SecurityAlgorithms.DefaultEncryptionAlgorithm );
        }
 
        internal static HashAlgorithm NewSha1HashAlgorithm()
        {
            return CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha1Digest);
        }
 
        internal static HashAlgorithm NewSha256HashAlgorithm()
        {
            return CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha256Digest);
        }
 
        internal static KeyedHashAlgorithm NewHmacSha1KeyedHashAlgorithm()
        {
            KeyedHashAlgorithm algorithm = GetAlgorithmFromConfig( SecurityAlgorithms.HmacSha1Signature ) as KeyedHashAlgorithm;
            if ( algorithm == null )
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( "algorithm", SR.GetString( SR.ID6037, SecurityAlgorithms.HmacSha1Signature ) );
            }
            return algorithm;
        }
 
        internal static KeyedHashAlgorithm NewHmacSha1KeyedHashAlgorithm(byte[] key)
        {
            return CryptoHelper.CreateKeyedHashAlgorithm(key, SecurityAlgorithms.HmacSha1Signature);
        }
 
        internal static KeyedHashAlgorithm NewHmacSha256KeyedHashAlgorithm(byte[] key)
        {
            return CryptoHelper.CreateKeyedHashAlgorithm(key, SecurityAlgorithms.HmacSha256Signature);
        }
 
        internal static Rijndael NewRijndaelSymmetricAlgorithm()
        {
            Rijndael rijndael = (GetSymmetricAlgorithm(null, SecurityAlgorithms.Aes128Encryption) as Rijndael);
            if (rijndael != null)
                return rijndael;
            throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, SecurityAlgorithms.Aes128Encryption)));
        }
 
        internal static ICryptoTransform CreateDecryptor(byte[] key, byte[] iv, string algorithm)
        {
            object algorithmObject = GetAlgorithmFromConfig(algorithm);
 
            if (algorithmObject != null)
            {
                SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
 
                if (symmetricAlgorithm != null)
                {
                    return symmetricAlgorithm.CreateDecryptor(key, iv);
                }
                //NOTE: KeyedHashAlgorithms are symmetric in nature but we still throw if it is passed as an argument. 
 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, algorithm)));
            }
 
            switch (algorithm)
            {
                case SecurityAlgorithms.TripleDesEncryption:
                    return TripleDES.CreateDecryptor(key, iv);
                case SecurityAlgorithms.Aes128Encryption:
                case SecurityAlgorithms.Aes192Encryption:
                case SecurityAlgorithms.Aes256Encryption:
                    return Rijndael.CreateDecryptor(key, iv);
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedEncryptionAlgorithm, algorithm)));
            }
        }
 
        internal static ICryptoTransform CreateEncryptor(byte[] key, byte[] iv, string algorithm)
        {
 
            object algorithmObject = GetAlgorithmFromConfig(algorithm);
 
            if (algorithmObject != null)
            {
                SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
                if (symmetricAlgorithm != null)
                {
                    return symmetricAlgorithm.CreateEncryptor(key, iv);
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, algorithm)));
            }
 
            switch (algorithm)
            {
                case SecurityAlgorithms.TripleDesEncryption:
                    return TripleDES.CreateEncryptor(key, iv);
                case SecurityAlgorithms.Aes128Encryption:
                case SecurityAlgorithms.Aes192Encryption:
                case SecurityAlgorithms.Aes256Encryption:
                    return Rijndael.CreateEncryptor(key, iv);
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedEncryptionAlgorithm, algorithm)));
            }
        }
 
        internal static HashAlgorithm CreateHashAlgorithm(string algorithm)
        {
            object algorithmObject = GetAlgorithmFromConfig(algorithm);
 
            if (algorithmObject != null)
            {
                HashAlgorithm hashAlgorithm = algorithmObject as HashAlgorithm;
                if (hashAlgorithm != null)
                {
                    return hashAlgorithm;
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidHashAlgorithm, algorithm)));
            }
 
            switch (algorithm)
            {
                case SHAString:
                case SHA1String:
                case SystemSecurityCryptographySha1String:
                case SecurityAlgorithms.Sha1Digest:
                    if (SecurityUtils.RequiresFipsCompliance)
                        return new SHA1CryptoServiceProvider();
                    else
                        return new SHA1Managed();
                case SHA256String:
                case SecurityAlgorithms.Sha256Digest:
                    if (SecurityUtils.RequiresFipsCompliance)
                        return new SHA256CryptoServiceProvider();
                    else
                        return new SHA256Managed();
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
            }
        }
 
        internal static KeyedHashAlgorithm CreateKeyedHashAlgorithm(byte[] key, string algorithm)
        {
            object algorithmObject = GetAlgorithmFromConfig(algorithm);
 
            if (algorithmObject != null)
            {
                KeyedHashAlgorithm keyedHashAlgorithm = algorithmObject as KeyedHashAlgorithm;
                if (keyedHashAlgorithm != null)
                {
                    keyedHashAlgorithm.Key = key;
                    return keyedHashAlgorithm;
                }
 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidKeyedHashAlgorithm, algorithm)));
            }
 
            switch (algorithm)
            {
                case SecurityAlgorithms.HmacSha1Signature:
                    return new HMACSHA1(key, !SecurityUtils.RequiresFipsCompliance);
                case SecurityAlgorithms.HmacSha256Signature:
                    if (!SecurityUtils.RequiresFipsCompliance)
                        return new HMACSHA256(key);
                    else
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CryptoAlgorithmIsNotFipsCompliant, algorithm)));
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
            }
        }
 
        internal static byte[] ComputeHash(byte[] buffer)
        {
            using (HashAlgorithm hasher = CryptoHelper.NewSha1HashAlgorithm())
            {
                return hasher.ComputeHash(buffer);
            }
        }
 
        internal static byte[] GenerateDerivedKey(byte[] key, string algorithm, byte[] label, byte[] nonce, int derivedKeySize, int position)
        {
            if ((algorithm != SecurityAlgorithms.Psha1KeyDerivation) && (algorithm != SecurityAlgorithms.Psha1KeyDerivationDec2005))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedKeyDerivationAlgorithm, algorithm)));
            }
            return new Psha1DerivedKeyGenerator(key).GenerateDerivedKey(label, nonce, derivedKeySize, position);
        }
 
        internal static int GetIVSize(string algorithm)
        {
            object algorithmObject = GetAlgorithmFromConfig(algorithm);
 
            if (algorithmObject != null)
            {
                SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
                if (symmetricAlgorithm != null)
                {
                    return symmetricAlgorithm.BlockSize;
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, algorithm)));
            }
 
            switch (algorithm)
            {
                case SecurityAlgorithms.TripleDesEncryption:
                    return TripleDES.BlockSize;
                case SecurityAlgorithms.Aes128Encryption:
                case SecurityAlgorithms.Aes192Encryption:
                case SecurityAlgorithms.Aes256Encryption:
                    return Rijndael.BlockSize;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedEncryptionAlgorithm, algorithm)));
            }
        }
 
        internal static void FillRandomBytes( byte[] buffer )
        {
            RandomNumberGenerator.GetBytes( buffer );
        }
 
        /// <summary>
        /// This generates the entropy using random number. This is usually used on the sending 
        /// side to generate the requestor's entropy.
        /// </summary>
        /// <param name="data">The array to fill with cryptographically strong random nonzero bytes.</param>
        public static void GenerateRandomBytes( byte[] data )
        {
            RandomNumberGenerator.GetNonZeroBytes( data );
        }
 
        /// <summary>
        /// This method generates a random byte array used as entropy with the given size. 
        /// </summary>
        /// <param name="sizeInBits"></param>
        /// <returns></returns>
        public static byte[] GenerateRandomBytes( int sizeInBits )
        {
            int sizeInBytes = sizeInBits / 8;
            if ( sizeInBits <= 0 )
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ArgumentOutOfRangeException( "sizeInBits", SR.GetString( SR.ID6033, sizeInBits ) ) );
            }
            else if ( sizeInBytes * 8 != sizeInBits )
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ArgumentException( SR.GetString( SR.ID6002, sizeInBits ), "sizeInBits" ) );
            }
 
            byte[] data = new byte[sizeInBytes];
            GenerateRandomBytes( data );
 
            return data;
        }
            
        internal static SymmetricAlgorithm GetSymmetricAlgorithm(byte[] key, string algorithm)
        {
            SymmetricAlgorithm symmetricAlgorithm;
 
            object algorithmObject = GetAlgorithmFromConfig(algorithm);
 
            if (algorithmObject != null)
            {
                symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
                if (symmetricAlgorithm != null)
                {
                    if (key != null)
                    {
                        symmetricAlgorithm.Key = key;
                    }
                    return symmetricAlgorithm;
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, algorithm)));
            }
 
            // NOTE: HMACSHA1 and HMACSHA256 ( KeyedHashAlgorithms ) are symmetric algorithms but they do not extend Symmetric class. 
            // Hence the function throws when they are passed as arguments.
 
            switch (algorithm)
            {
                case SecurityAlgorithms.TripleDesEncryption:
                case SecurityAlgorithms.TripleDesKeyWrap:
                    symmetricAlgorithm = new TripleDESCryptoServiceProvider();
                    break;
                case SecurityAlgorithms.Aes128Encryption:
                case SecurityAlgorithms.Aes192Encryption:
                case SecurityAlgorithms.Aes256Encryption:
                case SecurityAlgorithms.Aes128KeyWrap:
                case SecurityAlgorithms.Aes192KeyWrap:
                case SecurityAlgorithms.Aes256KeyWrap:
                    symmetricAlgorithm = SecurityUtils.RequiresFipsCompliance ? (Rijndael)new RijndaelCryptoServiceProvider() : new RijndaelManaged();
                    break;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedEncryptionAlgorithm, algorithm)));
 
            }
 
            if (key != null)
            {
                symmetricAlgorithm.Key = key;
            }
            return symmetricAlgorithm;
        }
 
        /// <summary>
        /// Wrapper that creates a signature for SHA256 taking into consideration the special logic required for FIPS compliance
        /// </summary>
        /// <param name="formatter">the signature formatter</param>
        /// <param name="hash">the hash algorithm</param>
        /// <returns>byte array representing the signature</returns>
        internal static byte[] CreateSignatureForSha256( AsymmetricSignatureFormatter formatter, HashAlgorithm hash )
        {
            if ( SecurityUtils.RequiresFipsCompliance )
            {
                //
                // When FIPS is turned ON. We need to set the hash algorithm specifically 
                // as we need to pass the pre-computed buffer to CreateSignature, else
                // for SHA256 and FIPS turned ON, the underlying formatter does not understand the 
                // OID for the hashing algorithm.
                //
                formatter.SetHashAlgorithm( "SHA256" );
                return formatter.CreateSignature( hash.Hash );
            }
            else
            {
                //
                // Calling the formatter with the object allows us to be Crypto-Agile
                //
                return formatter.CreateSignature( hash );
            }
        }
 
        /// <summary>
        /// Wrapper that verifies the signature for SHA256 taking into consideration the special logic for FIPS compliance
        /// </summary>
        /// <param name="deformatter">the signature deformatter</param>
        /// <param name="hash">the hash algorithm</param>
        /// <param name="signatureValue">the byte array for the signature value</param>
        /// <returns>true/false indicating if signature was verified or not</returns>
        internal static bool VerifySignatureForSha256( AsymmetricSignatureDeformatter deformatter, HashAlgorithm hash, byte[] signatureValue )
        {
            if ( SecurityUtils.RequiresFipsCompliance )
            {
                //
                // When FIPS is turned ON. We need to set the hash algorithm specifically 
                // else for SHA256 and FIPS turned ON, the underlying deformatter does not understand the 
                // OID for the hashing algorithm.
                //
                deformatter.SetHashAlgorithm( "SHA256" );
                return deformatter.VerifySignature( hash.Hash, signatureValue );
            }
            else
            {
                return deformatter.VerifySignature( hash, signatureValue );
            }
        }
 
        
        /// <summary>
        /// This method returns an AsymmetricSignatureFormatter capable of supporting Sha256 signatures. 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        internal static AsymmetricSignatureFormatter GetSignatureFormatterForSha256( AsymmetricSecurityKey key )
        {
            AsymmetricAlgorithm algorithm = key.GetAsymmetricAlgorithm( SecurityAlgorithms.RsaSha256Signature, true );
            RSACryptoServiceProvider rsaProvider = algorithm as RSACryptoServiceProvider;
            if ( null != rsaProvider )
            {
                return GetSignatureFormatterForSha256( rsaProvider );
            }
            else
            {
                //
                // If not an RSaCryptoServiceProvider, we can only hope that
                //  the derived imlementation does the correct thing thing WRT Sha256.
                //
                return new RSAPKCS1SignatureFormatter( algorithm );
            }
        }
 
        /// <summary>
        /// This method returns an AsymmetricSignatureFormatter capable of supporting Sha256 signatures. 
        /// </summary>
        internal static AsymmetricSignatureFormatter GetSignatureFormatterForSha256( RSACryptoServiceProvider rsaProvider )
        {
            const int PROV_RSA_AES = 24;    // CryptoApi provider type for an RSA provider supporting sha-256 digital signatures
            AsymmetricSignatureFormatter formatter = null;
            CspParameters csp = new CspParameters();
            csp.ProviderType = PROV_RSA_AES;
            if ( PROV_RSA_AES == rsaProvider.CspKeyContainerInfo.ProviderType )
            {
                csp.ProviderName = rsaProvider.CspKeyContainerInfo.ProviderName;
            }
            csp.KeyContainerName = rsaProvider.CspKeyContainerInfo.KeyContainerName;
            csp.KeyNumber = (int)rsaProvider.CspKeyContainerInfo.KeyNumber;
            if ( rsaProvider.CspKeyContainerInfo.MachineKeyStore )
            {
                csp.Flags = CspProviderFlags.UseMachineKeyStore;
            }
 
            csp.Flags |= CspProviderFlags.UseExistingKey;
            rsaProvider = new RSACryptoServiceProvider( csp );
            formatter = new RSAPKCS1SignatureFormatter( rsaProvider );
            return formatter;
        }
 
        /// <summary>
        /// This method returns an AsymmetricSignatureDeFormatter capable of supporting Sha256 signatures. 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        internal static AsymmetricSignatureDeformatter GetSignatureDeFormatterForSha256( AsymmetricSecurityKey key )
        {
            RSAPKCS1SignatureDeformatter deformatter;
            AsymmetricAlgorithm algorithm = key.GetAsymmetricAlgorithm( SecurityAlgorithms.RsaSha256Signature, false );
            RSACryptoServiceProvider rsaProvider = algorithm as RSACryptoServiceProvider;
            if ( null != rsaProvider )
            {
                return GetSignatureDeFormatterForSha256( rsaProvider );
            }
            else
            {
                //
                // If not an RSaCryptoServiceProvider, we can only hope that
                //  the derived imlementation does the correct thing WRT Sha256.
                //
                deformatter = new RSAPKCS1SignatureDeformatter( algorithm );
            }
 
            return deformatter;
        }
 
        /// <summary>
        /// This method returns an AsymmetricSignatureDeFormatter capable of supporting Sha256 signatures. 
        /// </summary>
        internal static AsymmetricSignatureDeformatter GetSignatureDeFormatterForSha256( RSACryptoServiceProvider rsaProvider )
        {
            const int PROV_RSA_AES = 24;    // CryptoApi provider type for an RSA provider supporting sha-256 digital signatures
            AsymmetricSignatureDeformatter deformatter = null;
            CspParameters csp = new CspParameters();
            csp.ProviderType = PROV_RSA_AES;
            if ( PROV_RSA_AES == rsaProvider.CspKeyContainerInfo.ProviderType )
            {
                csp.ProviderName = rsaProvider.CspKeyContainerInfo.ProviderName;
            }
            csp.KeyNumber = (int)rsaProvider.CspKeyContainerInfo.KeyNumber;
            if ( rsaProvider.CspKeyContainerInfo.MachineKeyStore )
            {
                csp.Flags = CspProviderFlags.UseMachineKeyStore;
            }
            
            csp.Flags |= CspProviderFlags.UseExistingKey;
            RSACryptoServiceProvider rsaPublicProvider = new RSACryptoServiceProvider( csp );
            rsaPublicProvider.ImportCspBlob( rsaProvider.ExportCspBlob( false ) );
            deformatter = new RSAPKCS1SignatureDeformatter( rsaPublicProvider );
            return deformatter;
        }
 
        internal static bool IsAsymmetricAlgorithm(string algorithm)
        {
            object algorithmObject = null;
 
            try
            {
                algorithmObject = GetAlgorithmFromConfig(algorithm);
            }
            catch (InvalidOperationException)
            {
                algorithmObject = null;
                // We swallow the exception and continue.
            }
 
            if (algorithmObject != null)
            {
                AsymmetricAlgorithm asymmetricAlgorithm = algorithmObject as AsymmetricAlgorithm;
                SignatureDescription signatureDescription = algorithmObject as SignatureDescription;
                if (asymmetricAlgorithm != null || signatureDescription != null)
                    return true;
                return false;
            }
 
            switch (algorithm)
            {
                case SecurityAlgorithms.DsaSha1Signature:
                case SecurityAlgorithms.RsaSha1Signature:
                case SecurityAlgorithms.RsaSha256Signature:
                case SecurityAlgorithms.RsaOaepKeyWrap:
                case SecurityAlgorithms.RsaV15KeyWrap:
                    return true;
                default:
                    return false;
            }
        }
 
        internal static bool IsSymmetricAlgorithm(string algorithm)
        {
            object algorithmObject = null;
 
            try
            {
                algorithmObject = GetAlgorithmFromConfig(algorithm);
            }
            catch (InvalidOperationException)
            {
                algorithmObject = null;
                // We swallow the exception and continue.
            }
            if (algorithmObject != null)
            {
                SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
                KeyedHashAlgorithm keyedHashAlgorithm = algorithmObject as KeyedHashAlgorithm;
                if (symmetricAlgorithm != null || keyedHashAlgorithm != null)
                    return true;
                return false;
            }
 
            // NOTE: A KeyedHashAlgorithm is symmetric in nature.
 
            switch (algorithm)
            {
                case SecurityAlgorithms.DsaSha1Signature:
                case SecurityAlgorithms.RsaSha1Signature:
                case SecurityAlgorithms.RsaSha256Signature:
                case SecurityAlgorithms.RsaOaepKeyWrap:
                case SecurityAlgorithms.RsaV15KeyWrap:
                    return false;
                case SecurityAlgorithms.HmacSha1Signature:
                case SecurityAlgorithms.HmacSha256Signature:
                case SecurityAlgorithms.Aes128Encryption:
                case SecurityAlgorithms.Aes192Encryption:
                case SecurityAlgorithms.DesEncryption:
                case SecurityAlgorithms.Aes256Encryption:
                case SecurityAlgorithms.TripleDesEncryption:
                case SecurityAlgorithms.Aes128KeyWrap:
                case SecurityAlgorithms.Aes192KeyWrap:
                case SecurityAlgorithms.Aes256KeyWrap:
                case SecurityAlgorithms.TripleDesKeyWrap:
                case SecurityAlgorithms.Psha1KeyDerivation:
                case SecurityAlgorithms.Psha1KeyDerivationDec2005:
                    return true;
                default:
                    return false;
            }
        }
 
        internal static bool IsSymmetricSupportedAlgorithm(string algorithm, int keySize)
        {
            bool found = false;
            object algorithmObject = null;
 
            try
            {
                algorithmObject = GetAlgorithmFromConfig(algorithm);
            }
            catch (InvalidOperationException)
            {
                // We swallow the exception and continue.
            }
            if (algorithmObject != null)
            {
                SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
                KeyedHashAlgorithm keyedHashAlgorithm = algorithmObject as KeyedHashAlgorithm;
 
                if (symmetricAlgorithm != null || keyedHashAlgorithm != null)
                    found = true;
                // The reason we do not return here even when the user has provided a custom algorithm in machine.config 
                // is because we need to check if the user has overwritten an existing standard URI.
            }
 
            switch (algorithm)
            {
                case SecurityAlgorithms.DsaSha1Signature:
                case SecurityAlgorithms.RsaSha1Signature:
                case SecurityAlgorithms.RsaSha256Signature:
                case SecurityAlgorithms.RsaOaepKeyWrap:
                case SecurityAlgorithms.RsaV15KeyWrap:
                    return false;
                case SecurityAlgorithms.HmacSha1Signature:
                case SecurityAlgorithms.HmacSha256Signature:
                case SecurityAlgorithms.Psha1KeyDerivation:
                case SecurityAlgorithms.Psha1KeyDerivationDec2005:
                    return true;
                case SecurityAlgorithms.Aes128Encryption:
                case SecurityAlgorithms.Aes128KeyWrap:
                    return keySize >= 128 && keySize <= 256;
                case SecurityAlgorithms.Aes192Encryption:
                case SecurityAlgorithms.Aes192KeyWrap:
                    return keySize >= 192 && keySize <= 256;
                case SecurityAlgorithms.Aes256Encryption:
                case SecurityAlgorithms.Aes256KeyWrap:
                    return keySize == 256;
                case SecurityAlgorithms.TripleDesEncryption:
                case SecurityAlgorithms.TripleDesKeyWrap:
                    return keySize == 128 || keySize == 192;
                default:
                    if (found)
                        return true;
                    return false;
                // We do not expect the user to map the uri of an existing standrad algorithm with say key size 128 bit 
                // to a custom algorithm with keySize 192 bits. If he does that, we anyways make sure that we return false.
            }
        }
 
        // We currently call the CLR APIs to do symmetric key wrap.
        // This ends up causing a triple cloning of the byte arrays.
        // However, the symmetric key wrap exists now primarily for
        // the feature completeness of cryptos and tokens.  That is,
        // it is never encountered in any Indigo AuthenticationMode.
        // The performance of this should be reviewed if this gets hit
        // in any mainline scenario.
        internal static byte[] UnwrapKey(byte[] wrappingKey, byte[] wrappedKey, string algorithm)
        {
            SymmetricAlgorithm symmetricAlgorithm;
            object algorithmObject = GetAlgorithmFromConfig(algorithm);
            if (algorithmObject != null)
            {
                symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
                if (symmetricAlgorithm == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.InvalidCustomKeyWrapAlgorithm, algorithm)));
                }
                using (symmetricAlgorithm)
                {
                    symmetricAlgorithm.Key = wrappingKey;
                    return EncryptedXml.DecryptKey(wrappedKey, symmetricAlgorithm);
                }
            }
            switch (algorithm)
            {
                case SecurityAlgorithms.TripleDesKeyWrap:
                    symmetricAlgorithm = new TripleDESCryptoServiceProvider();
                    break;
                case SecurityAlgorithms.Aes128KeyWrap:
                case SecurityAlgorithms.Aes192KeyWrap:
                case SecurityAlgorithms.Aes256KeyWrap:
                    symmetricAlgorithm = SecurityUtils.RequiresFipsCompliance ? (Rijndael)new RijndaelCryptoServiceProvider() : new RijndaelManaged();
                    break;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedKeyWrapAlgorithm, algorithm)));
            }
 
            using (symmetricAlgorithm)
            {
                symmetricAlgorithm.Key = wrappingKey;
                return EncryptedXml.DecryptKey(wrappedKey, symmetricAlgorithm);
            }
        }
 
        internal static byte[] WrapKey(byte[] wrappingKey, byte[] keyToBeWrapped, string algorithm)
        {
            SymmetricAlgorithm symmetricAlgorithm;
            object algorithmObject = GetAlgorithmFromConfig(algorithm);
            if (algorithmObject != null)
            {
                symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
                if (symmetricAlgorithm == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.InvalidCustomKeyWrapAlgorithm, algorithm)));
                }
                using (symmetricAlgorithm)
                {
                    symmetricAlgorithm.Key = wrappingKey;
                    return EncryptedXml.EncryptKey(keyToBeWrapped, symmetricAlgorithm);
                }
            }
 
            switch (algorithm)
            {
                case SecurityAlgorithms.TripleDesKeyWrap:
                    symmetricAlgorithm = new TripleDESCryptoServiceProvider();
                    break;
                case SecurityAlgorithms.Aes128KeyWrap:
                case SecurityAlgorithms.Aes192KeyWrap:
                case SecurityAlgorithms.Aes256KeyWrap:
                    symmetricAlgorithm = SecurityUtils.RequiresFipsCompliance ? (Rijndael)new RijndaelCryptoServiceProvider() : new RijndaelManaged();
                    break;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedKeyWrapAlgorithm, algorithm)));
            }
 
            using (symmetricAlgorithm)
            {
                symmetricAlgorithm.Key = wrappingKey;
                return EncryptedXml.EncryptKey(keyToBeWrapped, symmetricAlgorithm);
            }
        }
 
        internal static void ValidateBufferBounds(Array buffer, int offset, int count)
        {
            if (buffer == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("buffer"));
            }
            if (count < 0 || count > buffer.Length)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeInRange, 0, buffer.Length)));
            }
            if (offset < 0 || offset > buffer.Length - count)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeInRange, 0, buffer.Length - count)));
            }
        }
 
        /// <summary>
        /// Compares two byte arrays of arbitrary length for equality.
        /// FixedTimeEquals method should be used for crypto-sensitive byte array comparison
        /// i.e. when it's important NOT to leak timing information.
        /// </summary>
        /// <param name="a">One set of bytes to compare.</param>
        /// <param name="b">The other set of bytes to compare with.</param>
        /// <returns>true if the bytes are equal, false otherwise.</returns>
        internal static bool IsEqual(byte[] a, byte[] b)
        {
            if (ReferenceEquals(a, b))
            {
                return true;
            }
 
            if (a == null || b == null || a.Length != b.Length)
            {
                return false;
            }
 
            for (int i = 0; i < a.Length; i++)
            {
                if (a[i] != b[i])
                {
                    return false;
                }
            }
            return true;
        }
 
        /// <summary>
        /// Compares two byte arrays of arbitrary length for equality.
        /// The attempt here is to take the same (constant) time if an attacker changes some of the contents.
        /// </summary>
        /// <param name="a">One set of bytes to compare.</param>
        /// <param name="b">The other set of bytes to compare with.</param>
        /// <returns>true if the bytes are equal, false otherwise.</returns>
        [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
        public static bool FixedTimeEquals(byte[] a, byte[] b)
        {
            if (a == null && b == null)
            {
                return true;
            }
            else if (a == null || b == null)
            {
                return false;
            }
            else if (a.Length != b.Length)
            {
                return false;
            }
 
            var result = 0;
            var length = a.Length;
 
            for (int i = 0; i < length; i++)
            {
                result |= a[i] ^ b[i];
            }
 
            return result == 0;
        }
 
        private static object GetDefaultAlgorithm(string algorithm)
        {
            if (string.IsNullOrEmpty(algorithm))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("algorithm"));
            }
 
            switch (algorithm)
            {
                //case SecurityAlgorithms.RsaSha1Signature:
                //case SecurityAlgorithms.DsaSha1Signature:
                // For these algorithms above, crypto config returns internal objects.
                // As we cannot create those internal objects, we are returning null.
                // If no custom algorithm is plugged-in, at least these two algorithms
                // will be inside the delegate dictionary.
                case SecurityAlgorithms.Sha1Digest:
                    if (SecurityUtils.RequiresFipsCompliance)
                        return new SHA1CryptoServiceProvider();
                    else
                        return new SHA1Managed();
                case SecurityAlgorithms.ExclusiveC14n:
                    return new XmlDsigExcC14NTransform();
                case SHA256String:
                case SecurityAlgorithms.Sha256Digest:
                    if (SecurityUtils.RequiresFipsCompliance)
                        return new SHA256CryptoServiceProvider();
                    else
                        return new SHA256Managed();
                case SecurityAlgorithms.Sha512Digest:
                    if (SecurityUtils.RequiresFipsCompliance)
                        return new SHA512CryptoServiceProvider();
                    else
                        return new SHA512Managed();
                case SecurityAlgorithms.Aes128Encryption:
                case SecurityAlgorithms.Aes192Encryption:
                case SecurityAlgorithms.Aes256Encryption:
                case SecurityAlgorithms.Aes128KeyWrap:
                case SecurityAlgorithms.Aes192KeyWrap:
                case SecurityAlgorithms.Aes256KeyWrap:
                    if (SecurityUtils.RequiresFipsCompliance)
                        return new RijndaelCryptoServiceProvider();
                    else
                        return new RijndaelManaged();
                case SecurityAlgorithms.TripleDesEncryption:
                case SecurityAlgorithms.TripleDesKeyWrap:
                    return new TripleDESCryptoServiceProvider();
                case SecurityAlgorithms.HmacSha1Signature:
                    byte[] key = new byte[64];
                    new RNGCryptoServiceProvider().GetBytes(key);
                    return new HMACSHA1(key, !SecurityUtils.RequiresFipsCompliance);
                case SecurityAlgorithms.HmacSha256Signature:
                    if (!SecurityUtils.RequiresFipsCompliance)
                        return new HMACSHA256();
                    return null;
                case SecurityAlgorithms.ExclusiveC14nWithComments:
                    return new XmlDsigExcC14NWithCommentsTransform();
                case SecurityAlgorithms.Ripemd160Digest:
                    if (!SecurityUtils.RequiresFipsCompliance)
                        return new RIPEMD160Managed();
                    return null;
                case SecurityAlgorithms.DesEncryption:
                    return new DESCryptoServiceProvider();
                default:
                    return null;
            }
        }
 
        private static readonly char[] s_invalidChars = new char[] { ',', '`', '[', '*', '&' };
 
        internal static object GetAlgorithmFromConfig(string algorithm)
        {
            if (string.IsNullOrEmpty(algorithm))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("algorithm"));
            }
 
            object algorithmObject = null;
            object defaultObject = null;
            Func<object> delegateFunction = null;
 
            if (!algorithmDelegateDictionary.TryGetValue(algorithm, out delegateFunction))
            {
                lock (AlgorithmDictionaryLock)
                {
                    if (!algorithmDelegateDictionary.ContainsKey(algorithm))
                    {
                        try
                        {
                            if (!LocalAppContextSwitches.PassUnfilteredAlgorithmsToCryptoConfig)
                                if (algorithm == null || algorithm.IndexOfAny(s_invalidChars) > 0)
                                    return null;
 
                            algorithmObject = CryptoConfig.CreateFromName(algorithm);
                        }
                        catch (TargetInvocationException)
                        {
                            algorithmDelegateDictionary[algorithm] = null;
                        }
 
                        if (algorithmObject == null)
                        {
                            algorithmDelegateDictionary[algorithm] = null;
                        }
                        else
                        {
                            defaultObject = GetDefaultAlgorithm(algorithm);
                            if ((!SecurityUtils.RequiresFipsCompliance && algorithmObject is SHA1CryptoServiceProvider)
                                || (defaultObject != null && defaultObject.GetType() == algorithmObject.GetType()))
                            {
                                algorithmDelegateDictionary[algorithm] = null;
                            }
                            else
                            {
 
                                // Create a factory delegate which returns new instances of the algorithm type for later calls.
                                Type algorithmType = algorithmObject.GetType();
                                System.Linq.Expressions.NewExpression algorithmCreationExpression = System.Linq.Expressions.Expression.New(algorithmType);
                                System.Linq.Expressions.LambdaExpression creationFunction = System.Linq.Expressions.Expression.Lambda<Func<object>>(algorithmCreationExpression);
                                delegateFunction = creationFunction.Compile() as Func<object>;
 
                                if (delegateFunction != null)
                                {
                                    algorithmDelegateDictionary[algorithm] = delegateFunction;
                                }
                                return algorithmObject;
                            }
                        }
                    }
                }
            }
            else
            {
                if (delegateFunction != null)
                {
                    return delegateFunction.Invoke();
                }
            }
 
            //
            // This is a fallback in case CryptoConfig fails to return a valid
            // algorithm object. CrytoConfig does not understand all the uri's and
            // can return a null in that case, in which case it is our responsibility
            // to fallback and create the right algorithm if it is a uri we understand
            //
            switch (algorithm)
            {
                case SHA256String:
                case SecurityAlgorithms.Sha256Digest:
                    if (SecurityUtils.RequiresFipsCompliance)
                    {
                        return new SHA256CryptoServiceProvider();
                    }
                    else
                    {
                        return new SHA256Managed();
                    }
                case SecurityAlgorithms.Sha1Digest:
                    if (SecurityUtils.RequiresFipsCompliance)
                    {
                        return new SHA1CryptoServiceProvider();
                    }
                    else
                    {
                        return new SHA1Managed();
                    }
                case SecurityAlgorithms.HmacSha1Signature:
                    return new HMACSHA1(GenerateRandomBytes(64),
                                                    !SecurityUtils.RequiresFipsCompliance /* indicates the managed version of the algortithm */ );
                default:
                    break;
            }
 
            return null;
        }
 
        public static void ResetAllCertificates(X509Certificate2Collection certificates)
        {
            if (certificates != null)
            {
                for (int i = 0; i < certificates.Count; ++i)
                {
                    certificates[i].Reset();
                }
            }
        }
 
    }
}