Friday, October 1, 2021

Use self-signed SSL certificate for web API hosted in Azure App service

Suppose that we develop web API (e.g. https://api.example.com) and want to use self-signed certificate for it's domain name. Since it is web API we usually don't worry a lot about errors or warning which browser may show when you try to access it by url there. At the same time we don't want to compromise security and thus want to allow only our SSL certificate with known hash/thumbprint.

First of all we need to create self-signed certificate for domain name which will be used for web API (it should contain CN=api.example.com otherwise it won't be possible to bind it to custom domain name in Azure App service). Also certificate should be created with exportable private key (pfx) so we can use it in Azure. It can be done by the following PowerShell:

$start = [System.DateTime]::Now.AddDays(-1)
$end = $start.AddYears(2)
New-SelfSignedCertificate `
    -FriendlyName "api.example.com" `
    -KeyFriendlyName "api.example.com" `
    -KeyAlgorithm "RSA" `
    -DnsName "api.example.com" `
    -NotBefore $start `
    -NotAfter $end `
    -KeyUsage CertSign, CRLSign, DataEncipherment, DigitalSignature, NonRepudiation `
    -KeyUsageProperty All `
    -KeyLength 2048 `
    -CertStoreLocation cert:\LocalMachine\My `
    -KeyExportPolicy Exportable
$cert = Get-ChildItem -Path Cert:\LocalMachine\my | where-object { $_.Subject -eq "CN=api.example.com" }
$password = Read-Host -Prompt "Enter Password to protect private key" -AsSecureString
Export-PfxCertificate -Cert $cert -Password $password -FilePath api.example.com.pfx

Here we first create certificate itself and then export it's private key to file system.

Next step is to add custom domain name api.example.com to Azure app service where we will host our API. First of all we need to ensure that current pricing tier supports "Custom domains / SSL" feature. Currently minimal pricing tier with this option is B1 (it is not free):

Then we go to App service > Custom domains > Add custom domain and specify desired domain name for our web API:

Before Azure will allow to use custom domain name we will need to prove hostname ownership by adding TXT and CNAME DNS records for specified domain name - it is done in hosting provider control panel (here are detailed instructions of the whole process: Map an existing custom DNS name to Azure App Service).

Last part is related with client which call our web API. Since web API uses self-signed certificate attempt to call it from C# using HttpClient will fail. We need to configure it to allow usage of our SSL certificate but at the same time don't allow to use other self-signed SSL certificates. It can be done with the following C# code:

// thumprint of self-signed SSL certificate
private const string CERT_THUMBRINT = "...";

ServicePointManager.ServerCertificateValidationCallback = validateServerCertficate;
...
private static bool validateServerCertficate(
    object sender,
    X509Certificate cert,
    X509Chain chain,
    SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        // Good certificate.
        return true;
    }

    return string.Compare(cert.GetCertHashString(), CERT_THUMBRINT, true) == 0;
}

In this code we instruct ServicePointManager to allow only our self-signed certificate (if error occurred during checking of SSL certificate when connection to remote host is established). Those it won't allow connections to hosts which use other self-signed SSL certificates.

Update 2021-10-15: see also post Convert self-signed SSL certificate's private key from .pfx to .key and .crt which describes how to export private key of self-signed certificate from pfx to key/crt.

No comments:

Post a Comment