1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 namespace System.IdentityModel
7 using System.Collections.Generic;
8 using System.IdentityModel.Tokens;
9 using System.Reflection;
10 using System.Security.Cryptography;
11 using System.Security.Cryptography.X509Certificates;
12 using System.Security.Cryptography.Xml;
13 using System.ServiceModel.Security;
15 static class CryptoHelper
17 static byte[] emptyBuffer;
18 static RandomNumberGenerator random;
19 static Rijndael rijndael;
20 static TripleDES tripleDES;
22 static Dictionary<string, Func<object>> algorithmDelegateDictionary = new Dictionary<string, Func<object>>();
23 static object AlgorithmDictionaryLock = new object();
24 public const int WindowsVistaMajorNumber = 6;
25 const string SHAString = "SHA";
26 const string SHA1String = "SHA1";
27 const string SHA256String = "SHA256";
28 const string SystemSecurityCryptographySha1String = "System.Security.Cryptography.SHA1";
32 /// The helper class which helps user to compute the combined entropy as well as the session
35 public static class KeyGenerator
37 static RandomNumberGenerator _random = CryptoHelper.RandomNumberGenerator;
40 // 1/(2^32) keys will be weak. 20 random keys will never happen by chance without the RNG being messed up.
42 const int _maxKeyIterations = 20;
44 /// Computes the session key based on PSHA1 algorithm.
46 /// <param name="requestorEntropy">The entropy from the requestor side.</param>
47 /// <param name="issuerEntropy">The entropy from the token issuer side.</param>
48 /// <param name="keySizeInBits">The desired key size in bits.</param>
49 /// <returns>The computed session key.</returns>
50 public static byte[] ComputeCombinedKey( byte[] requestorEntropy, byte[] issuerEntropy, int keySizeInBits )
52 if ( null == requestorEntropy )
54 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "requestorEntropy" );
57 if ( null == issuerEntropy )
59 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "issuerEntropy" );
62 int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
64 byte[] key = new byte[keySizeInBytes]; // Final key
66 // The symmetric key generation chosen is
67 // http://schemas.xmlsoap.org/ws/2005/02/trust/CK/PSHA1
68 // which per the WS-Trust specification is defined as follows:
70 // The key is computed using P_SHA1
71 // from the TLS specification to generate
72 // a bit stream using entropy from both
73 // sides. The exact form is:
75 // key = P_SHA1 (EntREQ, EntRES)
77 // where P_SHA1 is defined per http://www.ietf.org/rfc/rfc2246.txt
78 // and EntREQ is the entropy supplied by the requestor and EntRES
79 // is the entrophy supplied by the issuer.
81 // From http://www.faqs.org/rfcs/rfc2246.html:
83 // 8<------------------------------------------------------------>8
84 // First, we define a data expansion function, P_hash(secret, data)
85 // which uses a single hash function to expand a secret and seed
86 // into an arbitrary quantity of output:
88 // P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
89 // HMAC_hash(secret, A(2) + seed) +
90 // HMAC_hash(secret, A(3) + seed) + ...
92 // Where + indicates concatenation.
96 // A(i) = HMAC_hash(secret, A(i-1))
98 // P_hash can be iterated as many times as is necessary to produce
99 // the required quantity of data. For example, if P_SHA-1 was
100 // being used to create 64 bytes of data, it would have to be
101 // iterated 4 times (through A(4)), creating 80 bytes of output
102 // data; the last 16 bytes of the final iteration would then be
103 // discarded, leaving 64 bytes of output data.
104 // 8<------------------------------------------------------------>8
106 // Note that requestorEntrophy is considered the 'secret'.
107 using ( KeyedHashAlgorithm kha = CryptoHelper.NewHmacSha1KeyedHashAlgorithm() )
109 kha.Key = requestorEntropy;
111 byte[] a = issuerEntropy; // A(0), the 'seed'.
112 byte[] b = new byte[kha.HashSize / 8 + a.Length]; // Buffer for A(i) + seed
113 byte[] result = null;
118 for ( int i = 0; i < keySizeInBytes; )
122 a = kha.ComputeHash( a );
124 // Calculate A(i) + seed
126 issuerEntropy.CopyTo( b, a.Length );
128 result = kha.ComputeHash( b );
130 for ( int j = 0; j < result.Length; j++ )
132 if ( i < keySizeInBytes )
134 key[i++] = result[j];
145 Array.Clear( key, 0, key.Length );
150 if ( result != null )
152 Array.Clear( result, 0, result.Length );
155 Array.Clear( b, 0, b.Length );
165 /// Generates a symmetric key with a given size.
167 /// <remarks>This function should not be used to generate DES keys because it does not perform an IsWeakKey check.
168 /// Use GenerateDESKey() instead.</remarks>
169 /// <param name="keySizeInBits">The key size in bits.</param>
170 /// <returns>The symmetric key.</returns>
171 /// <exception cref="ArgumentException">When keySizeInBits is not a whole number of bytes.</exception>
172 public static byte[] GenerateSymmetricKey( int keySizeInBits )
174 int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
176 byte[] key = new byte[keySizeInBytes];
178 CryptoHelper.GenerateRandomBytes( key );
184 /// Generates a combined-entropy key.
186 /// <remarks>This function should not be used to generate DES keys because it does not perform an IsWeakKey check.
187 /// Use GenerateDESKey() instead.</remarks>
188 /// <param name="keySizeInBits">The key size in bits.</param>
189 /// <param name="senderEntropy">Requestor's entropy.</param>
190 /// <param name="receiverEntropy">The issuer's entropy.</param>
191 /// <returns>The computed symmetric key based on PSHA1 algorithm.</returns>
192 /// <exception cref="ArgumentException">When keySizeInBits is not a whole number of bytes.</exception>
193 public static byte[] GenerateSymmetricKey( int keySizeInBits, byte[] senderEntropy, out byte[] receiverEntropy )
195 if ( senderEntropy == null )
197 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull( "senderEntropy" );
200 int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
203 // Generate proof key using sender entropy and receiver entropy
205 receiverEntropy = new byte[keySizeInBytes];
207 _random.GetNonZeroBytes( receiverEntropy );
209 return ComputeCombinedKey( senderEntropy, receiverEntropy, keySizeInBits );
213 /// Generates a symmetric key for use with the DES or Triple-DES algorithms. This function will always return a key that is
214 /// not considered weak by TripleDES.IsWeakKey().
216 /// <param name="keySizeInBits">The key size in bits.</param>
217 /// <returns>The symmetric key.</returns>
218 /// <exception cref="ArgumentException">When keySizeInBits is not a proper DES key size.</exception>
219 public static byte[] GenerateDESKey( int keySizeInBits )
221 int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
223 byte[] key = new byte[keySizeInBytes];
228 if ( tries > _maxKeyIterations )
230 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new CryptographicException( SR.GetString( SR.ID6048, _maxKeyIterations ) ) );
233 CryptoHelper.GenerateRandomBytes( key );
236 } while ( TripleDES.IsWeakKey( key ) );
242 /// Generates a combined-entropy key for use with the DES or Triple-DES algorithms. This function will always return a key that is
243 /// not considered weak by TripleDES.IsWeakKey().
245 /// <param name="keySizeInBits">The key size in bits.</param>
246 /// <param name="senderEntropy">Requestor's entropy.</param>
247 /// <param name="receiverEntropy">The issuer's entropy.</param>
248 /// <returns>The computed symmetric key based on PSHA1 algorithm.</returns>
249 /// <exception cref="ArgumentException">When keySizeInBits is not a proper DES key size.</exception>
250 public static byte[] GenerateDESKey( int keySizeInBits, byte[] senderEntropy, out byte[] receiverEntropy )
252 int keySizeInBytes = ValidateKeySizeInBytes( keySizeInBits );
254 byte[] key = new byte[keySizeInBytes];
259 if ( tries > _maxKeyIterations )
261 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new CryptographicException( SR.GetString( SR.ID6048, _maxKeyIterations ) ) );
264 receiverEntropy = new byte[keySizeInBytes];
265 _random.GetNonZeroBytes( receiverEntropy );
266 key = ComputeCombinedKey( senderEntropy, receiverEntropy, keySizeInBits );
269 } while ( TripleDES.IsWeakKey( key ) );
274 static int ValidateKeySizeInBytes( int keySizeInBits )
276 int keySizeInBytes = keySizeInBits / 8;
278 if ( keySizeInBits <= 0 )
280 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ArgumentOutOfRangeException( "keySizeInBits", SR.GetString( SR.ID6033, keySizeInBits ) ) );
282 else if ( keySizeInBytes * 8 != keySizeInBits )
284 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ArgumentException( SR.GetString( SR.ID6002, keySizeInBits ), "keySizeInBits" ) );
287 return keySizeInBytes;
291 /// Gets a security key identifier which contains the BinarySecretKeyIdentifierClause or
292 /// EncryptedKeyIdentifierClause if the wrapping credentials is available.
294 public static SecurityKeyIdentifier GetSecurityKeyIdentifier(byte[] secret, EncryptingCredentials wrappingCredentials)
298 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("secret");
301 if (secret.Length == 0)
303 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("secret", SR.GetString(SR.ID6031));
306 if (wrappingCredentials == null || wrappingCredentials.SecurityKey == null)
311 return new SecurityKeyIdentifier(new BinarySecretKeyIdentifierClause(secret));
318 byte[] wrappedKey = wrappingCredentials.SecurityKey.EncryptKey(wrappingCredentials.Algorithm, secret);
320 return new SecurityKeyIdentifier(new EncryptedKeyIdentifierClause(wrappedKey, wrappingCredentials.Algorithm, wrappingCredentials.SecurityKeyIdentifier));
327 /// Provides an integer-domain mathematical operation for
328 /// Ceiling( dividend / divisor ).
330 /// <param name="dividend"></param>
331 /// <param name="divisor"></param>
332 /// <returns></returns>
333 public static int CeilingDivide( int dividend, int divisor )
335 int remainder, quotient;
337 remainder = dividend % divisor;
338 quotient = dividend / divisor;
348 internal static byte[] EmptyBuffer
352 if (emptyBuffer == null)
354 byte[] tmp = new byte[0];
361 internal static Rijndael Rijndael
365 if (rijndael == null)
367 Rijndael tmp = SecurityUtils.RequiresFipsCompliance ? (Rijndael)new RijndaelCryptoServiceProvider() : new RijndaelManaged();
368 tmp.Padding = PaddingMode.ISO10126;
375 internal static TripleDES TripleDES
379 if (tripleDES == null)
381 TripleDESCryptoServiceProvider tmp = new TripleDESCryptoServiceProvider();
382 tmp.Padding = PaddingMode.ISO10126;
389 internal static RandomNumberGenerator RandomNumberGenerator
395 random = new RNGCryptoServiceProvider();
402 /// Creates the default encryption algorithm.
404 /// <returns>A SymmetricAlgorithm instance that must be disposed by the caller after use.</returns>
405 internal static SymmetricAlgorithm NewDefaultEncryption()
407 return GetSymmetricAlgorithm(null, SecurityAlgorithms.DefaultEncryptionAlgorithm );
410 internal static HashAlgorithm NewSha1HashAlgorithm()
412 return CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha1Digest);
415 internal static HashAlgorithm NewSha256HashAlgorithm()
417 return CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha256Digest);
420 internal static KeyedHashAlgorithm NewHmacSha1KeyedHashAlgorithm()
422 KeyedHashAlgorithm algorithm = GetAlgorithmFromConfig( SecurityAlgorithms.HmacSha1Signature ) as KeyedHashAlgorithm;
423 if ( algorithm == null )
425 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument( "algorithm", SR.GetString( SR.ID6037, SecurityAlgorithms.HmacSha1Signature ) );
430 internal static KeyedHashAlgorithm NewHmacSha1KeyedHashAlgorithm(byte[] key)
432 return CryptoHelper.CreateKeyedHashAlgorithm(key, SecurityAlgorithms.HmacSha1Signature);
435 internal static KeyedHashAlgorithm NewHmacSha256KeyedHashAlgorithm(byte[] key)
437 return CryptoHelper.CreateKeyedHashAlgorithm(key, SecurityAlgorithms.HmacSha256Signature);
440 internal static Rijndael NewRijndaelSymmetricAlgorithm()
442 Rijndael rijndael = (GetSymmetricAlgorithm(null, SecurityAlgorithms.Aes128Encryption) as Rijndael);
443 if (rijndael != null)
445 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, SecurityAlgorithms.Aes128Encryption)));
448 internal static ICryptoTransform CreateDecryptor(byte[] key, byte[] iv, string algorithm)
450 object algorithmObject = GetAlgorithmFromConfig(algorithm);
452 if (algorithmObject != null)
454 SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
456 if (symmetricAlgorithm != null)
458 return symmetricAlgorithm.CreateDecryptor(key, iv);
460 //NOTE: KeyedHashAlgorithms are symmetric in nature but we still throw if it is passed as an argument.
462 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, algorithm)));
467 case SecurityAlgorithms.TripleDesEncryption:
468 return TripleDES.CreateDecryptor(key, iv);
469 case SecurityAlgorithms.Aes128Encryption:
470 case SecurityAlgorithms.Aes192Encryption:
471 case SecurityAlgorithms.Aes256Encryption:
472 return Rijndael.CreateDecryptor(key, iv);
474 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedEncryptionAlgorithm, algorithm)));
478 internal static ICryptoTransform CreateEncryptor(byte[] key, byte[] iv, string algorithm)
481 object algorithmObject = GetAlgorithmFromConfig(algorithm);
483 if (algorithmObject != null)
485 SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
486 if (symmetricAlgorithm != null)
488 return symmetricAlgorithm.CreateEncryptor(key, iv);
490 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, algorithm)));
495 case SecurityAlgorithms.TripleDesEncryption:
496 return TripleDES.CreateEncryptor(key, iv);
497 case SecurityAlgorithms.Aes128Encryption:
498 case SecurityAlgorithms.Aes192Encryption:
499 case SecurityAlgorithms.Aes256Encryption:
500 return Rijndael.CreateEncryptor(key, iv);
502 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedEncryptionAlgorithm, algorithm)));
506 internal static HashAlgorithm CreateHashAlgorithm(string algorithm)
508 object algorithmObject = GetAlgorithmFromConfig(algorithm);
510 if (algorithmObject != null)
512 HashAlgorithm hashAlgorithm = algorithmObject as HashAlgorithm;
513 if (hashAlgorithm != null)
515 return hashAlgorithm;
517 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidHashAlgorithm, algorithm)));
524 case SystemSecurityCryptographySha1String:
525 case SecurityAlgorithms.Sha1Digest:
526 if (SecurityUtils.RequiresFipsCompliance)
527 return new SHA1CryptoServiceProvider();
529 return new SHA1Managed();
531 case SecurityAlgorithms.Sha256Digest:
532 if (SecurityUtils.RequiresFipsCompliance)
533 return new SHA256CryptoServiceProvider();
535 return new SHA256Managed();
537 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
541 internal static KeyedHashAlgorithm CreateKeyedHashAlgorithm(byte[] key, string algorithm)
543 object algorithmObject = GetAlgorithmFromConfig(algorithm);
545 if (algorithmObject != null)
547 KeyedHashAlgorithm keyedHashAlgorithm = algorithmObject as KeyedHashAlgorithm;
548 if (keyedHashAlgorithm != null)
550 keyedHashAlgorithm.Key = key;
551 return keyedHashAlgorithm;
554 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidKeyedHashAlgorithm, algorithm)));
559 case SecurityAlgorithms.HmacSha1Signature:
560 return new HMACSHA1(key, !SecurityUtils.RequiresFipsCompliance);
561 case SecurityAlgorithms.HmacSha256Signature:
562 if (!SecurityUtils.RequiresFipsCompliance)
563 return new HMACSHA256(key);
565 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CryptoAlgorithmIsNotFipsCompliant, algorithm)));
567 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
571 internal static byte[] ComputeHash(byte[] buffer)
573 using (HashAlgorithm hasher = CryptoHelper.NewSha1HashAlgorithm())
575 return hasher.ComputeHash(buffer);
579 internal static byte[] GenerateDerivedKey(byte[] key, string algorithm, byte[] label, byte[] nonce, int derivedKeySize, int position)
581 if ((algorithm != SecurityAlgorithms.Psha1KeyDerivation) && (algorithm != SecurityAlgorithms.Psha1KeyDerivationDec2005))
583 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedKeyDerivationAlgorithm, algorithm)));
585 return new Psha1DerivedKeyGenerator(key).GenerateDerivedKey(label, nonce, derivedKeySize, position);
588 internal static int GetIVSize(string algorithm)
590 object algorithmObject = GetAlgorithmFromConfig(algorithm);
592 if (algorithmObject != null)
594 SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
595 if (symmetricAlgorithm != null)
597 return symmetricAlgorithm.BlockSize;
599 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, algorithm)));
604 case SecurityAlgorithms.TripleDesEncryption:
605 return TripleDES.BlockSize;
606 case SecurityAlgorithms.Aes128Encryption:
607 case SecurityAlgorithms.Aes192Encryption:
608 case SecurityAlgorithms.Aes256Encryption:
609 return Rijndael.BlockSize;
611 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedEncryptionAlgorithm, algorithm)));
615 internal static void FillRandomBytes( byte[] buffer )
617 RandomNumberGenerator.GetBytes( buffer );
621 /// This generates the entropy using random number. This is usually used on the sending
622 /// side to generate the requestor's entropy.
624 /// <param name="data">The array to fill with cryptographically strong random nonzero bytes.</param>
625 public static void GenerateRandomBytes( byte[] data )
627 RandomNumberGenerator.GetNonZeroBytes( data );
631 /// This method generates a random byte array used as entropy with the given size.
633 /// <param name="sizeInBits"></param>
634 /// <returns></returns>
635 public static byte[] GenerateRandomBytes( int sizeInBits )
637 int sizeInBytes = sizeInBits / 8;
638 if ( sizeInBits <= 0 )
640 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ArgumentOutOfRangeException( "sizeInBits", SR.GetString( SR.ID6033, sizeInBits ) ) );
642 else if ( sizeInBytes * 8 != sizeInBits )
644 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ArgumentException( SR.GetString( SR.ID6002, sizeInBits ), "sizeInBits" ) );
647 byte[] data = new byte[sizeInBytes];
648 GenerateRandomBytes( data );
653 internal static SymmetricAlgorithm GetSymmetricAlgorithm(byte[] key, string algorithm)
655 SymmetricAlgorithm symmetricAlgorithm;
657 object algorithmObject = GetAlgorithmFromConfig(algorithm);
659 if (algorithmObject != null)
661 symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
662 if (symmetricAlgorithm != null)
666 symmetricAlgorithm.Key = key;
668 return symmetricAlgorithm;
670 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.CustomCryptoAlgorithmIsNotValidSymmetricAlgorithm, algorithm)));
673 // NOTE: HMACSHA1 and HMACSHA256 ( KeyedHashAlgorithms ) are symmetric algorithms but they do not extend Symmetric class.
674 // Hence the function throws when they are passed as arguments.
678 case SecurityAlgorithms.TripleDesEncryption:
679 case SecurityAlgorithms.TripleDesKeyWrap:
680 symmetricAlgorithm = new TripleDESCryptoServiceProvider();
682 case SecurityAlgorithms.Aes128Encryption:
683 case SecurityAlgorithms.Aes192Encryption:
684 case SecurityAlgorithms.Aes256Encryption:
685 case SecurityAlgorithms.Aes128KeyWrap:
686 case SecurityAlgorithms.Aes192KeyWrap:
687 case SecurityAlgorithms.Aes256KeyWrap:
688 symmetricAlgorithm = SecurityUtils.RequiresFipsCompliance ? (Rijndael)new RijndaelCryptoServiceProvider() : new RijndaelManaged();
691 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedEncryptionAlgorithm, algorithm)));
697 symmetricAlgorithm.Key = key;
699 return symmetricAlgorithm;
703 /// Wrapper that creates a signature for SHA256 taking into consideration the special logic required for FIPS compliance
705 /// <param name="formatter">the signature formatter</param>
706 /// <param name="hash">the hash algorithm</param>
707 /// <returns>byte array representing the signature</returns>
708 internal static byte[] CreateSignatureForSha256( AsymmetricSignatureFormatter formatter, HashAlgorithm hash )
710 if ( SecurityUtils.RequiresFipsCompliance )
713 // When FIPS is turned ON. We need to set the hash algorithm specifically
714 // as we need to pass the pre-computed buffer to CreateSignature, else
715 // for SHA256 and FIPS turned ON, the underlying formatter does not understand the
716 // OID for the hashing algorithm.
718 formatter.SetHashAlgorithm( "SHA256" );
719 return formatter.CreateSignature( hash.Hash );
724 // Calling the formatter with the object allows us to be Crypto-Agile
726 return formatter.CreateSignature( hash );
731 /// Wrapper that verifies the signature for SHA256 taking into consideration the special logic for FIPS compliance
733 /// <param name="deformatter">the signature deformatter</param>
734 /// <param name="hash">the hash algorithm</param>
735 /// <param name="signatureValue">the byte array for the signature value</param>
736 /// <returns>true/false indicating if signature was verified or not</returns>
737 internal static bool VerifySignatureForSha256( AsymmetricSignatureDeformatter deformatter, HashAlgorithm hash, byte[] signatureValue )
739 if ( SecurityUtils.RequiresFipsCompliance )
742 // When FIPS is turned ON. We need to set the hash algorithm specifically
743 // else for SHA256 and FIPS turned ON, the underlying deformatter does not understand the
744 // OID for the hashing algorithm.
746 deformatter.SetHashAlgorithm( "SHA256" );
747 return deformatter.VerifySignature( hash.Hash, signatureValue );
751 return deformatter.VerifySignature( hash, signatureValue );
757 /// This method returns an AsymmetricSignatureFormatter capable of supporting Sha256 signatures.
759 /// <param name="key"></param>
760 /// <returns></returns>
761 internal static AsymmetricSignatureFormatter GetSignatureFormatterForSha256( AsymmetricSecurityKey key )
763 AsymmetricAlgorithm algorithm = key.GetAsymmetricAlgorithm( SecurityAlgorithms.RsaSha256Signature, true );
764 RSACryptoServiceProvider rsaProvider = algorithm as RSACryptoServiceProvider;
765 if ( null != rsaProvider )
767 return GetSignatureFormatterForSha256( rsaProvider );
772 // If not an RSaCryptoServiceProvider, we can only hope that
773 // the derived imlementation does the correct thing thing WRT Sha256.
775 return new RSAPKCS1SignatureFormatter( algorithm );
780 /// This method returns an AsymmetricSignatureFormatter capable of supporting Sha256 signatures.
782 internal static AsymmetricSignatureFormatter GetSignatureFormatterForSha256( RSACryptoServiceProvider rsaProvider )
784 const int PROV_RSA_AES = 24; // CryptoApi provider type for an RSA provider supporting sha-256 digital signatures
785 AsymmetricSignatureFormatter formatter = null;
786 CspParameters csp = new CspParameters();
787 csp.ProviderType = PROV_RSA_AES;
788 if ( PROV_RSA_AES == rsaProvider.CspKeyContainerInfo.ProviderType )
790 csp.ProviderName = rsaProvider.CspKeyContainerInfo.ProviderName;
792 csp.KeyContainerName = rsaProvider.CspKeyContainerInfo.KeyContainerName;
793 csp.KeyNumber = (int)rsaProvider.CspKeyContainerInfo.KeyNumber;
794 if ( rsaProvider.CspKeyContainerInfo.MachineKeyStore )
796 csp.Flags = CspProviderFlags.UseMachineKeyStore;
799 csp.Flags |= CspProviderFlags.UseExistingKey;
800 rsaProvider = new RSACryptoServiceProvider( csp );
801 formatter = new RSAPKCS1SignatureFormatter( rsaProvider );
806 /// This method returns an AsymmetricSignatureDeFormatter capable of supporting Sha256 signatures.
808 /// <param name="key"></param>
809 /// <returns></returns>
810 internal static AsymmetricSignatureDeformatter GetSignatureDeFormatterForSha256( AsymmetricSecurityKey key )
812 RSAPKCS1SignatureDeformatter deformatter;
813 AsymmetricAlgorithm algorithm = key.GetAsymmetricAlgorithm( SecurityAlgorithms.RsaSha256Signature, false );
814 RSACryptoServiceProvider rsaProvider = algorithm as RSACryptoServiceProvider;
815 if ( null != rsaProvider )
817 return GetSignatureDeFormatterForSha256( rsaProvider );
822 // If not an RSaCryptoServiceProvider, we can only hope that
823 // the derived imlementation does the correct thing WRT Sha256.
825 deformatter = new RSAPKCS1SignatureDeformatter( algorithm );
832 /// This method returns an AsymmetricSignatureDeFormatter capable of supporting Sha256 signatures.
834 internal static AsymmetricSignatureDeformatter GetSignatureDeFormatterForSha256( RSACryptoServiceProvider rsaProvider )
836 const int PROV_RSA_AES = 24; // CryptoApi provider type for an RSA provider supporting sha-256 digital signatures
837 AsymmetricSignatureDeformatter deformatter = null;
838 CspParameters csp = new CspParameters();
839 csp.ProviderType = PROV_RSA_AES;
840 if ( PROV_RSA_AES == rsaProvider.CspKeyContainerInfo.ProviderType )
842 csp.ProviderName = rsaProvider.CspKeyContainerInfo.ProviderName;
844 csp.KeyNumber = (int)rsaProvider.CspKeyContainerInfo.KeyNumber;
845 if ( rsaProvider.CspKeyContainerInfo.MachineKeyStore )
847 csp.Flags = CspProviderFlags.UseMachineKeyStore;
850 csp.Flags |= CspProviderFlags.UseExistingKey;
851 RSACryptoServiceProvider rsaPublicProvider = new RSACryptoServiceProvider( csp );
852 rsaPublicProvider.ImportCspBlob( rsaProvider.ExportCspBlob( false ) );
853 deformatter = new RSAPKCS1SignatureDeformatter( rsaPublicProvider );
857 internal static bool IsAsymmetricAlgorithm(string algorithm)
859 object algorithmObject = null;
863 algorithmObject = GetAlgorithmFromConfig(algorithm);
865 catch (InvalidOperationException)
867 algorithmObject = null;
868 // We swallow the exception and continue.
871 if (algorithmObject != null)
873 AsymmetricAlgorithm asymmetricAlgorithm = algorithmObject as AsymmetricAlgorithm;
874 SignatureDescription signatureDescription = algorithmObject as SignatureDescription;
875 if (asymmetricAlgorithm != null || signatureDescription != null)
882 case SecurityAlgorithms.DsaSha1Signature:
883 case SecurityAlgorithms.RsaSha1Signature:
884 case SecurityAlgorithms.RsaSha256Signature:
885 case SecurityAlgorithms.RsaOaepKeyWrap:
886 case SecurityAlgorithms.RsaV15KeyWrap:
893 internal static bool IsSymmetricAlgorithm(string algorithm)
895 object algorithmObject = null;
899 algorithmObject = GetAlgorithmFromConfig(algorithm);
901 catch (InvalidOperationException)
903 algorithmObject = null;
904 // We swallow the exception and continue.
906 if (algorithmObject != null)
908 SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
909 KeyedHashAlgorithm keyedHashAlgorithm = algorithmObject as KeyedHashAlgorithm;
910 if (symmetricAlgorithm != null || keyedHashAlgorithm != null)
915 // NOTE: A KeyedHashAlgorithm is symmetric in nature.
919 case SecurityAlgorithms.DsaSha1Signature:
920 case SecurityAlgorithms.RsaSha1Signature:
921 case SecurityAlgorithms.RsaSha256Signature:
922 case SecurityAlgorithms.RsaOaepKeyWrap:
923 case SecurityAlgorithms.RsaV15KeyWrap:
925 case SecurityAlgorithms.HmacSha1Signature:
926 case SecurityAlgorithms.HmacSha256Signature:
927 case SecurityAlgorithms.Aes128Encryption:
928 case SecurityAlgorithms.Aes192Encryption:
929 case SecurityAlgorithms.DesEncryption:
930 case SecurityAlgorithms.Aes256Encryption:
931 case SecurityAlgorithms.TripleDesEncryption:
932 case SecurityAlgorithms.Aes128KeyWrap:
933 case SecurityAlgorithms.Aes192KeyWrap:
934 case SecurityAlgorithms.Aes256KeyWrap:
935 case SecurityAlgorithms.TripleDesKeyWrap:
936 case SecurityAlgorithms.Psha1KeyDerivation:
937 case SecurityAlgorithms.Psha1KeyDerivationDec2005:
944 internal static bool IsSymmetricSupportedAlgorithm(string algorithm, int keySize)
947 object algorithmObject = null;
951 algorithmObject = GetAlgorithmFromConfig(algorithm);
953 catch (InvalidOperationException)
955 // We swallow the exception and continue.
957 if (algorithmObject != null)
959 SymmetricAlgorithm symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
960 KeyedHashAlgorithm keyedHashAlgorithm = algorithmObject as KeyedHashAlgorithm;
962 if (symmetricAlgorithm != null || keyedHashAlgorithm != null)
964 // The reason we do not return here even when the user has provided a custom algorithm in machine.config
965 // is because we need to check if the user has overwritten an existing standard URI.
970 case SecurityAlgorithms.DsaSha1Signature:
971 case SecurityAlgorithms.RsaSha1Signature:
972 case SecurityAlgorithms.RsaSha256Signature:
973 case SecurityAlgorithms.RsaOaepKeyWrap:
974 case SecurityAlgorithms.RsaV15KeyWrap:
976 case SecurityAlgorithms.HmacSha1Signature:
977 case SecurityAlgorithms.HmacSha256Signature:
978 case SecurityAlgorithms.Psha1KeyDerivation:
979 case SecurityAlgorithms.Psha1KeyDerivationDec2005:
981 case SecurityAlgorithms.Aes128Encryption:
982 case SecurityAlgorithms.Aes128KeyWrap:
983 return keySize >= 128 && keySize <= 256;
984 case SecurityAlgorithms.Aes192Encryption:
985 case SecurityAlgorithms.Aes192KeyWrap:
986 return keySize >= 192 && keySize <= 256;
987 case SecurityAlgorithms.Aes256Encryption:
988 case SecurityAlgorithms.Aes256KeyWrap:
989 return keySize == 256;
990 case SecurityAlgorithms.TripleDesEncryption:
991 case SecurityAlgorithms.TripleDesKeyWrap:
992 return keySize == 128 || keySize == 192;
997 // We do not expect the user to map the uri of an existing standrad algorithm with say key size 128 bit
998 // to a custom algorithm with keySize 192 bits. If he does that, we anyways make sure that we return false.
1002 // We currently call the CLR APIs to do symmetric key wrap.
1003 // This ends up causing a triple cloning of the byte arrays.
1004 // However, the symmetric key wrap exists now primarily for
1005 // the feature completeness of cryptos and tokens. That is,
1006 // it is never encountered in any Indigo AuthenticationMode.
1007 // The performance of this should be reviewed if this gets hit
1008 // in any mainline scenario.
1009 internal static byte[] UnwrapKey(byte[] wrappingKey, byte[] wrappedKey, string algorithm)
1011 SymmetricAlgorithm symmetricAlgorithm;
1012 object algorithmObject = GetAlgorithmFromConfig(algorithm);
1013 if (algorithmObject != null)
1015 symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
1016 if (symmetricAlgorithm == null)
1018 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.InvalidCustomKeyWrapAlgorithm, algorithm)));
1020 using (symmetricAlgorithm)
1022 symmetricAlgorithm.Key = wrappingKey;
1023 return EncryptedXml.DecryptKey(wrappedKey, symmetricAlgorithm);
1028 case SecurityAlgorithms.TripleDesKeyWrap:
1029 symmetricAlgorithm = new TripleDESCryptoServiceProvider();
1031 case SecurityAlgorithms.Aes128KeyWrap:
1032 case SecurityAlgorithms.Aes192KeyWrap:
1033 case SecurityAlgorithms.Aes256KeyWrap:
1034 symmetricAlgorithm = SecurityUtils.RequiresFipsCompliance ? (Rijndael)new RijndaelCryptoServiceProvider() : new RijndaelManaged();
1037 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedKeyWrapAlgorithm, algorithm)));
1040 using (symmetricAlgorithm)
1042 symmetricAlgorithm.Key = wrappingKey;
1043 return EncryptedXml.DecryptKey(wrappedKey, symmetricAlgorithm);
1047 internal static byte[] WrapKey(byte[] wrappingKey, byte[] keyToBeWrapped, string algorithm)
1049 SymmetricAlgorithm symmetricAlgorithm;
1050 object algorithmObject = GetAlgorithmFromConfig(algorithm);
1051 if (algorithmObject != null)
1053 symmetricAlgorithm = algorithmObject as SymmetricAlgorithm;
1054 if (symmetricAlgorithm == null)
1056 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.InvalidCustomKeyWrapAlgorithm, algorithm)));
1058 using (symmetricAlgorithm)
1060 symmetricAlgorithm.Key = wrappingKey;
1061 return EncryptedXml.EncryptKey(keyToBeWrapped, symmetricAlgorithm);
1067 case SecurityAlgorithms.TripleDesKeyWrap:
1068 symmetricAlgorithm = new TripleDESCryptoServiceProvider();
1070 case SecurityAlgorithms.Aes128KeyWrap:
1071 case SecurityAlgorithms.Aes192KeyWrap:
1072 case SecurityAlgorithms.Aes256KeyWrap:
1073 symmetricAlgorithm = SecurityUtils.RequiresFipsCompliance ? (Rijndael)new RijndaelCryptoServiceProvider() : new RijndaelManaged();
1076 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.UnsupportedKeyWrapAlgorithm, algorithm)));
1079 using (symmetricAlgorithm)
1081 symmetricAlgorithm.Key = wrappingKey;
1082 return EncryptedXml.EncryptKey(keyToBeWrapped, symmetricAlgorithm);
1086 internal static void ValidateBufferBounds(Array buffer, int offset, int count)
1090 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("buffer"));
1092 if (count < 0 || count > buffer.Length)
1094 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", SR.GetString(SR.ValueMustBeInRange, 0, buffer.Length)));
1096 if (offset < 0 || offset > buffer.Length - count)
1098 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", SR.GetString(SR.ValueMustBeInRange, 0, buffer.Length - count)));
1102 internal static bool IsEqual(byte[] a, byte[] b)
1104 if (ReferenceEquals(a, b))
1109 if (a == null || b == null || a.Length != b.Length)
1114 for (int i = 0; i < a.Length; i++)
1124 private static object GetDefaultAlgorithm(string algorithm)
1126 if (string.IsNullOrEmpty(algorithm))
1128 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("algorithm"));
1133 //case SecurityAlgorithms.RsaSha1Signature:
1134 //case SecurityAlgorithms.DsaSha1Signature:
1135 // For these algorithms above, crypto config returns internal objects.
1136 // As we cannot create those internal objects, we are returning null.
1137 // If no custom algorithm is plugged-in, at least these two algorithms
1138 // will be inside the delegate dictionary.
1139 case SecurityAlgorithms.Sha1Digest:
1140 if (SecurityUtils.RequiresFipsCompliance)
1141 return new SHA1CryptoServiceProvider();
1143 return new SHA1Managed();
1144 case SecurityAlgorithms.ExclusiveC14n:
1145 return new XmlDsigExcC14NTransform();
1147 case SecurityAlgorithms.Sha256Digest:
1148 if (SecurityUtils.RequiresFipsCompliance)
1149 return new SHA256CryptoServiceProvider();
1151 return new SHA256Managed();
1152 case SecurityAlgorithms.Sha512Digest:
1153 if (SecurityUtils.RequiresFipsCompliance)
1154 return new SHA512CryptoServiceProvider();
1156 return new SHA512Managed();
1157 case SecurityAlgorithms.Aes128Encryption:
1158 case SecurityAlgorithms.Aes192Encryption:
1159 case SecurityAlgorithms.Aes256Encryption:
1160 case SecurityAlgorithms.Aes128KeyWrap:
1161 case SecurityAlgorithms.Aes192KeyWrap:
1162 case SecurityAlgorithms.Aes256KeyWrap:
1163 if (SecurityUtils.RequiresFipsCompliance)
1164 return new RijndaelCryptoServiceProvider();
1166 return new RijndaelManaged();
1167 case SecurityAlgorithms.TripleDesEncryption:
1168 case SecurityAlgorithms.TripleDesKeyWrap:
1169 return new TripleDESCryptoServiceProvider();
1170 case SecurityAlgorithms.HmacSha1Signature:
1171 byte[] key = new byte[64];
1172 new RNGCryptoServiceProvider().GetBytes(key);
1173 return new HMACSHA1(key, !SecurityUtils.RequiresFipsCompliance);
1174 case SecurityAlgorithms.HmacSha256Signature:
1175 if (!SecurityUtils.RequiresFipsCompliance)
1176 return new HMACSHA256();
1178 case SecurityAlgorithms.ExclusiveC14nWithComments:
1179 return new XmlDsigExcC14NWithCommentsTransform();
1180 case SecurityAlgorithms.Ripemd160Digest:
1181 if (!SecurityUtils.RequiresFipsCompliance)
1182 return new RIPEMD160Managed();
1184 case SecurityAlgorithms.DesEncryption:
1185 return new DESCryptoServiceProvider();
1191 internal static object GetAlgorithmFromConfig(string algorithm)
1193 if (string.IsNullOrEmpty(algorithm))
1195 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("algorithm"));
1198 object algorithmObject = null;
1199 object defaultObject = null;
1200 Func<object> delegateFunction = null;
1202 if (!algorithmDelegateDictionary.TryGetValue(algorithm, out delegateFunction))
1204 lock (AlgorithmDictionaryLock)
1206 if (!algorithmDelegateDictionary.ContainsKey(algorithm))
1210 algorithmObject = CryptoConfig.CreateFromName(algorithm);
1212 catch (TargetInvocationException)
1214 algorithmDelegateDictionary[algorithm] = null;
1217 if (algorithmObject == null)
1219 algorithmDelegateDictionary[algorithm] = null;
1223 defaultObject = GetDefaultAlgorithm(algorithm);
1224 if ((!SecurityUtils.RequiresFipsCompliance && algorithmObject is SHA1CryptoServiceProvider)
1225 || (defaultObject != null && defaultObject.GetType() == algorithmObject.GetType()))
1227 algorithmDelegateDictionary[algorithm] = null;
1232 // Create a factory delegate which returns new instances of the algorithm type for later calls.
1233 Type algorithmType = algorithmObject.GetType();
1234 System.Linq.Expressions.NewExpression algorithmCreationExpression = System.Linq.Expressions.Expression.New(algorithmType);
1235 System.Linq.Expressions.LambdaExpression creationFunction = System.Linq.Expressions.Expression.Lambda<Func<object>>(algorithmCreationExpression);
1236 delegateFunction = creationFunction.Compile() as Func<object>;
1238 if (delegateFunction != null)
1240 algorithmDelegateDictionary[algorithm] = delegateFunction;
1242 return algorithmObject;
1250 if (delegateFunction != null)
1252 return delegateFunction.Invoke();
1257 // This is a fallback in case CryptoConfig fails to return a valid
1258 // algorithm object. CrytoConfig does not understand all the uri's and
1259 // can return a null in that case, in which case it is our responsibility
1260 // to fallback and create the right algorithm if it is a uri we understand
1265 case SecurityAlgorithms.Sha256Digest:
1266 if (SecurityUtils.RequiresFipsCompliance)
1268 return new SHA256CryptoServiceProvider();
1272 return new SHA256Managed();
1274 case SecurityAlgorithms.Sha1Digest:
1275 if (SecurityUtils.RequiresFipsCompliance)
1277 return new SHA1CryptoServiceProvider();
1281 return new SHA1Managed();
1283 case SecurityAlgorithms.HmacSha1Signature:
1284 return new HMACSHA1(GenerateRandomBytes(64),
1285 !SecurityUtils.RequiresFipsCompliance /* indicates the managed version of the algortithm */ );
1293 public static void ResetAllCertificates(X509Certificate2Collection certificates)
1295 if (certificates != null)
1297 for (int i = 0; i < certificates.Count; ++i)
1299 certificates[i].Reset();