sslStream.AuthenticateAsServer ПРОБЛЕМЫ System.ComponentModel.Win32Exception (0x80004005): учетные данные, предоставленные для пакета, не были распознаны - PullRequest
0 голосов
/ 24 июня 2019

Я использую класс MSDN sslStream для TCP-сервера / клиента.Я создаю X509Certificate2, используя пакет NuGet для надувного замка.

Мой код сначала создает сертификат, затем преобразует его в сертификат .NET и помещает в хранилище локальных компьютеров / личных сертификатов.После того, как сертификат помещен в хранилище, я затем программно экспортирую сертификат .cer в папку установки моей программы на диске C.

Если я запускаю свое приложение в режиме отладки в Visual Studio, все шаги выполняются безпроблемы, и я могу запустить TCP-сервер, используя поток SSL.В моем сценарии я просто использую сертификат для проверки сервера из соединения с клиентом TCP и использую его исключительно для целей шифрования.

Однако - производственная сборка для моей программы должна работать как служба Windows, в которойЯ использую пакет TopShelf NuGet.

Проблема, с которой я столкнулся, заключается в том, что при запуске той же программы, что и служба Windows, у меня возникают проблемы с разрешениями.Если я запускаю программу в Visual Studio и тестирую TCP Client & Server на той же машине, все работает нормально.Я предполагаю, что это потому, что класс SSL Stream обращается к сертификату X509 из хранилища сертификатов с помощью учетной записи администратора Windows и, следовательно, может получить доступ к информации о секретном ключе.

Однако, если я запускаю ту же программукак служба Windows, т. е. изначально создавая сертификат, а затем пытаясь запустить TCP-сервер, у меня возникают проблемы, связанные с разрешениями на доступ к информации личного ключа из сертификата X509 в хранилище сертификатов.

