Azure App Service and Client Certificate Authentication

4 minute read

Azure App Services can make use of Client Certificate Authentication. The options for this are not available in the portal and need to be configured manually.

Activating Client Certificate Authentication

In the below blog post on the Azure documentation site is explained how you can configure your Azure Web App for client certificate authentication:

The same way can also be used for for example an Azure API App. The below option is a option for setting the same property in a different way.

  • Open https://resources.azure.com/ and login with the same credentials as the Azure Portal.
  • Find your site. It will be under subscriptions – [your subscription] – resouceGroups – [your resource group] – providers – Microsoft.Web – sites –[your site]

image

 

 

 

 

 

 

 

 

  • Make sure the interface is set on “Read\Write” mode.

image

  • Click the “Edit” button on the op of the screen.

image

 

 

 

  • Find the property “clientCertEnabled” and set it to “true”.

image

 

 

  • Click the “PUT” button on top to save your changes.

image

 

 

 

 

With the Azure resource configured you need to make sure that your application is able to use Client Certificate Authentication.

Preparing your Application

Keep in mind that your Azure resource does not validate your Client Certificate! It only requires you to supply a certificate when opening the application. That requires you to write your own logic for validating the certificate. In the blog post mentioned above there is class for validating the certificate. Based on this example I have created a Certificate Authorization Attribute.

/// <summary>
/// Certificate Authorization Attribute
/// </summary>
/// <seealso cref="System.Web.Http.AuthorizeAttribute" />
[AttributeUsage(AttributeTargets.All)]
public class CertificateAuthorizationAttribute : AuthorizeAttribute {

    /// <summary>
    /// Indicates whether the specified control is authorized.
    /// </summary>
    /// <param name="actionContext">The context.</param>
    /// <returns>
    /// true if the control is authorized; otherwise, false.
    /// </returns>
    protected override bool IsAuthorized(HttpActionContext actionContext) {
        bool retVal = false;

        var cert = actionContext.RequestContext.ClientCertificate;

        if (cert != null) {
            try {
                retVal = IsValidClientCertificate(cert);
            } catch {
                //log exception
            }
        }
        return retVal;
    }

    /// <summary>
    /// Determines whether [is valid client certificate] [the specified certificate].
    /// </summary>
    /// <param name="certificate">The certificate.</param>
    /// <returns>boolean</returns>
    private bool IsValidClientCertificate(X509Certificate2 certificate) {
        //1. Check time validity of certificate
        if (DateTime.Compare(DateTime.Now, certificate.NotBefore) < 0 || DateTime.Compare(DateTime.Now, certificate.NotAfter) > 0) {
            return false;
        }

        //2. Check subject name of certificate
        bool foundSubject = false;
        string[] certSubjectData = certificate.Subject.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string s in certSubjectData) {
            if (String.Compare(s.Trim(), "[Subject Name]") == 0) {
                foundSubject = true;
                break;
            }
        }
        if (!foundSubject) return false;

        //3. Check issuer name of certificate
        bool foundIssuerCN = false;
        string[] certIssuerData = certificate.Issuer.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string s in certIssuerData) {
            if (String.Compare(s.Trim(), "[Issuer]") == 0) {
                foundIssuerCN = true;
                break;
            }
        }
        if (!foundIssuerCN) return false;

        // 4. Check thumbprint of certificate
        if (String.Compare(certificate.Thumbprint.Trim().ToUpper(), "[Thumbprint]") != 0) return false;

        return true;
    }
}

This attribute makes sure you call the application with a valid certificate. It checks the following properties of the certificate:

  1. The time validity
  2. The subject name
  3. The issuer name
  4. The thumbprint

With the attribute implemented it can be used on a Controller method for example.

[CertificateAuthorization]
[Route("spitems/{clientId}")]
public HttpResponseMessage Get(string clientId) {
}

Make sure you do not forget to add the following xml node to your web.config and call your application with https. If you do not do this your certificate will not be recognized in your request.

<configuration>
<!-- other settings -->
  <system.webServer>
    <security>
      <access sslFlags="SslNegotiateCert" />
    </security>
  </system.webServer>
<configuration>

Testing your application

Testing your application can be done by using the HttpClient to perform a request.

private async Task<string> RunAsync() {
    string retVal = string.Empty;
    var handler = new WebRequestHandler();
    handler.ClientCertificateOptions = ClientCertificateOption.Manual;
    var cert = GetClientCert();
    handler.ClientCertificates.Add(cert);

    handler.ServerCertificateValidationCallback = delegate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors error) {
        return true;
    };
    handler.UseProxy = false;

    using (var client = new HttpClient(handler)) {
        client.BaseAddress = new Uri("[Base Address]");
        client.DefaultRequestHeaders.Accept.Clear();