Сертификаты, установленные на сервисной фабрике, не помечаются как экспортируемые, что нарушает аутентификацию keyvault - PullRequest
0 голосов
/ 25 января 2019

У меня есть новая фабричная служба, предназначенная для .Net 4.7 В 4.7 были внесены крипто-изменения, которые нарушили некоторые сценарии аутентификации, включая keyvault.Изменения были внесены в код авторизации для обработки 4.5 и 4.7.В моем локальном кластере все работает нормально, но в реальном кластере происходит сбой, за исключением следующего:

Сообщение: сбой RunAsync из-за необработанного исключения, вызывающего сбой хост-системы: System.Security.Cryptography.CryptographicException: Ключ недопустим для использования в указанном состоянии.

в System.Security.Cryptography.CryptographicException.ThrowCryptographicException (Int32 ч) в System.Security.Cryptography.Utils._ExportKey (SafeKeyHandle hKey, Int32 blobTT, Объект cspObject) в System.Security.Cryptography.RSACryptoServiceProvider.ExportParameters (Boolean includePrivateParameters) в System.Security.Cryptography.RSA.ToXmlString (логические includePrivateParameters) установлены *1000* найдено 1010

по коду.Закрытые ключи сертификатов также имеют соответствующие разрешения, поэтому они могут быть прочитаны NetworkService, от имени которого работает служба.Похоже, что эта ошибка вызвана тем, что сертификат, развернутый в сервисной фабрике, не помечен как экспортируемый.Ниже приведен код метода Sign, который используется для того, чтобы наши приложения из 4.5 и 4.7 могли использовать keyvault с одной и той же библиотекой.С другими нашими облачными сервисами все в порядке, просто сервисная фабрика выходит из строя.

Я полагаю, что возможным решением было бы написать код для конкретного метода 4.7, но я не нашел конкретного примера 4.7, который не используеттот же метод, который я уже использую.Я также не нашел способа получить шаблон развертывания, чтобы пометить сертификаты как экспортируемые.

        /// <summary>
        /// Sign a message with the private key of the certificate provided
        /// The signature is expected use the SHA256 algorithm or the assertion will not be valid.
        /// </summary>
        /// <param name="message">The message to sign.</param>
        /// <returns>Array of signed bytes.</returns>
        public byte[] Sign(string message)
        {
            byte[] messageBytes = Encoding.UTF8.GetBytes(message);

            X509AsymmetricSecurityKey x509Key = new X509AsymmetricSecurityKey(this.certificate);

            using (RSA rsa = x509Key.GetAsymmetricAlgorithm(SecurityAlgorithms.RsaSha256Signature, true) as RSA)
            {
                RSACryptoServiceProvider newRsa = null;

                try
                {
                    if (rsa is RSACryptoServiceProvider)
                    {
                        // For .NET 4.6 and below we get the old RSACryptoServiceProvider implementation as the default.
                        // Try and get an instance of RSACryptoServiceProvider which supports SHA256
                        newRsa = GetCryptoProviderForSha256((RSACryptoServiceProvider)rsa);
                    }
                    else
                    {
                        // For .NET Framework 4.7 and onwards the RSACng implementation is the default.
                        // Since we're targeting .NET Framework 4.5, we cannot actually use this type as it was
                        // only introduced with .NET Framework 4.6.
                        // Instead we try and create an RSACryptoServiceProvider based on the private key from the
                        // certificate.
                        newRsa = GetCryptoProviderForSha256(this.certificate);
                    }

                    using (SHA256Cng sha = new SHA256Cng())
                    {
                        return newRsa.SignData(messageBytes, sha);
                    }
                }
                finally
                {
                    // We only want to dispose of the 'newRsa' instance if it is a *different instance*
                    // from the original one that was used to create it.
                    if (newRsa != null && !ReferenceEquals(rsa, newRsa))
                    {
                        newRsa.Dispose();
                    }
                }
            }
        }

        /// <summary>
        /// Create a <see cref="RSACryptoServiceProvider"/> using the private key from the given <see cref="X509Certificate2"/>.
        /// </summary>
        /// <param name="certificate">Certificate including private key with which to initialize the <see cref="RSACryptoServiceProvider"/> with</param>
        /// <returns><see cref="RSACryptoServiceProvider"/> initialized with private key from <paramref name="certificate"/></returns>
        private static RSACryptoServiceProvider GetCryptoProviderForSha256(X509Certificate2 certificate)
        {
            string privateKeyXmlParams = certificate.PrivateKey.ToXmlString(true);
            RSACryptoServiceProvider rsa = null;

            try
            {
                rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(privateKeyXmlParams);
                return rsa;
            }
            catch (Exception)
            {
                if (rsa != null)
                {
                    rsa.Dispose();
                }

                throw;
            }
        }

Обновление:

Получает решение для чисто 4,7просто

                    else if (rsa is RSACng)
                    {
                        return rsa.SignData(messageBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                    }

Но это не очень хорошо работает для библиотек, на которые могут ссылаться проекты в более старых версиях .Net, например, для библиотеки, из которой я позаимствовал этот код.Более новый тип RSACng не будет доступен в этих случаях.Я предполагаю, что это может быть обработано кучей #if NETXXX, но для этого потребуется обновление в каждом выпуске .Net.

...