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