Azure App Service and Client Certificate Authentication

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();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        var response = await client.GetAsync("[url part 2]");
        if (response.IsSuccessStatusCode) {
            retVal = await response.Content.ReadAsStringAsync();
        } 
    }

    return retVal;
}

9 Replies to “Azure App Service and Client Certificate Authentication”

  1. Hi, we are getting “The page cannot be displayed because an internal server error has occurred.” when we add “” to the web.config. We have also updated the Any idea why or if there is anything else we need to modify?

    1. Hi Allan,

      Did you turn off the custom errors, and did you make every configuration change? (Also on the Azure side). Keep in mind that when using Certificate Authentication you will need to open the site in the https form.

  2. Hi, Is it possible to enable client certificate only to a specific path?

    For example adding the following element to web.config, Is there any way to set clientCertEnabled true only for the same path?

    1. Hi CF,

      In the web.config you can’t enable client certificate for only a specific path. What you could test is not applying the attribute to specific controllers and test if it works when you do not apply a certificate to that specific controller.

  3. Hi,

    Do we need to upload any key (public key of client cert or CA which used to sign this client cert) to Azure?

      1. Hi,

        Thanks for your response. So you mean if someone has my client info (likes CN, serial…), they can generate certificate and use it? Sounds like it turns to Knowledge Factor instead Ownership, and why Azure have option that let us upload public key to it?

        1. That is true. Depending on the things that you validate people could generate a certificate, in that situation they should know the properties on which you validate the certificate.

          That was the only option there was at the time of writing the article do not know if there is a better solution as this time. I would guess there is because there is also a keyvault which can contain certificates.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.