sslStream.AuthenticateAsServer ISSUES System.ComponentModel.Win32Exception(0x80004005):无法识别提供给程序包的凭据

时间:2019-06-23 21:47:30

标签: ssl ssl-certificate x509certificate2 sslstream

我正在将MSDN sslStream类用于TCP服务器/客户端。 我使用充气城堡NuGet包创建了X509Certificate2。

我的代码首先创建证书,然后将其转换为.NET证书并将其放入本地计算机/个人证书存储中。将证书放入存储区后,我将以编程方式将.cer证书导出到C盘上的程序安装文件夹中。

如果我在Visual Studio中的调试模式下运行应用程序,则所有步骤都将执行而没有任何问题,并且我可以使用SSL流运行TCP Server。在我的方案中,我只是使用证书从TCP客户端连接中验证服务器,并将其纯粹用于加密目的。

但是-我程序的生产版本需要作为Windows服务运行,我可以在其中使用TopShelf NuGet软件包。

我面临的挑战是,当运行与Windows Service相同的程序时,我会遇到权限问题。如果我在Visual Studio中运行该程序并在同一台计算机上测试TCP客户端和服务器,则一切正常。我猜这是因为SSL Stream类正在使用Windows管理员帐户从证书存储区访问X509证书,因此能够访问私钥信息。

但是,如果我运行与Windows Service相同的程序,即首先创建证书,然后尝试运行TCP Server,则会出现问题,该问题似乎与X509证书中对私钥信息的访问权限有关。证书存储。

当程序作为Windows服务运行时,我无法解决使程序成功运行所需的条件。请注意,在该过程中添加手动配置步骤不是一个选择,因为该程序旨在由普通用户安装,并通过编程方式进行所有设置,而不用让他们担心...

下面的代码是我用于创建证书的类,我再次强调,当以管理员身份登录Windows并在调试模式下运行时,它确实可以工作,这是在运行与Windows Service相同的程序时全部失败...

 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;
    }
}

0 个答案:

没有答案