【问题标题】:How can I create an X509Certificate2 object from an Azure Key Vault KeyBundle如何从 Azure Key Vault KeyBundle 创建 X509Certificate2 对象
【发布时间】:2016-08-30 04:58:00
【问题描述】:

我正在使用 Azure Key Vault 来保护我们的密钥和机密,但我不确定如何使用通过 .net SDK 检索的 KeyBundle。如何创建 X509Certificate2 对象?

【问题讨论】:

    标签: c# .net azure x509 azure-keyvault


    【解决方案1】:

    您不能将 KeyBundle 结果用作 X509Certificate2 对象,因为它在这里仅表示密钥对的公钥部分(无颁发者)。请参阅KeyVaultClientExtensions 中的方法,了解使用此 KeyBundle 对象加密数据、验证签名等的功能。

    【讨论】:

    • 感谢您的评论,我误解了如何在密钥保管库中使用密钥。我在这里找到了我想做的很好的解释(将 pfx 存储在密钥库中):stackoverflow.com/questions/33728213/…
    【解决方案2】:

    当您在 KeyVault 中导入/创建证书时,会创建 3 个实体:

    • 证书 - 包含有关证书的所有相关详细信息,包括其公共部分(即公钥、有效期、指纹等)

    • Secret - 包含 base64 中的私钥(证书的私有部分)

    • 关键 - 我不知道,但与此线程无关。

    您可以使用 Certificate 对象或 Secret 对象创建 X509Certificate2 对象。

    如果您希望 X509Certificate2 包含私钥,那么您当然需要获取 Secret 实体的值并执行以下操作:

    SecretBundle certificatePrivateKeySecretBundle =
        await keyVaultClient.GetSecretAsync(certificateIdentifierSecretPart);
    
    byte[] privateKeyBytes = Convert.FromBase64String(certificatePrivateKeySecretBundle.Value);
    X509Certificate2 certificateWithPrivateKey = new X509Certificate2(privateKeyBytes, (string) null, X509KeyStorageFlags.MachineKeySet);
    

    certificateIdentifierSecretPart 等于证书的秘密部分路径: https://<vault name>.vaults.azure.net/secrets/<certificate name>

    注意 /secrets/ 路径。

    【讨论】:

    • 这对我有用:keyVaultClient.GetSecretAsync(, )
    • 在 .Net Core 2.1 中,我是 using Microsoft.Azure.KeyVault;,但 KeyVaultClient 不包含 GetSecretAsync 的定义。安装/更新 Azure SDK。 ://
    • 可以从KeyBundle中确定certificateIdentifierSecretPartvar kb = await keyVaultClient.GetCertificateAsync("https://<vault-name>.vault.azure.net", "<cert-name>"); var certificateIdentifierSecretPart = kb.SecretIdentifier.Identifier; 然后得到SecretBundle如上图。
    • 很好的答案!知道记录在哪里(特别是 /secrets/ 路径)吗?
    • 证书代表刚刚创建的证书,Key代表证书的私有部分,Secret有PFX格式的证书(就像你上传了一个PFX作为Secret一样)。由于上面创建的证书是可导出的,因此 Secret 也包含密钥的 Private 部分。要在内存中本地重新创建证书,我们使用以下代码。参考:rahulpnath.com/blog/signing-a-pdf-file-using-azure-key-vault
    【解决方案3】:

    2020 年 11 月更新:

    在当前版本的 Azure Key Vault 中,Certificates 是一流的概念,而不是一种秘密。

    如果您的 Key Vault 实例已经有一个带有可导出私钥的证书,您将获取它并按如下方式合并 X509Certificate2

    使用DefaultAzureCredential创建所需的客户端

    var certClient = new CertificateClient(new Uri("https://yourKeyVault.vault.azure.net/"), new DefaultAzureCredential());
    var secretClient = new SecretClient(new Uri("https://yourKeyVault.vault.azure.net/"), new DefaultAzureCredential());
    

    获取证书,其中包含指向私钥的链接。

    注意:Key Vault Secrets 库的最新(4.2.0 测试版)包含一个名为 KeyVaultSecretIdentifier 的帮助器类,它会为您执行此解析。

    Response<KeyVaultCertificateWithPolicy> certResponse = await certClient.GetCertificateAsync("testCert");
    
    // If using client version 4.2.0 or later
    KeyVaultSecretIdentifier identifier = new KeyVaultSecretIdentifier(certResponse.Value.SecretId);
    
    // Else, Get the secretId and parse out the parts needed to fetch the secret.
    Uri secretId = certResponse.Value.SecretId;
    var segments = secretId.Segments;
    string secretName = segments[2].Trim('/');
    string version = segments[3].TrimEnd('/');
    

    获取证书的秘密并使用它构造一个新的X509Certificate2

    // If using client version 4.2.0 or later
    Response<KeyVaultSecret> secretResponse = await secretClient.GetSecretAsync(identifier.Name, identifier.Version);
    
    // else
    Response<KeyVaultSecret> secretResponse = await secretClient.GetSecretAsync(secretName, version);
    
    KeyVaultSecret secret = secretResponse.Value;
    byte[] privateKeyBytes = Convert.FromBase64String(secret.Value);
    
    var cert = new X509Certificate2(privateKeyBytes);
    

    有关最新的 Key Vault 证书和 Secret 客户端的更多信息,请在此处查看其各自的 README 文档:

    Azure.Security.KeyVault.Certificates (migration guide from the old version)

    Azure.Security.KeyVault.Secrets (migration guide from the old version)

    【讨论】:

    • 我想补充一点,旧的 Microsoft.Azure.KeyVault 包已被弃用。这些新包更快、更容易定制、正在积极开发中(由 Christopher 和我自己开发)等等。查看:aka.ms/azsdk/intro 了解迁移的更多好处。
    • 感谢您提供此信息。我注意到,为了从 Key Vault 证书中获取X509Certificate2 对象,Azure.Security.KeyVault.Secrets 客户端就足够了,因为证书的名称可以用作GetSecretAsync 的参数。我还注意到将访问 Key Vault 的身份必须具有 Secret Get 访问策略(而不是 Certificate Get 访问策略)。
    • @michaelmaillot 您对秘密名称的看法是正确的,但这是一个不能保证一致的实现细节。秘密名称和证书名称匹配可能最终总是正确的,但额外的调用只是确保秘密名称始终准确。
    • 太棒了,你拯救了我的一天
    【解决方案4】:

    在使用新的 Azure.Security.KeyVault.* 库并在 Christopher Scott 已回答的基础上进行构建时,您可以加载所有活动和未过期的版本,并使用以下内容跳过 GetCertificate 和解析步骤:

    public static IEnumerable<X509Certificate2> LoadCertificateVerisons(
        string keyVaultName,
        string certificateName)
    {
        var keyVaultUrl = new Uri($"https://{keyVaultName}.vault.azure.net");
        var certificateClient = new CertificateClient(keyVaultUrl, new AzureCliCredential());
        var secretClient = new SecretClient(keyVaultUrl, new AzureCliCredential());
    
        var versions = certificateClient.GetPropertiesOfCertificateVersions(certificateName).ToArray();
    
        foreach (var certificate in versions)
        {
            if (!certificate.Enabled.GetValueOrDefault(false) ||
                certificate.ExpiresOn <= DateTimeOffset.UtcNow) continue;
    
            var certificateSecret = secretClient.GetSecret(certificate.Name, certificate.Version).Value;
            var privateKey = Convert.FromBase64String(certificateSecret.Value);
            yield return new X509Certificate2(privateKey, (string) null, X509KeyStorageFlags.MachineKeySet);
        }
    }
    

    【讨论】:

    • 重要提示:证书创建命令中的附加选项很重要。在没有附加参数的情况下,我总是遇到“找不到文件”错误:new X509Certificate2(privateKey, (string) null, X509KeyStorageFlags.MachineKeySet);
    猜你喜欢
    • 2023-03-16
    • 2016-04-24
    • 1970-01-01
    • 2017-07-20
    • 2021-09-30
    • 2020-03-11
    • 2019-10-16
    • 1970-01-01
    相关资源
    最近更新 更多