Problem:
We are implementing client certificate validation in Azure API Management (APIM) and encountering issues with certificate revocation checks. Specifically, APIM returns the following error for both valid and revoked certificates:
Message: The revocation function was unable to check revocation for the certificate.
ExceptionType: ClientCertificateRevocationCheckFailed
If we disable revocation validation (validate-revocation="false"
), the API requests work as expected.
Setup:
-
Added our root certificate to Azure API Management as a CA certificate (Root). This root certificate serves as the anchor of trust for the entire certificate chain.
-
Added our intermediate certificate to Azure API Management as a CA certificate (Intermediate Certificate). The intermediate certificate includes a Certificate Revocation List (CRL) URL. Both our intermediate certificate and client certificates have a Certificate Distribution Point (CDP) that points to the CRL. We have verified the CRL URLs and the revocation status of certificates using OpenSSL, ensuring that the CRL is accessible and correctly indicates revoked certificates.
-
Enabled the “Negotiate client certificate” option on the Self-Hosted Gateway under Custom Domains. This configuration ensures that the gateway will request and negotiate client certificates during the TLS handshake, allowing us to enforce client certificate validation policies.
-
Applied the following policy in APIM:
<policies>
<inbound>
<validate-client-certificate validate-revocation="true" validate-trust="true" validate-not-before="false" validate-not-after="false" ignore-error="false" />
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
- Tested API requests using both valid and revoked client certificates.
Troubleshooting:
We validated the certificates and CRL URLs using the following C# code, which correctly identifies revoked and non-revoked certificates:
using System;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public class CertificateValidator
{
private static readonly HttpClient httpClient = new HttpClient();
public static bool ValidateCertificate(X509Certificate2 clientCertificate)
{
if (clientCertificate == null)
{
throw new ArgumentNullException(nameof(clientCertificate));
}
// Check if the certificate is valid
if (DateTime.Now > clientCertificate.NotAfter || DateTime.Now < clientCertificate.NotBefore)
{
return false;
}
// Check if the certificate has been revoked
bool isRevoked = CheckCertificateRevocation(clientCertificate).Result;
return !isRevoked;
}
private static async Task<bool> CheckCertificateRevocation(X509Certificate2 certificate)
{
// Get the CRL distribution points from the certificate
foreach (X509Extension extension in certificate.Extensions)
{
if (extension is X509CrlDistributionPointsExtension crlDistributionPointsExtension)
{
foreach (var distributionPoint in crlDistributionPointsExtension.DistributionPoints)
{
if (await CheckRevocationList(distributionPoint.FullName, certificate))
{
return true; // Certificate is revoked
}
}
}
}
return false; // Certificate is not revoked
}
private static async Task<bool> CheckRevocationList(string crlUrl, X509Certificate2 certificate)
{
try
{
// Download the CRL from the distribution point
byte[] crlData = await httpClient.GetByteArrayAsync(crlUrl);
X509Crl2 crl = new X509Crl2(crlData);
// Check if the certificate serial number is in the CRL
foreach (var entry in crl.RevokedCertificates)
{
if (entry.SerialNumber == certificate.SerialNumber)
{
return true; // Certificate is revoked
}
}
}
catch
{
// Handle exceptions appropriately (e.g., log them)
}
return false; // Certificate is not revoked
}
}
// Example usage
public class Program
{
public static void Main()
{
// Load a client certificate from file
X509Certificate2 clientCertificate = new X509Certificate2("path/to/certificate.cer");
// Validate the certificate
bool isValid = CertificateValidator.ValidateCertificate(clientCertificate);
Console.WriteLine($"Certificate is valid: {isValid}");
}
}
Question:
How can we determine the root cause of the certificate revocation check failure in Azure API Management? Are there specific logs or diagnostic tools available that can help us troubleshoot this issue?