|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.IdentityModel.Tokens
{
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
public class X509AsymmetricSecurityKey : AsymmetricSecurityKey
{
X509Certificate2 certificate;
AsymmetricAlgorithm privateKey;
bool privateKeyAvailabilityDetermined;
AsymmetricAlgorithm publicKey;
bool publicKeyAvailabilityDetermined;
object thisLock = new Object();
public X509AsymmetricSecurityKey(X509Certificate2 certificate)
{
if (certificate == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
this.certificate = certificate;
}
public override int KeySize
{
get { return this.PublicKey.KeySize; }
}
AsymmetricAlgorithm PrivateKey
{
get
{
if (!this.privateKeyAvailabilityDetermined)
{
lock (ThisLock)
{
if (LocalAppContextSwitches.DisableCngCertificates)
{
this.privateKey = this.certificate.PrivateKey;
}
else
{
this.privateKey = CngLightup.GetRSAPrivateKey(this.certificate);
if (this.privateKey != null)
{
RSACryptoServiceProvider rsaCsp = this.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;
this.privateKey = new RSACryptoServiceProvider(csp);
}
}
else
{
this.privateKey = CngLightup.GetDSAPrivateKey(this.certificate);
}
if (certificate.HasPrivateKey && this.privateKey == null)
DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotSupported)));
}
this.privateKeyAvailabilityDetermined = true;
}
}
return this.privateKey;
}
}
AsymmetricAlgorithm PublicKey
{
get
{
if (!this.publicKeyAvailabilityDetermined)
{
lock (ThisLock)
{
if (!this.publicKeyAvailabilityDetermined)
{
if (LocalAppContextSwitches.DisableCngCertificates)
{
this.publicKey = this.certificate.PublicKey.Key;
}
else
{
this.publicKey = CngLightup.GetRSAPublicKey(this.certificate);
if (this.publicKey == null)
this.publicKey = CngLightup.GetDSAPublicKey(this.certificate);
if (this.publicKey == null)
DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PublicKeyNotSupported)));
}
this.publicKeyAvailabilityDetermined = true;
}
}
}
return this.publicKey;
}
}
Object ThisLock
{
get
{
return thisLock;
}
}
public override byte[] DecryptKey(string algorithm, byte[] keyData)
{
// We can decrypt key only if we have the private key in the certificate.
if (this.PrivateKey == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.MissingPrivateKey)));
}
RSA rsa = this.PrivateKey as RSA;
if (rsa == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotRSA)));
}
// Support exchange keySpec, AT_EXCHANGE ?
if (rsa.KeyExchangeAlgorithm == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyExchangeNotSupported)));
}
switch (algorithm)
{
case EncryptedXml.XmlEncRSA15Url:
return EncryptedXml.DecryptKey(keyData, rsa, false);
case EncryptedXml.XmlEncRSAOAEPUrl:
return EncryptedXml.DecryptKey(keyData, rsa, true);
default:
if (IsSupportedAlgorithm(algorithm))
return EncryptedXml.DecryptKey(keyData, rsa, true);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
}
}
public override byte[] EncryptKey(string algorithm, byte[] keyData)
{
// Ensure that we have an RSA algorithm object
RSA rsa = this.PublicKey as RSA;
if (rsa == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PublicKeyNotRSA)));
}
switch (algorithm)
{
case EncryptedXml.XmlEncRSA15Url:
return EncryptedXml.EncryptKey(keyData, rsa, false);
case EncryptedXml.XmlEncRSAOAEPUrl:
return EncryptedXml.EncryptKey(keyData, rsa, true);
default:
if (IsSupportedAlgorithm(algorithm))
return EncryptedXml.EncryptKey(keyData, rsa, true);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
}
}
public override AsymmetricAlgorithm GetAsymmetricAlgorithm(string algorithm, bool privateKey)
{
if (privateKey)
{
if (this.PrivateKey == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.MissingPrivateKey)));
}
if (string.IsNullOrEmpty(algorithm))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
}
switch (algorithm)
{
case SignedXml.XmlDsigDSAUrl:
if ((this.PrivateKey as DSA) != null)
{
return (this.PrivateKey as DSA);
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPrivateKeyMisMatch)));
case SignedXml.XmlDsigRSASHA1Url:
case SecurityAlgorithms.RsaSha256Signature:
case EncryptedXml.XmlEncRSA15Url:
case EncryptedXml.XmlEncRSAOAEPUrl:
if ((this.PrivateKey as RSA) != null)
{
return (this.PrivateKey as RSA);
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPrivateKeyMisMatch)));
default:
if (IsSupportedAlgorithm(algorithm))
return this.PrivateKey;
else
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
}
}
else
{
switch (algorithm)
{
case SignedXml.XmlDsigDSAUrl:
if ((this.PublicKey as DSA) != null)
{
return (this.PublicKey as DSA);
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPublicKeyMisMatch)));
case SignedXml.XmlDsigRSASHA1Url:
case SecurityAlgorithms.RsaSha256Signature:
case EncryptedXml.XmlEncRSA15Url:
case EncryptedXml.XmlEncRSAOAEPUrl:
if ((this.PublicKey as RSA) != null)
{
return (this.PublicKey as RSA);
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPublicKeyMisMatch)));
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
}
}
}
public override HashAlgorithm GetHashAlgorithmForSignature(string algorithm)
{
if (string.IsNullOrEmpty(algorithm))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "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(SR.GetString(SR.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(SR.GetString(SR.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, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
}
object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm);
if (algorithmObject != null)
{
SignatureDescription description = algorithmObject as SignatureDescription;
if (description != null)
return description.CreateDeformatter(this.PublicKey);
try
{
AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = algorithmObject as AsymmetricSignatureDeformatter;
if (asymmetricSignatureDeformatter != null)
{
asymmetricSignatureDeformatter.SetKey(this.PublicKey);
return asymmetricSignatureDeformatter;
}
}
catch (InvalidCastException e)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.AlgorithmAndPublicKeyMisMatch), e));
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.UnsupportedAlgorithmForCryptoOperation,
algorithm, "GetSignatureDeformatter")));
}
switch (algorithm)
{
case SignedXml.XmlDsigDSAUrl:
// Ensure that we have a DSA algorithm object.
DSA dsa = (this.PublicKey as DSA);
if (dsa == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PublicKeyNotDSA)));
return new DSASignatureDeformatter(dsa);
case SignedXml.XmlDsigRSASHA1Url:
case SecurityAlgorithms.RsaSha256Signature:
// Ensure that we have an RSA algorithm object.
RSA rsa = (this.PublicKey as RSA);
if (rsa == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PublicKeyNotRSA)));
return new RSAPKCS1SignatureDeformatter(rsa);
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
}
}
public override AsymmetricSignatureFormatter GetSignatureFormatter(string algorithm)
{
// One can sign only if the private key is present.
if (this.PrivateKey == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.MissingPrivateKey)));
}
if (string.IsNullOrEmpty(algorithm))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "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 = LevelUpRsa(this.PrivateKey, algorithm);
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(SR.GetString(SR.AlgorithmAndPrivateKeyMisMatch), e));
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(SR.GetString(SR.UnsupportedAlgorithmForCryptoOperation,
algorithm, "GetSignatureFormatter")));
}
switch (algorithm)
{
case SignedXml.XmlDsigDSAUrl:
// Ensure that we have a DSA algorithm object.
DSA dsa = (this.PrivateKey as DSA);
if (dsa == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotDSA)));
return new DSASignatureFormatter(dsa);
case SignedXml.XmlDsigRSASHA1Url:
// Ensure that we have an RSA algorithm object.
RSA rsa = (this.PrivateKey as RSA);
if (rsa == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.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(SR.GetString(SR.PrivateKeyNotRSA)));
return new RSAPKCS1SignatureFormatter(rsaSha256);
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm)));
}
}
private static AsymmetricAlgorithm LevelUpRsa(AsymmetricAlgorithm asymmetricAlgorithm, string algorithm)
{
// If user turned off leveling up at app level, return
if (LocalAppContextSwitches.DisableUpdatingRsaProviderType)
return asymmetricAlgorithm;
if (asymmetricAlgorithm == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("asymmetricAlgorithm"));
if (string.IsNullOrEmpty(algorithm))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
// only level up if alg is sha256
if (!string.Equals(algorithm, SecurityAlgorithms.RsaSha256Signature))
return asymmetricAlgorithm;
RSACryptoServiceProvider rsaCsp = asymmetricAlgorithm as RSACryptoServiceProvider;
if (rsaCsp == null)
return asymmetricAlgorithm;
// 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.
// Only levels up if the associated key is not a hardware key.
// Another provider type related to rsa, PROV_RSA_SIG == 2 that only supports Sha1 is no longer supported
if ((rsaCsp.CspKeyContainerInfo.ProviderType == 1 || rsaCsp.CspKeyContainerInfo.ProviderType == 12) && !rsaCsp.CspKeyContainerInfo.HardwareDevice)
{
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;
return new RSACryptoServiceProvider(csp);
}
return rsaCsp;
}
public override bool HasPrivateKey()
{
return (this.PrivateKey != null);
}
public override bool IsAsymmetricAlgorithm(string algorithm)
{
if (string.IsNullOrEmpty(algorithm))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm"));
}
return (CryptoHelper.IsAsymmetricAlgorithm(algorithm));
}
public override bool IsSupportedAlgorithm(string algorithm)
{
if (string.IsNullOrEmpty(algorithm))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "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 (this.PublicKey is DSA);
case SignedXml.XmlDsigRSASHA1Url:
case SecurityAlgorithms.RsaSha256Signature:
case EncryptedXml.XmlEncRSA15Url:
case EncryptedXml.XmlEncRSAOAEPUrl:
return (this.PublicKey is RSA);
default:
return false;
}
}
public override bool IsSymmetricAlgorithm(string algorithm)
{
return CryptoHelper.IsSymmetricAlgorithm(algorithm);
}
}
}
|