从 Azure Keyvault 获取 X509 证书以在 REST 调用中使用

Get X509 Certificate from Azure Keyvault to use in a REST call

我正在尝试从 Azure Keyvault 获取证书,然后使用它来调用需要证书进行身份验证的 REST API。

我试过在本地执行此操作 - 我在磁盘上有 .pfx 文件,我将其加载到一个字节数组中,然后从中创建我的证书:

X509Certificate2 x509 = new X509Certificate2(File.ReadAllBytes(path), password);

然后在 RestSharp 中使用该证书进行我的 REST 调用:

IRestClient client = new RestClient(url);
client.ClientCertificates = new X509CertificateCollection { x509 };

var request = new RestRequest(lastUrlPart, Method.GET);
request.AddHeader("Cache-Control", "no-cache");
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json");

IRestResponse response = client.Execute(request);

if (response.IsSuccessful)
{
    // read out the response and process it
}

很有魅力。

现在我尝试做同样的事情,但从 Azure Keyvault 获取证书。我在 Azure AD 中创建了一个应用程序注册,创建了我的密钥库,并为我的应用程序注册的服务标识提供了对密钥库的访问权限。我已将我的证书上传到密钥库中。到目前为止,还不错。

我发现这段代码可以从 Keyvault 中获取证书:

var azureServiceTokenProvider = new AzureServiceTokenProvider();

var kv = new KeyVaultClient(async (authority, resource, scope) =>
                                        {
                                            var authContext = new AuthenticationContext(authority, TokenCache.DefaultShared);
                                            var clientCred = new ClientCredential(clientAppId, clientSecret);
                                            var result = await authContext.AcquireTokenAsync(resource, clientCred);

                                            if (result == null)
                                            {
                                                throw new InvalidOperationException("Failed to obtain the JWT token");
                                            }

                                            return result.AccessToken;
                                        });

string certIdentifier = "https://mykeyvault.vault.azure.net/certificates/Certificate-TEST/14753af7586445fe9d57efa136ac090c";

var vaultCertificate = kv.GetCertificateAsync(certIdentifier).GetAwaiter().GetResult();

这也有效——我可以用我的应用身份访问密钥库,我可以从密钥库获取证书,X.509 指纹有效——但现在这是一个 CertificateBundle 来自Microsoft.Azure.KeyVault.Models 命名空间 - 如何将其 "convert" 放入 "regular" X509Certificate2 对象中,以便我可以将其用于 REST 调用?

我试过很多东西,例如

X509Certificate2 x509 = new X509Certificate2(vaultCertificate.Cer);

但没有任何效果 - 当我发出 REST 调用时,我收到 HTTP 403 - 禁止错误返回....

我错过了什么??如何以可用于在后续 REST 调用中进行身份验证的格式从 Azure Keyvault 获取证书?

当然 - 在我发布这个问题之后,我偶然发现了解决方案....

这个 blog post by Matt Small 非常详细地解释了它(并且非常好,并且 工作 代码示例也是如此)。

基本上,为了使用证书进行身份验证,您还需要有私钥 - 当您执行 GetCertificateAsync 时,您只会得到 public 信息证书.

您需要获取证书作为秘密,然后对其进行 base64 解码 - 然后您获得所有必要的位,REST 调用就可以工作了。

天啊!为什么这如此令人费解?为什么在 GetCertificateAsync 调用中不能只有一个 includePrivate: bool 参数来告诉 Keyvault 您是否只需要 public 或 public 你证书的隐私部分?

您存储了一个证书 - 但要获取它,您需要获取一个秘密....那是完全错误的,并且违反了最不意外的基本原则! MS ,如果你在听 - 这个 API 确实需要做更多的工作才能让 Azure 开发新手更容易上手!!

和PS:这当然也意味着您用来访问密钥库的user/identity现在需要权限来读取机密——而不仅仅是证书......

试试下面的代码:

var cert = kvc.GetCertificateAsync(baseUrl, "Demo").ConfigureAwait(false).GetAwaiter().GetResult();
var cert_content = cert.Cer;
X509Certificate2 x509 = new X509Certificate2(cert_content);

您可以轻松地从 CertificateBundle 获取证书的原始字节,然后使用原始字节创建您的 X509Certificate2 实例。