首页 > 解决方案 > C#:使用计划的 Azure Functions 中不存在密钥集

问题描述

我们中的许多人在尝试使用 X509Certificate2 时都遇到了“Keyset does not esist”异常。症状是:

  1. 您将 .pfx 加载到 X509Certificate2

  2. 您在任何需要私钥的操作中使用它

  3. 你得到“键集不存在”异常。

在代码中,它如下所示:

string CreateJWTToken(byte[] certificate, string psw, string serviceUserIss, string serviceUserSub, string serviceUserAud)
    {
        using (X509Certificate2 combinedCertificate = new X509Certificate2(certificate, psw,
              X509KeyStorageFlags.MachineKeySet
            | X509KeyStorageFlags.PersistKeySet 
            | X509KeyStorageFlags.Exportable))
        {
            var signingKey = new X509SecurityKey(combinedCertificate);
            var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
            var header = new JwtHeader(credentials);
            var payload = new JwtPayload
            {
               { "iss", serviceUserIss},
               { "sub", serviceUserSub},
               { "aud", serviceUserAud},
               { "exp", $"{unixTimestamp}"}
            };
            var secToken = new JwtSecurityToken(header, payload);
            var handler = new JwtSecurityTokenHandler();
            return handler.WriteToken(secToken); // here exception is thrown
        }
    }

它在本地运行良好,甚至在本地 Azure Functions 主机中也能正常工作,但由于某种原因,它有时(例如在 5% 的情况下)在 Azure Function(定时器触发)中运行时会抛出“密钥集不存在”的消费计划。

标签: c#azuresecurityazure-functionsprivate-key

解决方案


问题似乎在于 Azure Functions 消费计划如何与证书一起使用的一些细微差别。

我不知道细节,但X509Certificate2有时会清除 ' 的 PrivateKey ......通过某事。它是积极的垃圾收集器吗?它与自动缩放有关,还是与不同主机之间共享的资源有关?我不知道。

但问题似乎通过避免使用 X509Certificate2 来解决,通过使用 BouncyCastle 的机制从 PFX 加载私钥。请参阅下面的代码片段。

下面的代码段也使用 Jose.JWT 来创建 JWT 令牌。

    private static string CreateJWTTokenBountyCastle(byte[] certificate, string psw, string serviceUserIss, string serviceUserSub, string serviceUserAud)
    {
        string jwt;
        using (RSACryptoServiceProvider rsax = OpenCertificate(certificate, psw)) // open using BouncyCastle and avoid usage of X502Certificate2
        {
            Dictionary<string, object> payload = new Dictionary<string, object>(){
                { "iss", serviceUserIss },
               { "sub", serviceUserSub},
               { "aud", serviceUserAud},
            jwt = Jose.JWT.Encode(payload, rsax, Jose.JwsAlgorithm.RS256);
        }
        return jwt;
    }
    private static RSACryptoServiceProvider OpenCertificate(byte[] certB, string pwd)
    {
        MemoryStream ms = new MemoryStream(certB);

        Pkcs12Store st = new Pkcs12Store(ms, pwd.ToCharArray());

        var alias = st.Aliases.Cast<string>().FirstOrDefault(p => st.IsKeyEntry(p));
        AsymmetricKeyEntry keyEntry = st.GetKey(alias);

        var kkey = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyEntry.Key);
        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
        rsa.ImportParameters(kkey);
        return rsa;
    }

推荐阅读