Quote of the Day

more Quotes

Categories

Buy me a coffee

  • Home>
  • IIS>

Access azure key vault from an ASP.NET core app on IIS using X.509 certificate

In this post, I go over in more details the steps of retrieving secrets from an azure key vault using client id and secret. This approach is one of the three ways to authenticate a Windows virtual machine against azure key vault. It is suitable if your app runs on a virtual machine which is not an azure resource and so cannot use azure managed identity.

At the high level, the process involves these steps:

  • Register the application in azure.
  • Generate and add a X.509 certificate into a certificate store.
  • Grant IIS_IUSRS user permission to access the private key of the certificate.
  • Upload the public key of the certificate to the app’s registration.
  • Grant the app access to the key vault.
  • Add codes to Startup file to authenticate against AD using the certificate.

You can find the sample project for this post here.

Key vault

For instructions on creating a key vault, checkout the documentation.

Once you have created a key vault, take note of the URL (DNS Name) of your key vault. You need to reference this url when accessing the key vault from your app. You can find the URL by going to your key vault resource, under Settings -> Properties.

Register the application in azure

To connect to a key vault, an application must first authenticate against azure AD. Therefore, you need to register the application in your azure tenant to give it an identity.

For instructions on registering an application in Azure AD, checkout the documentation.

X.509 certificate

A X509 certificate consists of a private and public key pair. You upload the public key to azure and keep the private key somewhere safe. For instance, you can store the certificate in the personal store of the Windows server on which your app runs, letting the OS manage the certificate for you.

For a discussion of using certificate vs username/password for authentication, checkout this post.

Generate a X.509 self-signed certificate

Follows the steps below to generate a self-signed X.509 certificate which consists of a public and private key and inserts the certificate into the personal store.

  1. Open Windows PowerShell ISE.
  2. Paste the script below which is the same script I copy over from my other post.
$date_now = Get-Date
# Set the number of years before the certificate expires. $extended_date = $date_now.AddYears(20) 
# DNS name of the host/machine. 
# Ex: MyTestServer $DnsName = "My test server" 
# owner of the certificate. 
$Subject = "CN=myapp,CN=mycompany,DC=com" 
# friendly name of the certificate 
$FriendlyName = "KeyVaultAccess" 
$cert = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\my -DnsName $DnsName -NotAfter $extended_date -Subject $Subject -FriendlyName $FriendlyName

Replace the variables in the above snippet to match your need.

3. Run the script to generate and insert the certificate into the Personal store.

You should see the certificate which the script generates under this store. For instance, below shows my certificate I generated with the script.

Screenshot of certificate generated using Powershell New-SelfSignedCertificate cmdlet.
Certificate generated and added to the Personal store.

Grant IIS_IUSRS user access to the private key of the X.509 certificate

If your app runs on IIS, ensure the IIS_IUSRS user has at least Read permission to the private key. Otherwise, your app may fail to load the private key and throw a WindowsCryptographicException: The system cannot find the file specified. My team spent a good few hours debugging the issue before figuring out the problem. Below shows the snippet of the exception we were getting:

2019-08-29 09:28:12.1966|6|FATAL|Microsoft.AspNetCore.Hosting.Internal.WebHost|Application startup exception Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The system cannot find the file specified
   at System.Security.Cryptography.CngKey.Open(String keyName, CngProvider provider, CngKeyOpenOptions openOptions)
   at System.Security.Cryptography.CngKey.Open(String keyName, CngProvider provider)
   at Internal.Cryptography.Pal.CertificatePal.GetPrivateKey[T](Func`2 createCsp, Func`2 createCng)
   at Internal.Cryptography.Pal.CertificatePal.GetRSAPrivateKey()
   at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPrivateKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
   ...

To check and grant IIS_IUSRS the permissions, follow the steps below:

  1. Open Microsoft Management Console.
    • Press the Windows key, and type MMC
  2. Add the snap in to manage certificates under Computer account
    • Go to File -> Add/Remove Snap-in
    • Select Certificates under Available snap-ins
    • Select Computer account for Certificates snap-in
    • Select Local computer for Select Computer
  3. Under Certificates, navigate to the certificate you generated. If you use the PowerShell script above, the certificate should be under Personal -> Certificates.
  4. Right click on the certificate, select All Tasks -> Manage Private Keys
Manage private key of a certificate

5. Under Security, Group or user names, verify the IIS_IUSRS has Read permission. If not, click the Add button to add the user and assign the permission.

Export and upload the public key

  1. Right click on the certificate, select All Tasks -> Export
  2. Follow the Welcome to the Certificate Export Wizard to export the public key.
  • For Export Private Key, select No, do not export the private key.
  • For Export File Format , select DER encoded binary X.509 (.CER)

3. Go to the app’s registration on azure portal.

4. Under Certificates & secrets, select Upload certificate

5. Select and upload the public key from your server.

Grant app access to the key vault

The application must have appropriate permissions to access the key vault. To grant permissions to the app, follow the steps below:

  1. In your key vault resource, select Access policies from the left menu.
  2. Click on Add Access Policy.
  3. Under Secret permissions, check Get and List permissions. You need at least these two permissions to read secrets from the key vault.
  4. Under Select Principal, click to select a principal.
  5. On the right side, in the search bar, enter to search and select the name of your registered application.
  6. Click Select.
  7. Click Add.
Granting application permissions to read secrets from a key vault

Add codes to Startup file

You use the classes under System.Security.Cryptography.X509Certificates to retrieve the certificate from the store where we save the certificate at the earlier step. You also need to add the package Microsoft.Extensions.Configuration.AzureKeyVault which exposes the method to configure the key vault given the url, application id, and certificate.

The following extension method is an example of adding the key vault

public static IConfigurationBuilder SetupKeyVault(this IConfigurationBuilder builder)
        {
            var configuration = builder.Build();
            var keyVaultURL = configuration["KeyVault:URL"];
            var appId = configuration["AzureAD:AppId"];
       
            using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
            {
                store.Open(OpenFlags.ReadOnly);
                var certs = store.Certificates;
                Debug.WriteLine("Num of certificates in store: " + certs.Count);
                var distinguishedName = new X500DistinguishedName(configuration["KeyVault:SubjectDistinguishedName"]);
                var certFound = certs.Find(X509FindType.FindBySubjectDistinguishedName, 
                    distinguishedName.Name, false).OfType<X509Certificate2>();
                if (!certFound.Any())
                {
                    Debug.WriteLine("Unable to find the certificate to authenticate and access key vault");
                }
                else
                {
                    // found the certificate 
                    builder.AddAzureKeyVault(keyVaultURL, appId, certFound.Single());
                    store.Close();
                }
            }

            return builder; 
        }

You can search for a certificate by different types of fields such as Thumbprint, Subject Name, Issuer Name etc … If your app runs on multiple servers, and you want to use different certificates for the server, you can set a common field for all the certificates and search by that field. For instance, I use the powershell script above to generate different certificates with the same Subject Distinguished Name which I use to search for the certificates in the above snippets.

References

Quickstart: Set and retrieve a secret from Azure Key Vault using the Azure portal

How to: Use the portal to create an Azure AD application and service principle that can access resources

Certificate based authentication vs Username and Password authentication

New-SelfSignedCertificate powershell cmdlet

Microsoft.Extensions.Configuration.AzureKeyVault nuget package

How to Grant IIS 7.5 access to a certificate in certificate store?

No comments yet