C #: Набор ключей не существует в функциях Azure в плане потребления - PullRequest
0 голосов
/ 31 августа 2018

Многие из нас сталкивались с исключением «Keyset is esist» при работе с X509Certificate2. Симптомы:

  1. Вы загружаете .pfx в X509Сертификат2

  2. Вы используете его в любой операции, требующей секретного ключа

  3. Вы получаете исключение "Keyset not Существует".

В коде это выглядит следующим образом:

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, но по какой-то причине иногда (как в 5% случаев) выбрасывает «Набор ключей не существует» при запуске в функции Azure (запускаемой по таймеру) в плане потребления.

1 Ответ

0 голосов
/ 31 августа 2018

Проблема заключалась в некоторых нюансах того, как функции Azure в плане потребления работают с сертификатами.

Я не знаю подробностей, но PrivateKey X509Certificate2 иногда очищается ... чем-то. Это агрессивный сборщик мусора? Это связано с автоматическим масштабированием или с ресурсами, используемыми разными хостами? Я не знаю.

Но проблема, кажется, решена, избегая использования 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;
    }
...