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:
You can find the sample project for this post here.
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.
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.
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.
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.
$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.
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:
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.
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.
The application must have appropriate permissions to access the key vault. To grant permissions to the app, follow the steps below:
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.
Quickstart: Set and retrieve a secret from Azure Key Vault using the Azure portal
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?
Enhancing ASP.NET Core/Blazor App Security and Reusability with HttpMessageHandler and Named HttpClient
Authenticate against azure ad using certificate in a client credentials flow
How to retrieve connection strings in azure key vault from ASP.NET using configuration builders, XML transformation and azure devops.
Securely log to blob storage using NLog with connection string in key vault.
Connect to azure key vault from an ASP.NET core app using azure managed identity
Three ways of authenticating a Windows virtual machine against Azure Key Vault.
Secure app settings in ASP.NET Core 2
Web scraping in C# using HtmlAgilityPack