Duende Identity Server v6 抛出“WindowsCryptographicException:密钥不存在”

问题描述 投票:0回答:1

我正在从身份服务器 4 升级到 duende 身份服务器 6。

在本地运行时,我可以通过“DefaultAzureCredential”引用 Azure Key Vault 上的密钥,没有任何问题,并且它设置正确。

但是:(

然后站点运行(站点和身份服务器全部作为一个运行)并且我登录它会抛出异常“WindowsCryptographicException:密钥不存在”

            var identityServiceBuild = services.AddIdentityServer(options =>
            {
                options.KeyManagement.Enabled = false;
            });

            string keyVaultName = "mykeyvault";
            var kvUri = "https://" + keyVaultName + ".vault.azure.net";

            var keyClient = new KeyClient(new Uri(kvUri), new DefaultAzureCredential());
            var keyResponse = keyClient.GetKey("devidentityserver");

            RSA rsa = keyResponse.Value.Key.ToRSA();
            key = new RsaSecurityKey(rsa) { KeyId = keyResponse.Value.Properties.Version };

           identityServiceBuild.AddSigningCredential(key, IdentityServerConstants.RsaSigningAlgorithm.RS256);

这里有更多的堆栈跟踪(如果有帮助的话):

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Key does not exist.
   at Internal.Cryptography.CngCommon.SignHash(SafeNCryptKeyHandle keyHandle, ReadOnlySpan`1 hash, AsymmetricPaddingMode paddingMode, Void* pPaddingInfo, Int32 estimatedSize)
   at System.Security.Cryptography.RSAImplementation.RSACng.SignHash(Byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider.Sign(Byte[] input)
   at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
   at Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateTokenPrivate(JObject payload, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials, String compressionAlgorithm, IDictionary`2 additionalHeaderClaims, String tokenType)
   at Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateToken(String payload, SigningCredentials signingCredentials, IDictionary`2 additionalHeaderClaims)
   at Duende.IdentityServer.Services.DefaultTokenCreationService.CreateJwtAsync(Token token, String payload, Dictionary`2 headerElements) in /_/src/IdentityServer/Services/Default/DefaultTokenCreationService.cs:line 138

是因为它期望键作为“Keys”SQL 表中的一行进行持久化吗?我想不是因为我通过 KeyManagement.Enabled = false 禁用了密钥管理?

救命!

c# identityserver4 ef-core-6.0 duende-identity-server duende
1个回答
0
投票

问题在于这一行:

var keyResponse = keyClient.GetKey("devidentityserver");

只会从KeyVault返回公钥,要获取私钥,您需要将其作为秘密获取。 (是的,这很奇怪)。

这是我在设置中使用的代码,用于从 keyvault 获取密钥:

        /// <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:ClientSecret"] ?? "";

            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}");
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.