1 
2 //------------------------------------------------------------
3 // Copyright (c) Microsoft Corporation.  All rights reserved.
4 //------------------------------------------------------------
5 
6 namespace System.IdentityModel.Tokens
7 {
8     using System.Security.Cryptography;
9     using System.Security.Cryptography.X509Certificates;
10     using System.Security.Cryptography.Xml;
11 
12     public class X509AsymmetricSecurityKey : AsymmetricSecurityKey
13     {
14         X509Certificate2 certificate;
15         AsymmetricAlgorithm privateKey;
16         bool privateKeyAvailabilityDetermined;
17         AsymmetricAlgorithm publicKey;
18         bool publicKeyAvailabilityDetermined;
19 
20         object thisLock = new Object();
21 
X509AsymmetricSecurityKey(X509Certificate2 certificate)22         public X509AsymmetricSecurityKey(X509Certificate2 certificate)
23         {
24             if (certificate == null)
25                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
26 
27             this.certificate = certificate;
28         }
29 
30         public override int KeySize
31         {
32             get { return this.PublicKey.KeySize; }
33         }
34 
35         AsymmetricAlgorithm PrivateKey
36         {
37             get
38             {
39                 if (!this.privateKeyAvailabilityDetermined)
40                 {
41                     lock (ThisLock)
42                     {
43                         if (LocalAppContextSwitches.DisableCngCertificates)
44                         {
45                             this.privateKey = this.certificate.PrivateKey;
46                         }
47                         else
48                         {
49                             this.privateKey = CngLightup.GetRSAPrivateKey(this.certificate);
50                             if (this.privateKey != null)
51                             {
52                                 RSACryptoServiceProvider rsaCsp = this.privateKey as RSACryptoServiceProvider;
53                                 // ProviderType == 1 is PROV_RSA_FULL provider type that only supports SHA1. Change it to PROV_RSA_AES=24 that supports SHA2 also.
54                                 if (rsaCsp != null && rsaCsp.CspKeyContainerInfo.ProviderType == 1)
55                                 {
56                                     CspParameters csp = new CspParameters();
57                                     csp.ProviderType = 24;
58                                     csp.KeyContainerName = rsaCsp.CspKeyContainerInfo.KeyContainerName;
59                                     csp.KeyNumber = (int)rsaCsp.CspKeyContainerInfo.KeyNumber;
60                                     if (rsaCsp.CspKeyContainerInfo.MachineKeyStore)
61                                         csp.Flags = CspProviderFlags.UseMachineKeyStore;
62 
63                                     csp.Flags |= CspProviderFlags.UseExistingKey;
64                                     this.privateKey = new RSACryptoServiceProvider(csp);
65                                 }
66                             }
67                             else
68                             {
69                                 this.privateKey = CngLightup.GetDSAPrivateKey(this.certificate);
70                             }
71                             if (certificate.HasPrivateKey && this.privateKey == null)
72                                 DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotSupported)));
73                         }
74                         this.privateKeyAvailabilityDetermined = true;
75                     }
76                 }
77                 return this.privateKey;
78             }
79         }
80 
81         AsymmetricAlgorithm PublicKey
82         {
83             get
84             {
85                 if (!this.publicKeyAvailabilityDetermined)
86                 {
87                     lock (ThisLock)
88                     {
89                         if (!this.publicKeyAvailabilityDetermined)
90                         {
91                             if (LocalAppContextSwitches.DisableCngCertificates)
92                             {
93                                 this.publicKey = this.certificate.PublicKey.Key;
94                             }
95                             else
96                             {
97                                 this.publicKey = CngLightup.GetRSAPublicKey(this.certificate);
98                                 if (this.publicKey == null)
99                                     this.publicKey = CngLightup.GetDSAPublicKey(this.certificate);
100                                 if (this.publicKey == null)
101                                     DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PublicKeyNotSupported)));
102                             }
103                             this.publicKeyAvailabilityDetermined = true;
104                         }
105                     }
106                 }
107                 return this.publicKey;
108             }
109         }
110 
111         Object ThisLock
112         {
113             get
114             {
115                 return thisLock;
116             }
117         }
118 
DecryptKey(string algorithm, byte[] keyData)119         public override byte[] DecryptKey(string algorithm, byte[] keyData)
120         {
121             // We can decrypt key only if we have the private key in the certificate.
122             if (this.PrivateKey == null)
123             {
124                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.MissingPrivateKey)));
125             }
126 
127             RSA rsa = this.PrivateKey as RSA;
128             if (rsa == null)
129             {
130                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotRSA)));
131             }
132 
133             // Support exchange keySpec, AT_EXCHANGE ?
134             if (rsa.KeyExchangeAlgorithm == null)
135             {
136                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyExchangeNotSupported)));
137             }
138 
139             switch (algorithm)
140             {
141                 case EncryptedXml.XmlEncRSA15Url:
142                     return EncryptedXml.DecryptKey(keyData, rsa, false);
143 
144                 case EncryptedXml.XmlEncRSAOAEPUrl:
145                     return EncryptedXml.DecryptKey(keyData, rsa, true);
146 
147                 default:
148                     if (IsSupportedAlgorithm(algorithm))
149                         return EncryptedXml.DecryptKey(keyData, rsa, true);
150 
151                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
152             }
153         }
154 
EncryptKey(string algorithm, byte[] keyData)155         public override byte[] EncryptKey(string algorithm, byte[] keyData)
156         {
157             // Ensure that we have an RSA algorithm object
158             RSA rsa = this.PublicKey as RSA;
159             if (rsa == null)
160             {
161                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PublicKeyNotRSA)));
162             }
163 
164             switch (algorithm)
165             {
166                 case EncryptedXml.XmlEncRSA15Url:
167                     return EncryptedXml.EncryptKey(keyData, rsa, false);
168 
169                 case EncryptedXml.XmlEncRSAOAEPUrl:
170                     return EncryptedXml.EncryptKey(keyData, rsa, true);
171 
172                 default:
173                     if (IsSupportedAlgorithm(algorithm))
174                         return EncryptedXml.EncryptKey(keyData, rsa, true);
175 
176                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
177             }
178         }
179 
GetAsymmetricAlgorithm(string algorithm, bool privateKey)180         public override AsymmetricAlgorithm GetAsymmetricAlgorithm(string algorithm, bool privateKey)
181         {
182             if (privateKey)
183             {
184                 if (this.PrivateKey == null)
185                 {
186                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.MissingPrivateKey)));
187                 }
188 
189                 if (string.IsNullOrEmpty(algorithm))
190                 {
191                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
192                 }
193 
194                 switch (algorithm)
195                 {
196                     case SignedXml.XmlDsigDSAUrl:
197                         if ((this.PrivateKey as DSA) != null)
198                         {
199                             return (this.PrivateKey as DSA);
200                         }
201                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPrivateKeyMisMatch)));
202 
203                     case SignedXml.XmlDsigRSASHA1Url:
204                     case SecurityAlgorithms.RsaSha256Signature:
205                     case EncryptedXml.XmlEncRSA15Url:
206                     case EncryptedXml.XmlEncRSAOAEPUrl:
207                         if ((this.PrivateKey as RSA) != null)
208                         {
209                             return (this.PrivateKey as RSA);
210                         }
211                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPrivateKeyMisMatch)));
212                     default:
213                         if (IsSupportedAlgorithm(algorithm))
214                             return this.PrivateKey;
215                         else
216                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
217                 }
218             }
219             else
220             {
221                 switch (algorithm)
222                 {
223                     case SignedXml.XmlDsigDSAUrl:
224                         if ((this.PublicKey as DSA) != null)
225                         {
226                             return (this.PublicKey as DSA);
227                         }
228                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPublicKeyMisMatch)));
229                     case SignedXml.XmlDsigRSASHA1Url:
230                     case SecurityAlgorithms.RsaSha256Signature:
231                     case EncryptedXml.XmlEncRSA15Url:
232                     case EncryptedXml.XmlEncRSAOAEPUrl:
233                         if ((this.PublicKey as RSA) != null)
234                         {
235                             return (this.PublicKey as RSA);
236                         }
237                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPublicKeyMisMatch)));
238                     default:
239 
240                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
241                 }
242             }
243         }
244 
GetHashAlgorithmForSignature(string algorithm)245         public override HashAlgorithm GetHashAlgorithmForSignature(string algorithm)
246         {
247             if (string.IsNullOrEmpty(algorithm))
248             {
249                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
250             }
251 
252             object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm);
253 
254             if (algorithmObject != null)
255             {
256                 SignatureDescription description = algorithmObject as SignatureDescription;
257                 if (description != null)
258                     return description.CreateDigest();
259 
260                 HashAlgorithm hashAlgorithm = algorithmObject as HashAlgorithm;
261                 if (hashAlgorithm != null)
262                     return hashAlgorithm;
263 
264                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.UnsupportedAlgorithmForCryptoOperation,
265                         algorithm, "CreateDigest")));
266             }
267 
268             switch (algorithm)
269             {
270                 case SignedXml.XmlDsigDSAUrl:
271                 case SignedXml.XmlDsigRSASHA1Url:
272                     return CryptoHelper.NewSha1HashAlgorithm();
273                 case SecurityAlgorithms.RsaSha256Signature:
274                     return CryptoHelper.NewSha256HashAlgorithm();
275                 default:
276                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
277             }
278         }
279 
GetSignatureDeformatter(string algorithm)280         public override AsymmetricSignatureDeformatter GetSignatureDeformatter(string algorithm)
281         {
282 
283             // We support one of the two algoritms, but not both.
284             //     XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
285             //     XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
286 
287             if (string.IsNullOrEmpty(algorithm))
288             {
289                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
290             }
291 
292             object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm);
293             if (algorithmObject != null)
294             {
295                 SignatureDescription description = algorithmObject as SignatureDescription;
296                 if (description != null)
297                     return description.CreateDeformatter(this.PublicKey);
298 
299                 try
300                 {
301                     AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = algorithmObject as AsymmetricSignatureDeformatter;
302                     if (asymmetricSignatureDeformatter != null)
303                     {
304                         asymmetricSignatureDeformatter.SetKey(this.PublicKey);
305                         return asymmetricSignatureDeformatter;
306                     }
307                 }
308                 catch (InvalidCastException e)
309                 {
310                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPublicKeyMisMatch), e));
311                 }
312 
313                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.UnsupportedAlgorithmForCryptoOperation,
314                        algorithm, "GetSignatureDeformatter")));
315             }
316 
317             switch (algorithm)
318             {
319                 case SignedXml.XmlDsigDSAUrl:
320 
321                     // Ensure that we have a DSA algorithm object.
322                     DSA dsa = (this.PublicKey as DSA);
323                     if (dsa == null)
324                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PublicKeyNotDSA)));
325                     return new DSASignatureDeformatter(dsa);
326 
327                 case SignedXml.XmlDsigRSASHA1Url:
328                 case SecurityAlgorithms.RsaSha256Signature:
329                     // Ensure that we have an RSA algorithm object.
330                     RSA rsa = (this.PublicKey as RSA);
331                     if (rsa == null)
332                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PublicKeyNotRSA)));
333                     return new RSAPKCS1SignatureDeformatter(rsa);
334 
335                 default:
336                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
337             }
338         }
339 
GetSignatureFormatter(string algorithm)340         public override AsymmetricSignatureFormatter GetSignatureFormatter(string algorithm)
341         {
342             // One can sign only if the private key is present.
343             if (this.PrivateKey == null)
344             {
345                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.MissingPrivateKey)));
346             }
347 
348             if (string.IsNullOrEmpty(algorithm))
349             {
350                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
351             }
352 
353             // We support:
354             //     XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
355             //     XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
356             //     RsaSha256Signature = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
357             AsymmetricAlgorithm privateKey = LevelUpRsa(this.PrivateKey, algorithm);
358 
359             object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm);
360             if (algorithmObject != null)
361             {
362                 SignatureDescription description = algorithmObject as SignatureDescription;
363                 if (description != null)
364                     return description.CreateFormatter(privateKey);
365 
366                 try
367                 {
368                     AsymmetricSignatureFormatter asymmetricSignatureFormatter = algorithmObject as AsymmetricSignatureFormatter;
369                     if (asymmetricSignatureFormatter != null)
370                     {
371                         asymmetricSignatureFormatter.SetKey(privateKey);
372                         return asymmetricSignatureFormatter;
373                     }
374                 }
375                 catch (InvalidCastException e)
376                 {
377                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPrivateKeyMisMatch), e));
378                 }
379 
380                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.UnsupportedAlgorithmForCryptoOperation,
381                        algorithm, "GetSignatureFormatter")));
382             }
383 
384             switch (algorithm)
385             {
386                 case SignedXml.XmlDsigDSAUrl:
387 
388                     // Ensure that we have a DSA algorithm object.
389                     DSA dsa = (this.PrivateKey as DSA);
390                     if (dsa == null)
391                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotDSA)));
392                     return new DSASignatureFormatter(dsa);
393 
394                 case SignedXml.XmlDsigRSASHA1Url:
395                     // Ensure that we have an RSA algorithm object.
396                     RSA rsa = (this.PrivateKey as RSA);
397                     if (rsa == null)
398                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotRSA)));
399                     return new RSAPKCS1SignatureFormatter(rsa);
400 
401                 case SecurityAlgorithms.RsaSha256Signature:
402                     // Ensure that we have an RSA algorithm object.
403                     RSA rsaSha256 = (privateKey as RSA);
404                     if (rsaSha256 == null)
405                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotRSA)));
406                     return new RSAPKCS1SignatureFormatter(rsaSha256);
407 
408                 default:
409                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
410             }
411 
412         }
413 
LevelUpRsa(AsymmetricAlgorithm asymmetricAlgorithm, string algorithm)414         private static AsymmetricAlgorithm LevelUpRsa(AsymmetricAlgorithm asymmetricAlgorithm, string algorithm)
415         {
416             // If user turned off leveling up at app level, return
417             if (LocalAppContextSwitches.DisableUpdatingRsaProviderType)
418                 return asymmetricAlgorithm;
419 
420             if (asymmetricAlgorithm == null)
421                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("asymmetricAlgorithm"));
422 
423             if (string.IsNullOrEmpty(algorithm))
424                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
425 
426             // only level up if alg is sha256
427             if (!string.Equals(algorithm, SecurityAlgorithms.RsaSha256Signature))
428                 return asymmetricAlgorithm;
429 
430             RSACryptoServiceProvider rsaCsp = asymmetricAlgorithm as RSACryptoServiceProvider;
431             if (rsaCsp == null)
432                 return asymmetricAlgorithm;
433 
434             // ProviderType == 1(PROV_RSA_FULL) and providerType == 12(PROV_RSA_SCHANNEL) are provider types that only support SHA1. Change them to PROV_RSA_AES=24 that supports SHA2 also.
435             // Only levels up if the associated key is not a hardware key.
436             // Another provider type related to rsa, PROV_RSA_SIG == 2 that only supports Sha1 is no longer supported
437             if ((rsaCsp.CspKeyContainerInfo.ProviderType == 1 || rsaCsp.CspKeyContainerInfo.ProviderType == 12) && !rsaCsp.CspKeyContainerInfo.HardwareDevice)
438             {
439                 CspParameters csp = new CspParameters();
440                 csp.ProviderType = 24;
441                 csp.KeyContainerName = rsaCsp.CspKeyContainerInfo.KeyContainerName;
442                 csp.KeyNumber = (int)rsaCsp.CspKeyContainerInfo.KeyNumber;
443                 if (rsaCsp.CspKeyContainerInfo.MachineKeyStore)
444                     csp.Flags = CspProviderFlags.UseMachineKeyStore;
445 
446                 csp.Flags |= CspProviderFlags.UseExistingKey;
447                 return new RSACryptoServiceProvider(csp);
448             }
449 
450             return rsaCsp;
451         }
452 
HasPrivateKey()453         public override bool HasPrivateKey()
454         {
455             return (this.PrivateKey != null);
456         }
457 
IsAsymmetricAlgorithm(string algorithm)458         public override bool IsAsymmetricAlgorithm(string algorithm)
459         {
460             if (string.IsNullOrEmpty(algorithm))
461             {
462                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
463             }
464 
465             return (CryptoHelper.IsAsymmetricAlgorithm(algorithm));
466         }
467 
IsSupportedAlgorithm(string algorithm)468         public override bool IsSupportedAlgorithm(string algorithm)
469         {
470             if (string.IsNullOrEmpty(algorithm))
471             {
472                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
473             }
474 
475             object algorithmObject = null;
476             try
477             {
478                 algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm);
479             }
480             catch (InvalidOperationException)
481             {
482                 algorithm = null;
483             }
484 
485             if (algorithmObject != null)
486             {
487                 SignatureDescription signatureDescription = algorithmObject as SignatureDescription;
488                 if (signatureDescription != null)
489                     return true;
490                 AsymmetricAlgorithm asymmetricAlgorithm = algorithmObject as AsymmetricAlgorithm;
491                 if (asymmetricAlgorithm != null)
492                     return true;
493                 return false;
494             }
495 
496             switch (algorithm)
497             {
498                 case SignedXml.XmlDsigDSAUrl:
499                     return (this.PublicKey is DSA);
500 
501                 case SignedXml.XmlDsigRSASHA1Url:
502                 case SecurityAlgorithms.RsaSha256Signature:
503                 case EncryptedXml.XmlEncRSA15Url:
504                 case EncryptedXml.XmlEncRSAOAEPUrl:
505                     return (this.PublicKey is RSA);
506                 default:
507                     return false;
508             }
509         }
510 
IsSymmetricAlgorithm(string algorithm)511         public override bool IsSymmetricAlgorithm(string algorithm)
512         {
513             return CryptoHelper.IsSymmetricAlgorithm(algorithm);
514         }
515     }
516 }
517