Unable to decrypt key (forbidden response) when using ITFoxTec SAML2 and Azure KeyVault

105 Views Asked by At

We are using the ITfoxtec Identity SAML 2.0 package to implement our SSO integration with an external IdP (integration with the dansih KOMBIT systems). Implemented as MVC in .NET 6.

Starting from the sample https://github.com/ITfoxtec/ITfoxtec.Identity.Saml2/tree/master/test/TestWebAppCoreAzureKeyVault, we have implemented use of Azure KeyVault for the cryptographic operations.

After upgrading our certificate, our issued certificate no longer has the "DataEncipherment" flag for keyusage. "Only" the flag for "KeyEncipherment". We are now getting a forbidden response from Azure Keyvault when receiving and decrypting SAML responses from the IdP. Root CA for certificate is "Den Danske Stat OCES rod-CA".

For context, our SAML2Configuration is as follows:

  var saml2Configuration = new Saml2Configuration
  {
     Issuer = _config.Issuer,
     SignatureAlgorithm = _config.SignatureAlgorithm,
     CertificateValidationMode = _config.CertificateValidationMode,
     RevocationMode = _config.RevocationMode
  };

  var certificateClient = new CertificateClient(new Uri(AzureKeyVaultBaseUrl), _tokenCredential);
  var certificateWithPolicy = certificateClient.GetCertificate(AzureKeyVaultCertificateName);

  var publicCertificate = new X509Certificate2(certificateWithPolicy.Value.Cer);
  var rsa = RSAFactory.Create(_tokenCredential, certificateWithPolicy.Value.KeyId, 
     new Azure.Security.KeyVault.Keys.JsonWebKey(publicCertificate.GetRSAPublicKey()));

  saml2Configuration.SigningCertificate = new Saml2X509Certificate(publicCertificate, rsa);
  saml2Configuration.DecryptionCertificates = new List<X509Certificate2>()
     {new Saml2X509Certificate(publicCertificate, rsa)};

  saml2Configuration.SignAuthnRequest = true;

And our callback endpoint reading the SAML response:

     var binding = new Saml2PostBinding();
     var saml2AuthResponse = new Saml2AuthnResponse(_saml2Configuration);
     var httpRequest = Request.ToGenericHttpRequest();

     binding.ReadSamlResponse(httpRequest, saml2AuthResponse);

Where ReadSamlResponse throws:

System.Security.Cryptography.CryptographicException: Error calling Key Vault
 ---> Azure.RequestFailedException: Operation decrypt is not permitted on this key.
Status: 403 (Forbidden)
ErrorCode: Forbidden


Content:
{\"error\":{\"code\":\"Forbidden\",\"message\":\"Operation decrypt is not permitted on this key.\",\"innererror\":{\"code\":\"KeyOperationForbidden\"}}}


   at Azure.Security.KeyVault.KeyVaultPipeline.SendRequest(Request request, CancellationToken cancellationToken)
   at Azure.Security.KeyVault.KeyVaultPipeline.SendRequest[TContent,TResult](RequestMethod method, TContent content, Func`1 resultFactory, CancellationToken cancellationToken, String[] path)
   at Azure.Security.KeyVault.Keys.Cryptography.RemoteCryptographyClient.Decrypt(DecryptParameters parameters, CancellationToken cancellationToken)
   at Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.Decrypt(DecryptParameters decryptParameters, CancellationToken cancellationToken)
   at Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.Decrypt(EncryptionAlgorithm algorithm, Byte[] ciphertext, CancellationToken cancellationToken)
   at RSAKeyVaultProvider.KeyVaultContext.DecryptData(Byte[] cipherText, RSAEncryptionPadding padding) in /_/RSAKeyVaultProvider/KeyVaultContext.cs:line 104
   at RSAKeyVaultProvider.RSAKeyVault.Decrypt(Byte[] data, RSAEncryptionPadding padding) in /_/RSAKeyVaultProvider/RSAKeyVault.cs:line 87
   --- End of inner exception stack trace ---
   at RSAKeyVaultProvider.RSAKeyVault.Decrypt(Byte[] data, RSAEncryptionPadding padding) in /_/RSAKeyVaultProvider/RSAKeyVault.cs:line 91
   at System.Security.Cryptography.RSAOAEPKeyExchangeDeformatter.DecryptKeyExchange(Byte[] rgbData)
   at System.Security.Cryptography.Xml.EncryptedXml.DecryptKey(Byte[] keyData, RSA rsa, Boolean useOAEP)
   at ITfoxtec.Identity.Saml2.Cryptography.Saml2EncryptedXml.DecryptEncryptedKey(EncryptedKey encryptedKey)
   at System.Security.Cryptography.Xml.EncryptedXml.GetDecryptionKey(EncryptedData encryptedData, String symmetricAlgorithmUri)
   at System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument()
   at ITfoxtec.Identity.Saml2.Saml2AuthnResponse.DecryptMessage()
   at ITfoxtec.Identity.Saml2.Saml2Request.Read(String xml, Boolean validateXmlSignature, Boolean detectReplayedTokens)
   at ITfoxtec.Identity.Saml2.Saml2Response.Read(String xml, Boolean validateXmlSignature, Boolean detectReplayedTokens)
   at ITfoxtec.Identity.Saml2.Saml2AuthnResponse.Read(String xml, Boolean validateXmlSignature, Boolean detectReplayedTokens)
   at ITfoxtec.Identity.Saml2.Saml2PostBinding.Read(HttpRequest request, Saml2Request saml2RequestResponse, String messageName, Boolean validateXmlSignature, Boolean detectReplayedTokens)
   at ITfoxtec.Identity.Saml2.Saml2Binding`1.ReadSamlResponse(HttpRequest request, Saml2Response saml2Response)
   at "our code"

What I tried: We tried debugging the code and can conclude (as the stacktrace also says) that ITfoxtec.Identity.Saml2.Cryptography.Saml2EncryptedXml.DecryptEncryptedKey ends up calling RSAKeyVaultProvider.KeyVaultContext.DecryptData which results in an API call to Azure KeyVault which then gives a forbidden response: Azure.RequestFailedException: Operation decrypt is not permitted on this key.

We tried a workaround, where we change the implementation of DecryptData in KeyVaultContext from RSAKeyVaultProvider to perform an "UnwrapKey" API call instead of a "Decrypt". This allows us to decrypt the key i Azure Keyvault and everything works as before updating our certificate. We are however, not very much in favor of having to change the implementation in a 3rd party library to what in other cases would be unexpected behaviour.

What I expected Perhaps naively, I expected to be able to continue the implementation with Azure KeyVault with the Saml2X509Certificate constructor accepting an RSA:

  saml2Configuration.SigningCertificate = new Saml2X509Certificate(publicCertificate, rsa);
  saml2Configuration.DecryptionCertificates = new List<X509Certificate2>()
     {new Saml2X509Certificate(publicCertificate, rsa)};

However, looking at the RSA class from System.Security.Cryptography (which the Saml2X509Certificate ctor accepts), there are no methods matching the "DecryptKey" operation. Only Decrypt.

Perhaps our use case with a certificate without the "DataEncipherment" flag is simply no longer possible following the sample TestWebAppCoreAzureKeyVault?

1

There are 1 best solutions below

1
On

The old OCES2 certificates had the key usage dataEncipherment but the new OCES3 do not. Instead OCES3 certificates only have KeyEncipherment, which OCES2 also had.

And you are correct with only KeyEncipherment present the only way in Key Vault is wrapKey and unwrapKey. I agree it do not look pretty...

I do not know way they have removed dataEnciphermens in OCES3 certificates.