-
Notifications
You must be signed in to change notification settings - Fork 401
Using a custom CryptoProvider
By default, CryptoProviderFactory provides support for many different algorithms (see here for a complete list), but in the case that you would like to sign and/or verify using an algorithm that we do not support by default, you will need to plug in a custom CryptoProvider.
This custom CryptoProvider must implement the ICryptoProvider interface, and can either be set on CryptoProviderFactory.Default or directly on a SecurityKey.
For example, here is a custom CryptoProvider that supports signing and verifying operations using the SHA1 algorithm:
public class Sha1Provider : ICryptoProvider
{
public object Create(string algorithm, params object[] args)
{
if (algorithm == SignedXml.XmlDsigRSASHA1Url)
return new Sha1SigProvider((SecurityKey)args[0], algorithm);
if (algorithm == SignedXml.XmlDsigSHA1Url)
return SHA1.Create();
return null;
}
public bool IsSupportedAlgorithm(string algorithm, params object[] args)
{
return new[] { SignedXml.XmlDsigRSASHA1Url, SignedXml.XmlDsigSHA1Url }.Contains(algorithm);
}
public void Release(object cryptoInstance)
{
}
}
public class Sha1SigProvider : SignatureProvider
{
private readonly RSA _puk;
private readonly RSA _prk;
public Sha1SigProvider(SecurityKey key, string algorithm) : base(key, algorithm)
{
if (!(key is X509SecurityKey x509Key)) return;
_puk = x509Key.PublicKey as RSA;
_prk = x509Key.PrivateKey as RSA;
}
public override byte[] Sign(byte[] input)
{
if (input == null || input.Length == 0)
throw new ArgumentNullException(nameof(input));
return _prk.SignData(input, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
}
public override bool Verify(byte[] input, byte[] signature)
{
if (input == null || input.Length == 0)
throw new ArgumentNullException(nameof(input));
if (signature == null || signature.Length == 0)
throw new ArgumentNullException(nameof(signature));
return _puk.VerifyData(input, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
}
protected override void Dispose(bool disposing)
{
//throw new NotImplementedException();
}
}
As mentioned earlier, you can set the custom CryptoProvider on CryptoProviderFactory.Default:
CryptoProviderFactory.Default.CustomCryptoProvider = new Sha1Provider();
Or alternatively you can set it on the SecurityKey being used:
var key = new X509SecurityKey(new X509Certificate2(new byte[]));
key.CryptoProviderFactory.CustomCryptoProvider = new Sha1Provider();
NOTE: Since you can only set one custom CryptoProvider, if you want to provide support for more than one cryptographic algorithm it may be necessary to create a wrapper class that will loop through a set of custom CryptoProviders.
This sample code was taken from: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/973
Conceptual Documentation
- Using TokenValidationParameters.ValidateIssuerSigningKey
- Scenarios
- Validating tokens
- Outbound policy claim type mapping
- How ASP.NET Core uses Microsoft.IdentityModel extensions for .NET
- Using a custom CryptoProvider
- SignedHttpRequest aka PoP (Proof-of-Possession)
- Creating and Validating JWEs (Json Web Encryptions)
- Caching in Microsoft.IdentityModel
- Resiliency on metadata refresh
- Use KeyVault extensions
- Signing key roll over