Iне может решить, что необходимо для успешного запуска программы при ее запуске в качестве службы Windows.Обратите внимание, что добавление шагов ручной настройки в процесс не является вариантом, поскольку эта программа предназначена для установки обычным пользователем, и все настройки выполняются программно, и им не нужно беспокоиться о ...

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

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 // User Added
 using System.IO;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Operators;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Pkcs;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.X509;
 using System.Security.Cryptography.X509Certificates;
 using Org.BouncyCastle.Crypto.Prng;

 namespace WindowsService
 {
     class X509Certification
     {
         private static readonly log4net.ILog log = log4net.LogManager.GetLogger("SystemLogsRollingFileAppender");
    readonly static string certificateFilePath = System.Configuration.ConfigurationManager.AppSettings["CertificateFilePath"];

    // String used to set the subject name of the X509 Certificate.
    public static string subjectName = "MyCertificateSubject";

    public static void CheckIfCertificateExists()
    {
        //X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadOnly);

        try
        {
            var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, subjectName, false);

            if (certificates != null && certificates.Count > 0)
            {
                log.Info("CHECK for X509 Certificate in localmachine certificate store = OK");
            }
            else
            {
                X509Certificate2 certificate = GenerateCertificate();
                SaveCertificate(certificate);
            }
        }
        catch (Exception ex)
        {
            log.Error(ex);
            SystemEvents.X509CertificateExceptions(ex);
        }
    }

    private static void SaveCertificate(X509Certificate2 certificate)
    {
        var userStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        userStore.Open(OpenFlags.ReadWrite);
        userStore.Add(certificate);
        userStore.Close();
        log.Info("X509 Certificate created and added to localmachine certificate store = OK");

        // Export a DER encoded binary X.509 (.CER) certificates for SSL Server Validation.
        File.WriteAllBytes(certificateFilePath, certificate.Export(X509ContentType.Cert));
        log.Info("DER encoded binary X.509 (.CER) certifcate created and added to My Program Windows Directory on C:Drive = OK");
    }

    /// <summary>
    /// Method used to create a self-signed server certificate using C# and the Bouncy Castle .NET API.
    /// </summary>
    /// <returns></returns>
    public static X509Certificate2 GenerateCertificate()
    {
        // ----- Generating Random Numbers -----

        // We’re going to need some random numbers later, so create a RNG first. 
        var randomGenerator = new CryptoApiRandomGenerator();
        var random = new SecureRandom(randomGenerator);

        // ----- The Certificate Generator -----

        // Then we need a certificate generator:
        var certificateGenerator = new X509V3CertificateGenerator();

        // ---- Serial Number -----

        // The certificate needs a serial number. This is used for revocation, and usually should be an incrementing 
        // index (which makes it easier to revoke a range of certificates).
        var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
        certificateGenerator.SetSerialNumber(serialNumber);

        // ----- Issuer and Subject Name -----

        // We have to specify the issuer name and subject name. Since this is a self-signed certificate, these are the same.
        //var subjectDN = new X509Name(subjectName);
        var subjectDN = new X509Name($"C=NL, O=SomeCompany, CN={subjectName}");
        var issuerDN = subjectDN;
        certificateGenerator.SetIssuerDN(issuerDN);
        certificateGenerator.SetSubjectDN(subjectDN);

        // Note that Bouncy Castle allows you to omit the subject name, provided you specify a Subject Alternative Name (SAN).

        // ----- Certificate Validation Period -----

        // We need to specify a date range for which this certificate is valid:
        certificateGenerator.SetNotBefore(DateTime.UtcNow.Date);
        certificateGenerator.SetNotAfter(DateTime.UtcNow.Date.AddYears(20));

        // ----- Subject Public Key -----

        // We need to generate the important bit: the subject’s key pair. The public key goes into the certificate, and the private 
        // key remains private. In this example, strength is the key length, in bits. For RSA, 2048-bits should be considered the 
        // minimum acceptable these days.

        const int strength = 2048;
        var keyGenerationParameters = new KeyGenerationParameters(random, strength);
        var keyPairGenerator = new RsaKeyPairGenerator();
        keyPairGenerator.Init(keyGenerationParameters);

        var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
        certificateGenerator.SetPublicKey(subjectKeyPair.Public);

        // ----- Set the Signature Algorithmn & Generating the Certificate -----

        // To generate the certificate, we need to provide the issuer’s private key. 
        // Because this is a self-signed certificate, this is the same as the subject private key.

        // For certificates, the current recommendation seems to be SHA-256 or SHA-512.

        var issuerKeyPair = subjectKeyPair;
        const string signatureAlgorithm = "SHA256WithRSA";
        var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private);
        var bouncyCert = certificateGenerator.Generate(signatureFactory);

        // Note that you’ll still see “SHA1” for the “Thumbprint Algorithm” property. 
        // This is expected: the thumbprint is not the same as the signature.

        // From the above, we’ve generated an X509v3 certificate using the Bouncy Castle libraries. 

        // ----- Convert to a .NET Certificate -----

        // Next we need to convert the Bouncy Castle Certificate to a .NET Certificate. To do this we create a PKCS12 file. 
        // On Windows, these are more commonly known as .PFX files.

        // Lets convert it to X509Certificate2
        X509Certificate2 certificate;

        Pkcs12Store store = new Pkcs12StoreBuilder().Build();
        store.SetKeyEntry($"{subjectName}_key", new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { new X509CertificateEntry(bouncyCert) });
        string exportpw = Guid.NewGuid().ToString("x");

        // Now we’ve got a Pkcs12Store, we can copy it to a stream.
        using (var ms = new MemoryStream())
        {
            store.Save(ms, exportpw.ToCharArray(), random);

            // At this point, we can convert it to a .NET X509Certificate2 object.
            certificate = new X509Certificate2(ms.ToArray(), exportpw, X509KeyStorageFlags.Exportable);
        }

        // Now we’ve got a Pkcs12Store, we can copy it to a stream. For this part, we need to specify a password:
        const string password = "password";
        var stream = new MemoryStream();
        store.Save(stream, password.ToCharArray(), random);

        // Return the newly created certificate back to the callin method.
        return certificate;
    }
}
...