| File: System\IdentityModel\Tokens\X509AsymmetricSecurityKey.cs | Web Access |
| Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.ServiceModel; namespace System.IdentityModel.Tokens { public class X509AsymmetricSecurityKey : AsymmetricSecurityKey { private X509Certificate2 _certificate; private AsymmetricAlgorithm _privateKey; private bool _privateKeyAvailabilityDetermined; private AsymmetricAlgorithm _publicKey; private bool _publicKeyAvailabilityDetermined; public X509AsymmetricSecurityKey(X509Certificate2 certificate) { _certificate = certificate ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(certificate)); } public override int KeySize { get { return PublicKey.KeySize; } } private AsymmetricAlgorithm PrivateKey { get { if (!_privateKeyAvailabilityDetermined) { lock (ThisLock) { _privateKey = _certificate.GetRSAPrivateKey(); if (_privateKey is not null) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Investigate if this code is still needed or if there's a way to do the same thing without RSACryptoServiceProvider RSACryptoServiceProvider rsaCsp = _privateKey as RSACryptoServiceProvider; // ProviderType == 1 is PROV_RSA_FULL provider type that only supports SHA1. Change it to PROV_RSA_AES=24 that supports SHA2 also. if (rsaCsp != null && rsaCsp.CspKeyContainerInfo.ProviderType == 1) { CspParameters csp = new CspParameters(); csp.ProviderType = 24; csp.KeyContainerName = rsaCsp.CspKeyContainerInfo.KeyContainerName; csp.KeyNumber = (int)rsaCsp.CspKeyContainerInfo.KeyNumber; if (rsaCsp.CspKeyContainerInfo.MachineKeyStore) { csp.Flags = CspProviderFlags.UseMachineKeyStore; } csp.Flags |= CspProviderFlags.UseExistingKey; _privateKey = new RSACryptoServiceProvider(csp); } } } else { _privateKey = _certificate.GetDSAPrivateKey(); _privateKey ??= _certificate.GetECDsaPrivateKey(); } if (_certificate.HasPrivateKey && _privateKey == null) { DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.PrivateKeyNotSupported)); } _privateKeyAvailabilityDetermined = true; } } return _privateKey; } } private AsymmetricAlgorithm PublicKey { get { if (!_publicKeyAvailabilityDetermined) { lock (ThisLock) { if (!_publicKeyAvailabilityDetermined) { _publicKey = _certificate.GetRSAPublicKey(); if (_publicKey == null) { // Need DSACertificateExtensions to support DSA certificate which is in netstandard2.1 and netcore2.0. As we target netstandard2.0, we don't // have access to DSACertificateExtensions DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.PublicKeyNotSupported)); } _publicKeyAvailabilityDetermined = true; } } } return _publicKey; } } private Object ThisLock { get; } = new Object(); public override byte[] DecryptKey(string algorithm, byte[] keyData) { throw ExceptionHelper.PlatformNotSupported(); } public override byte[] EncryptKey(string algorithm, byte[] keyData) { throw ExceptionHelper.PlatformNotSupported(); } public override AsymmetricAlgorithm GetAsymmetricAlgorithm(string algorithm, bool privateKey) { if (privateKey) { if (PrivateKey == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.MissingPrivateKey)); } if (string.IsNullOrEmpty(algorithm)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SRP.Format(SRP.EmptyOrNullArgumentString, nameof(algorithm))); } switch (algorithm) { case SignedXml.XmlDsigDSAUrl: if ((PrivateKey as DSA) != null) { return (PrivateKey as DSA); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.AlgorithmAndPrivateKeyMisMatch)); case SignedXml.XmlDsigRSASHA1Url: case SecurityAlgorithms.RsaSha256Signature: case EncryptedXml.XmlEncRSA15Url: case EncryptedXml.XmlEncRSAOAEPUrl: if ((PrivateKey as RSA) != null) { return (PrivateKey as RSA); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.AlgorithmAndPrivateKeyMisMatch)); default: if (IsSupportedAlgorithm(algorithm)) { return PrivateKey; } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.Format(SRP.UnsupportedCryptoAlgorithm, algorithm))); } } } else { switch (algorithm) { case SignedXml.XmlDsigDSAUrl: if (PublicKey is DSA dsaPrivateKey) { return dsaPrivateKey; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.AlgorithmAndPublicKeyMisMatch)); case SignedXml.XmlDsigRSASHA1Url: case SecurityAlgorithms.RsaSha256Signature: case EncryptedXml.XmlEncRSA15Url: case EncryptedXml.XmlEncRSAOAEPUrl: if ((PublicKey as RSA) != null) { return (PublicKey as RSA); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.AlgorithmAndPublicKeyMisMatch)); default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.Format(SRP.UnsupportedCryptoAlgorithm, algorithm))); } } } public override HashAlgorithm GetHashAlgorithmForSignature(string algorithm) { if (string.IsNullOrEmpty(algorithm)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SRP.Format(SRP.EmptyOrNullArgumentString, nameof(algorithm))); } object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm); if (algorithmObject != null) { SignatureDescription description = algorithmObject as SignatureDescription; if (description != null) { return description.CreateDigest(); } HashAlgorithm hashAlgorithm = algorithmObject as HashAlgorithm; if (hashAlgorithm != null) { return hashAlgorithm; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SRP.Format(SRP.UnsupportedAlgorithmForCryptoOperation, algorithm, "CreateDigest"))); } switch (algorithm) { case SignedXml.XmlDsigDSAUrl: case SignedXml.XmlDsigRSASHA1Url: return CryptoHelper.NewSha1HashAlgorithm(); case SecurityAlgorithms.RsaSha256Signature: return CryptoHelper.NewSha256HashAlgorithm(); default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.Format(SRP.UnsupportedCryptoAlgorithm, algorithm))); } } public override AsymmetricSignatureDeformatter GetSignatureDeformatter(string algorithm) { // We support one of the two algoritms, but not both. // XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; // XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; if (string.IsNullOrEmpty(algorithm)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SRP.Format(SRP.EmptyOrNullArgumentString, nameof(algorithm))); } object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm); if (algorithmObject != null) { SignatureDescription description = algorithmObject as SignatureDescription; if (description != null) { return description.CreateDeformatter(PublicKey); } try { AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = algorithmObject as AsymmetricSignatureDeformatter; if (asymmetricSignatureDeformatter != null) { asymmetricSignatureDeformatter.SetKey(PublicKey); return asymmetricSignatureDeformatter; } } catch (InvalidCastException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.AlgorithmAndPublicKeyMisMatch, e)); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SRP.Format(SRP.UnsupportedAlgorithmForCryptoOperation, algorithm, nameof(GetSignatureDeformatter)))); } switch (algorithm) { case SignedXml.XmlDsigDSAUrl: // Ensure that we have a DSA algorithm object. DSA dsa = (PublicKey as DSA); if (dsa == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.PublicKeyNotDSA)); } return new DSASignatureDeformatter(dsa); case SignedXml.XmlDsigRSASHA1Url: case SecurityAlgorithms.RsaSha256Signature: // Ensure that we have an RSA algorithm object. RSA rsa = (PublicKey as RSA); if (rsa == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.PublicKeyNotRSA)); } return new RSAPKCS1SignatureDeformatter(rsa); default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.Format(SRP.UnsupportedCryptoAlgorithm, algorithm))); } } public override AsymmetricSignatureFormatter GetSignatureFormatter(string algorithm) { // One can sign only if the private key is present. if (PrivateKey == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.MissingPrivateKey)); } if (string.IsNullOrEmpty(algorithm)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SRP.Format(SRP.EmptyOrNullArgumentString, nameof(algorithm))); } // We support: // XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; // XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; // RsaSha256Signature = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; AsymmetricAlgorithm privateKey = PrivateKey; object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm); if (algorithmObject != null) { SignatureDescription description = algorithmObject as SignatureDescription; if (description != null) { return description.CreateFormatter(privateKey); } try { AsymmetricSignatureFormatter asymmetricSignatureFormatter = algorithmObject as AsymmetricSignatureFormatter; if (asymmetricSignatureFormatter != null) { asymmetricSignatureFormatter.SetKey(privateKey); return asymmetricSignatureFormatter; } } catch (InvalidCastException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.AlgorithmAndPrivateKeyMisMatch, e)); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SRP.Format(SRP.UnsupportedAlgorithmForCryptoOperation, algorithm, nameof(GetSignatureFormatter)))); } switch (algorithm) { case SignedXml.XmlDsigDSAUrl: // Ensure that we have a DSA algorithm object. DSA dsa = (PrivateKey as DSA); if (dsa == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.PrivateKeyNotDSA)); } #pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms return new DSASignatureFormatter(dsa); #pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms case SignedXml.XmlDsigRSASHA1Url: // Ensure that we have an RSA algorithm object. RSA rsa = (PrivateKey as RSA); if (rsa == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.PrivateKeyNotRSA)); } return new RSAPKCS1SignatureFormatter(rsa); case SecurityAlgorithms.RsaSha256Signature: // Ensure that we have an RSA algorithm object. RSA rsaSha256 = (privateKey as RSA); if (rsaSha256 == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.PrivateKeyNotRSA)); } return new RSAPKCS1SignatureFormatter(rsaSha256); default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SRP.Format(SRP.UnsupportedCryptoAlgorithm, algorithm))); } } public override bool HasPrivateKey() { return (PrivateKey != null); } public override bool IsAsymmetricAlgorithm(string algorithm) { if (string.IsNullOrEmpty(algorithm)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SRP.Format(SRP.EmptyOrNullArgumentString, nameof(algorithm))); } return (CryptoHelper.IsAsymmetricAlgorithm(algorithm)); } public override bool IsSupportedAlgorithm(string algorithm) { if (string.IsNullOrEmpty(algorithm)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SRP.Format(SRP.EmptyOrNullArgumentString, nameof(algorithm))); } object algorithmObject = null; try { algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm); } catch (InvalidOperationException) { algorithm = null; } if (algorithmObject != null) { SignatureDescription signatureDescription = algorithmObject as SignatureDescription; if (signatureDescription != null) { return true; } AsymmetricAlgorithm asymmetricAlgorithm = algorithmObject as AsymmetricAlgorithm; if (asymmetricAlgorithm != null) { return true; } return false; } switch (algorithm) { case SignedXml.XmlDsigDSAUrl: return (PublicKey is DSA); case SignedXml.XmlDsigRSASHA1Url: case SecurityAlgorithms.RsaSha256Signature: case EncryptedXml.XmlEncRSA15Url: case EncryptedXml.XmlEncRSAOAEPUrl: return (PublicKey is RSA); default: return false; } } public override bool IsSymmetricAlgorithm(string algorithm) { return CryptoHelper.IsSymmetricAlgorithm(algorithm); } } }