2016-08-03 10:59:49 +00:00
//------------------------------------------------------------
// 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 ;
2017-08-21 15:34:15 +00:00
AsymmetricAlgorithm publicKey ;
bool publicKeyAvailabilityDetermined ;
2016-08-03 10:59:49 +00:00
object thisLock = new Object ( ) ;
public X509AsymmetricSecurityKey ( X509Certificate2 certificate )
{
if ( certificate = = null )
throw DiagnosticUtility . ExceptionUtility . ThrowHelperArgumentNull ( "certificate" ) ;
this . certificate = certificate ;
}
public override int KeySize
{
2017-08-21 15:34:15 +00:00
get { return this . PublicKey . KeySize ; }
2016-08-03 10:59:49 +00:00
}
AsymmetricAlgorithm PrivateKey
{
get
{
if ( ! this . privateKeyAvailabilityDetermined )
{
lock ( ThisLock )
{
2017-08-21 15:34:15 +00:00
if ( LocalAppContextSwitches . DisableCngCertificates )
2016-08-03 10:59:49 +00:00
{
this . privateKey = this . certificate . PrivateKey ;
}
2017-08-21 15:34:15 +00:00
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 ;
2016-08-03 10:59:49 +00:00
}
}
return this . privateKey ;
}
}
2017-08-21 15:34:15 +00:00
AsymmetricAlgorithm PublicKey
2016-08-03 10:59:49 +00:00
{
get
{
2017-08-21 15:34:15 +00:00
if ( ! this . publicKeyAvailabilityDetermined )
2016-08-03 10:59:49 +00:00
{
lock ( ThisLock )
{
2017-08-21 15:34:15 +00:00
if ( ! this . publicKeyAvailabilityDetermined )
2016-08-03 10:59:49 +00:00
{
2017-08-21 15:34:15 +00:00
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 ;
2016-08-03 10:59:49 +00:00
}
}
}
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
2017-08-21 15:34:15 +00:00
RSA rsa = this . PublicKey as RSA ;
2016-08-03 10:59:49 +00:00
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 :
2017-08-21 15:34:15 +00:00
if ( ( this . PublicKey as DSA ) ! = null )
2016-08-03 10:59:49 +00:00
{
2017-08-21 15:34:15 +00:00
return ( this . PublicKey as DSA ) ;
2016-08-03 10:59:49 +00:00
}
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new NotSupportedException ( SR . GetString ( SR . AlgorithmAndPublicKeyMisMatch ) ) ) ;
case SignedXml . XmlDsigRSASHA1Url :
case SecurityAlgorithms . RsaSha256Signature :
case EncryptedXml . XmlEncRSA15Url :
case EncryptedXml . XmlEncRSAOAEPUrl :
2017-08-21 15:34:15 +00:00
if ( ( this . PublicKey as RSA ) ! = null )
2016-08-03 10:59:49 +00:00
{
2017-08-21 15:34:15 +00:00
return ( this . PublicKey as RSA ) ;
2016-08-03 10:59:49 +00:00
}
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 )
2017-08-21 15:34:15 +00:00
return description . CreateDeformatter ( this . PublicKey ) ;
2016-08-03 10:59:49 +00:00
try
{
AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = algorithmObject as AsymmetricSignatureDeformatter ;
if ( asymmetricSignatureDeformatter ! = null )
{
2017-08-21 15:34:15 +00:00
asymmetricSignatureDeformatter . SetKey ( this . PublicKey ) ;
2016-08-03 10:59:49 +00:00
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.
2017-08-21 15:34:15 +00:00
DSA dsa = ( this . PublicKey as DSA ) ;
2016-08-03 10:59:49 +00:00
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.
2017-08-21 15:34:15 +00:00
RSA rsa = ( this . PublicKey as RSA ) ;
2016-08-03 10:59:49 +00:00
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" ) ) ;
}
2016-11-10 13:04:39 +00:00
// We support:
2016-08-03 10:59:49 +00:00
// XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
// XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
2016-11-10 13:04:39 +00:00
// RsaSha256Signature = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
AsymmetricAlgorithm privateKey = LevelUpRsa ( this . PrivateKey , algorithm ) ;
2016-08-03 10:59:49 +00:00
object algorithmObject = CryptoHelper . GetAlgorithmFromConfig ( algorithm ) ;
if ( algorithmObject ! = null )
{
SignatureDescription description = algorithmObject as SignatureDescription ;
if ( description ! = null )
2016-11-10 13:04:39 +00:00
return description . CreateFormatter ( privateKey ) ;
2016-08-03 10:59:49 +00:00
try
{
AsymmetricSignatureFormatter asymmetricSignatureFormatter = algorithmObject as AsymmetricSignatureFormatter ;
if ( asymmetricSignatureFormatter ! = null )
{
2016-11-10 13:04:39 +00:00
asymmetricSignatureFormatter . SetKey ( privateKey ) ;
2016-08-03 10:59:49 +00:00
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.
2016-11-10 13:04:39 +00:00
RSA rsaSha256 = ( privateKey as RSA ) ;
if ( rsaSha256 = = null )
2016-08-03 10:59:49 +00:00
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new NotSupportedException ( SR . GetString ( SR . PrivateKeyNotRSA ) ) ) ;
2016-11-10 13:04:39 +00:00
return new RSAPKCS1SignatureFormatter ( rsaSha256 ) ;
2016-08-03 10:59:49 +00:00
default :
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new NotSupportedException ( SR . GetString ( SR . UnsupportedCryptoAlgorithm , algorithm ) ) ) ;
}
}
2016-11-10 13:04:39 +00:00
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 ;
}
2016-08-03 10:59:49 +00:00
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 :
2017-08-21 15:34:15 +00:00
return ( this . PublicKey is DSA ) ;
2016-08-03 10:59:49 +00:00
case SignedXml . XmlDsigRSASHA1Url :
case SecurityAlgorithms . RsaSha256Signature :
case EncryptedXml . XmlEncRSA15Url :
case EncryptedXml . XmlEncRSAOAEPUrl :
2017-08-21 15:34:15 +00:00
return ( this . PublicKey is RSA ) ;
2016-08-03 10:59:49 +00:00
default :
return false ;
}
}
public override bool IsSymmetricAlgorithm ( string algorithm )
{
return CryptoHelper . IsSymmetricAlgorithm ( algorithm ) ;
}
}
}