如何解决无法使用从AzureKeyVault读取的证书编写JWTToken
我们正在使用自签名证书来创建JwtSecurityToken。当前,我们正在手动将证书上传到我们的Azure应用服务,然后使用此代码查找它
X509Store certStore = new X509Store(StoreName.My,StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,signingCertThumbprint,false);
return new X509SigningCredentials(certCollection[0]);
这很好,但是我们希望不必在运行appservice的计算机上安装证书,而是从Azure Key Vault中读取证书。额外的好处是,这意味着App Service可以在开发人员的计算机上本地运行,而无需共享和安装证书。
我们可以使用以下方法从Azure Key Vault获取证书
var certificateClient = new CertificateClient(new Uri("https://ourkeyvault.vault.azure.net/"),new DefaultAzureCredential());
var b2cInviteCertificate = certificateClient.GetCertificate("B2CInvite");
return new X509SigningCredentials(new X509Certificate2(b2cInviteCertificate.Value.Cer));
FWIW,我还尝试过重载需要密码的X509Certificate2
ctor。
根据这些凭据,我们创建一个JwtSecurityToken
JwtSecurityToken token = new JwtSecurityToken(
issuer.ToString(),audience,claims,DateTime.Now,DateTime.Now.AddDays(7),JwtService.signingCredentials.Value);
然后我们使用JwtSecurityTokenHandler获取令牌字符串
JwtSecurityTokenHandler jwtHandler = new JwtSecurityTokenHandler();
return jwtHandler.WriteToken(token);
对WriteToken的调用会导致以下错误消息
InvalidOperationException:IDX10638:无法创建 SignatureProvider,“ key.HasPrivateKey”为假,无法创建签名
为什么会这样?
解决方法
同意Pat Longs的回答,只有在将其作为证书使用时才获得公共密钥。是的,它肯定没有任何意义!不得不通过秘密绕道而感到愚蠢。
无论如何,我最终最终从Azure Key Vault获得带有私钥的证书的代码看起来像这样:
/// <summary>
/// Load a certificate (with private key) from Azure Key Vault
///
/// Getting a certificate with private key is a bit of a pain,but the code below solves it.
///
/// Get the private key for Key Vault certificate
/// https://github.com/heaths/azsdk-sample-getcert
///
/// See also these GitHub issues:
/// https://github.com/Azure/azure-sdk-for-net/issues/12742
/// https://github.com/Azure/azure-sdk-for-net/issues/12083
/// </summary>
/// <param name="config"></param>
/// <param name="certificateName"></param>
/// <returns></returns>
public static X509Certificate2 LoadCertificate(IConfiguration config,string certificateName)
{
string vaultUrl = config["Vault:Url"] ?? "";
string clientId = config["Vault:ClientId"] ?? "";
string tenantId = config["Vault:TenantId"] ?? "";
string secret = config["Vault:Secret"] ?? "";
Console.WriteLine($"Loading certificate '{certificateName}' from Azure Key Vault");
var credentials = new ClientSecretCredential(tenantId: tenantId,clientId: clientId,clientSecret: secret);
var certClient = new CertificateClient(new Uri(vaultUrl),credentials);
var secretClient = new SecretClient(new Uri(vaultUrl),credentials);
var cert = GetCertificateAsync(certClient,secretClient,certificateName);
Console.WriteLine("Certificate loaded");
return cert;
}
/// <summary>
/// Helper method to get a certificate
///
/// Source https://github.com/heaths/azsdk-sample-getcert/blob/master/Program.cs
/// </summary>
/// <param name="certificateClient"></param>
/// <param name="secretClient"></param>
/// <param name="certificateName"></param>
/// <returns></returns>
private static X509Certificate2 GetCertificateAsync(CertificateClient certificateClient,SecretClient secretClient,string certificateName)
{
KeyVaultCertificateWithPolicy certificate = certificateClient.GetCertificate(certificateName);
// Return a certificate with only the public key if the private key is not exportable.
if (certificate.Policy?.Exportable != true)
{
return new X509Certificate2(certificate.Cer);
}
// Parse the secret ID and version to retrieve the private key.
string[] segments = certificate.SecretId.AbsolutePath.Split('/',StringSplitOptions.RemoveEmptyEntries);
if (segments.Length != 3)
{
throw new InvalidOperationException($"Number of segments is incorrect: {segments.Length},URI: {certificate.SecretId}");
}
string secretName = segments[1];
string secretVersion = segments[2];
KeyVaultSecret secret = secretClient.GetSecret(secretName,secretVersion);
// For PEM,you'll need to extract the base64-encoded message body.
// .NET 5.0 preview introduces the System.Security.Cryptography.PemEncoding class to make this easier.
if ("application/x-pkcs12".Equals(secret.Properties.ContentType,StringComparison.InvariantCultureIgnoreCase))
{
byte[] pfx = Convert.FromBase64String(secret.Value);
return new X509Certificate2(pfx);
}
throw new NotSupportedException($"Only PKCS#12 is supported. Found Content-Type: {secret.Properties.ContentType}");
}
}
,
之所以不起作用,是因为从KeyVault检索到的证书的私钥为空。如果需要私钥,则需要将证书作为“机密”拉出。
This answer至Azure Key Vault Certificates does not have the Private Key when retrieved via IKeyVaultClient.GetCertificateAsync解释了原因,并详细介绍了另一个答案
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。