94b2861243
Former-commit-id: 5f9c6ae75f295e057a7d2971f3a6df4656fa8850
477 lines
20 KiB
C#
477 lines
20 KiB
C#
|
|
//------------------------------------------------------------
|
|
// 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;
|
|
PublicKey publicKey;
|
|
|
|
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.Key.KeySize; }
|
|
}
|
|
|
|
AsymmetricAlgorithm PrivateKey
|
|
{
|
|
get
|
|
{
|
|
if (!this.privateKeyAvailabilityDetermined)
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (!this.privateKeyAvailabilityDetermined)
|
|
{
|
|
this.privateKey = this.certificate.PrivateKey;
|
|
this.privateKeyAvailabilityDetermined = true;
|
|
}
|
|
}
|
|
}
|
|
return this.privateKey;
|
|
}
|
|
}
|
|
|
|
PublicKey PublicKey
|
|
{
|
|
get
|
|
{
|
|
if (this.publicKey == null)
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (this.publicKey == null)
|
|
{
|
|
this.publicKey = this.certificate.PublicKey;
|
|
}
|
|
}
|
|
}
|
|
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.Key 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.Key as DSA) != null)
|
|
{
|
|
return (this.PublicKey.Key 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.Key as RSA) != null)
|
|
{
|
|
return (this.PublicKey.Key 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.Key);
|
|
|
|
try
|
|
{
|
|
AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = algorithmObject as AsymmetricSignatureDeformatter;
|
|
if (asymmetricSignatureDeformatter != null)
|
|
{
|
|
asymmetricSignatureDeformatter.SetKey(this.PublicKey.Key);
|
|
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.Key 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.Key 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.Key is DSA);
|
|
|
|
case SignedXml.XmlDsigRSASHA1Url:
|
|
case SecurityAlgorithms.RsaSha256Signature:
|
|
case EncryptedXml.XmlEncRSA15Url:
|
|
case EncryptedXml.XmlEncRSAOAEPUrl:
|
|
return (this.PublicKey.Key is RSA);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override bool IsSymmetricAlgorithm(string algorithm)
|
|
{
|
|
return CryptoHelper.IsSymmetricAlgorithm(algorithm);
|
|
}
|
|
}
|
|
}
|