Search code examples
c#azureazure-cloud-servicesx509certificate2azure-keyvault

Azure: Connect to key vault from cloud worker via certificate


I have a worker process running as a classic cloud service in Azure. This process needs to retrieve a value from an Azure key vault, and I need to keep the vault authentication secret outside my source tree.

Managed identities don't seem to be available for classic cloud workers, so I'm looking at certificates instead. I have gotten authentication by certificates to work locally, by creating a certificate and then uploading in Azure Active Directory for my app registration:

Certificate creation:

New-SelfSignedCertificate -Subject "CN=MyFineCertificate" -CertStoreLocation "Cert:\CurrentUser\My" -KeyExportPolicy Exportable -KeySpec Signature

Code that uses it to connect to a key vault (works locally):

private static KeyVaultClient GetClient()
{
    var certificate = GetCertificate();
    var assertion = new ClientAssertionCertificate(clientId, certificate);
    var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback((a, r, s) => GetAccessTokenUsingCert(a, r, s, assertion)));
    return client;
}

private static X509Certificate2 GetCertificate()
{
    var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    certStore.Open(OpenFlags.ReadOnly);
    var results = certStore.Certificates.Find(/* criteria */);
    return results[0];
}

private static async Task<string> GetAccessToken(string authority, string resource, string scope, ClientAssertionCertificate cert)
{
    var authContext = new AuthenticationContext(authority, TokenCache.DefaultShared);
    var result = await authContext.AcquireTokenAsync(resource, cert);
    return result.AccessToken;
}

So far so good. However, I want my Azure cloud worker to do this, so I need my certificate available there. My naive assumption was that I could, from the "Certificates" panel for my cloud worker in the portal, upload the certificate (pfx).

Unfortunately my cloud worker isn't able to find it. If I run this on Azure, after uploading my certificate, it doesn't show up:

foreach (StoreName name in Enum.GetValues(typeof(StoreName)))
{
    foreach (StoreLocation location in Enum.GetValues(typeof(StoreLocation)))
    {
        var certStore = new X509Store(name, location);
        certStore.Open(OpenFlags.ReadOnly);
        foreach (var res in certStore.Certificates)
        {
            /* log certificate */
        }
    }
}

Why isn't it showing up? Am I even on the right track, or have I completely misunderstood how this works?


Solution

  • There are two parts to your question here:

    1. Approach - As Managed Service Identity is not available with Cloud Services, how to work/authenticate with Azure Key Vault?

      You are on the right track. I have described the approach for a similar problem to work with Key Vault in this SO Post - Securing sensitive information in Azure Cloud Service Configuration

    2. Implementation - specifically how to access the self-signed certificate from cloud service role instance.

      I think you might be missing to specify the certificate and it's location in the cloud service definition and configuration files. You should try adding something like this -

      CSCFG

       <Certificates>
           <Certificate name="MyFineCertificate" thumbprint="<my_thumbprint>" thumbprintAlgorithm="<my_thumbprint_algo e.g. sha1>" />
       </Certificates>
      

      CSDEF

       <Certificates>
            <Certificate name="MyFineCertificate" storeLocation="LocalMachine" storeName="My" />
       </Certificates> 
      

      Notice that I've mentioned store location as LocalMachine. Now, you should be able to access the certificate from your code by specifying the right location.

      var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);