【问题标题】:Localhost HttpListener with Https support - stops working after a period of time支持 Https 的 Localhost HttpListener - 一段时间后停止工作
【发布时间】:2017-10-08 23:44:52
【问题描述】:

我已经使用自签名证书设置了一个测试 C# Https Listener(遵循 Httplistener with https support 中的建议)。

该服务(在本地系统下作为 Windows 服务运行)和最初(启动后)运行良好。 在一段时间(约 1.5 小时)后,对 https 端点的调用停止使用 Edge/IE 抱怨:

无法安全连接到此页面 这可能是因为该站点使用过时或不安全的 TLS 安全设置。如果这种情况持续发生,请尝试联系网站所有者。

Chrome 报错如下:

无法访问此网站 连接被重置。 ERR_CONNECTION_RESET

发生这种情况时,检查证书存储会显示证书(在 Root 和 My 存储中)仍然存在。

检查

netsh http 显示 sslcert

也说明注册到端口的证书还在。

重新启动应用程序(重新创建、重新安装和重新绑定证书到 C# http(s) 侦听器侦听的端口)会有所帮助,但直到下一个可能在一段时间内发生的故障(~1.5. .2 小时?)。

我知道侦听器线程仍然存在,因为不安全端口上的请求仍然有效。

在此期间发生了一些事情,我不知道是什么......

代码:

一切从生成自签名证书开始:

        // create DN for subject and issuer
        var dn = new CX500DistinguishedName();
        dn.Encode("CN=localhost");

        // create a new private key for the certificate
        var privateKey = new CX509PrivateKey
        {
            ProviderName = "Microsoft Base Cryptographic Provider v1.0",
            MachineContext = false,
            Length = 2048,
            KeySpec = X509KeySpec.XCN_AT_SIGNATURE,
            KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_SIGNING_FLAG,
            FriendlyName = "Application Testing Key",
            ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG
        };
        privateKey.Create();

        var hashobj = new CObjectId();
        hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
            ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
            AlgorithmFlags.AlgorithmFlagsNone, "SHA256");

        // Create the self signing request
        var certificateRequest = new CX509CertificateRequestCertificate();
        certificateRequest.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, privateKey, String.Empty);
        certificateRequest.Subject = dn;
        certificateRequest.Issuer = dn; // the issuer and the subject are the same
        certificateRequest.NotBefore = DateTime.UtcNow.AddDays(-1);
        certificateRequest.NotAfter = DateTime.UtcNow.AddYears(10);
        certificateRequest.HashAlgorithm = hashobj;

        // Set up the Subject Alternative Names extension.
        var nameslist = new CAlternativeNames();
        var alternativeName = new CAlternativeName();
        alternativeName.InitializeFromString(AlternativeNameType.XCN_CERT_ALT_NAME_DNS_NAME, "localhost");
        nameslist.Add(alternativeName);
        var subjectAlternativeNamesExtension = new CX509ExtensionAlternativeNames();
        subjectAlternativeNamesExtension.InitializeEncode(nameslist);
        certificateRequest.X509Extensions.Add((CX509Extension)subjectAlternativeNamesExtension);

        var skiExtension = new CX509ExtensionSubjectKeyIdentifier();
        skiExtension.InitializeEncode(EncodingType.XCN_CRYPT_STRING_BASE64, Convert.ToBase64String(StringToByteArray(certSKI)));
        certificateRequest.X509Extensions.Add((CX509Extension)skiExtension);

        certificateRequest.Encode();

        // Do the final enrollment process
        var enroll = new CX509Enrollment();
        enroll.InitializeFromRequest(certificateRequest); // load the certificate
        enroll.CertificateFriendlyName = "Application Testing Cert";
        var csr = enroll.CreateRequest(); // Output the request in base64
        var pwd = Guid.NewGuid().ToString();
        enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate, csr, EncodingType.XCN_CRYPT_STRING_BASE64, pwd); // and install it back as the response
        var base64encoded = enroll.CreatePFX(pwd, PFXExportOptions.PFXExportChainWithRoot);

        // instantiate the target class with the PKCS#12 data 
        return new X509Certificate2(Convert.FromBase64String(base64encoded), pwd);

随后将新生成的证书添加到 localhost 根和私有存储:

InstallCertificateToCertStore(cert, new X509Store(StoreName.Root, StoreLocation.LocalMachine));
InstallCertificateToCertStore(cert, new X509Store(StoreName.My, StoreLocation.LocalMachine));

随后将新创建并安装的证书注册到 Http Listener 侦听的端口:

netsh.exe http add sslcert ipport=0.0.0.0:{port} certhash={certThumbprint} appid={appid_guid} 

【问题讨论】:

标签: c# ssl self-signed httplistener netsh


【解决方案1】:

我遇到的问题如下:

当您在 .NET 中创建自签名证书时,您会构建 X509Certificate(2) 的实例。

如果你不指定

X509KeyStorageFlags.PersistKeySet

标记,那么在对您的证书的所有引用都超出我们的范围后,垃圾收集最终会删除与您的证书对应的私钥(因此问题不是立即重现,而是在一段时间后重现)。

要保留证书的私钥,您需要指定上述标志。

这是微软在这里提到的: https://support.microsoft.com/en-us/help/950090/installing-a-pfx-file-using-x509certificate-from-a-standard--net-appli

自签名证书创建的最终工作代码如下:

        // create DN for subject and issuer
        var dn = new CX500DistinguishedName();
        dn.Encode("CN=localhost");

        // create a new private key for the certificate
        var privateKey = new CX509PrivateKey
        {
            ProviderName = "Microsoft Base Cryptographic Provider v1.0",
            MachineContext = true,
            Length = 2048,
            KeySpec = X509KeySpec.XCN_AT_SIGNATURE,
            KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES,
            FriendlyName = "App Testing Key",
            ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG
        };
        privateKey.Create();

        var hashobj = new CObjectId();
        hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
            ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
            AlgorithmFlags.AlgorithmFlagsNone, "SHA256");

        // Create the self signing request
        // also see: https://security.stackexchange.com/a/103362
        var certificateRequest = new CX509CertificateRequestCertificate();
        certificateRequest.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, String.Empty);
        certificateRequest.Subject = dn;
        certificateRequest.Issuer = dn; // the issuer and the subject are the same
        certificateRequest.NotBefore = DateTime.UtcNow.AddDays(-1);
        certificateRequest.NotAfter = DateTime.UtcNow.AddYears(10);
        certificateRequest.HashAlgorithm = hashobj;

        // Set up the Subject Alternative Names extension.
        var nameslist = new CAlternativeNames();
        var alternativeName = new CAlternativeName();
        alternativeName.InitializeFromString(AlternativeNameType.XCN_CERT_ALT_NAME_DNS_NAME, "localhost");
        nameslist.Add(alternativeName);
        var subjectAlternativeNamesExtension = new CX509ExtensionAlternativeNames();
        subjectAlternativeNamesExtension.InitializeEncode(nameslist);
        certificateRequest.X509Extensions.Add((CX509Extension)subjectAlternativeNamesExtension);

        var skiExtension = new CX509ExtensionSubjectKeyIdentifier();
        skiExtension.InitializeEncode(EncodingType.XCN_CRYPT_STRING_BASE64, Convert.ToBase64String(StringToByteArray(certSKI)));
        certificateRequest.X509Extensions.Add((CX509Extension)skiExtension);

        certificateRequest.Encode();

        // Do the final enrollment process
        var enroll = new CX509Enrollment();
        enroll.InitializeFromRequest(certificateRequest); // load the certificate
        enroll.CertificateFriendlyName = "App Testing Cert";
        var csr = enroll.CreateRequest(); // Output the request in base64
        var pwd = Guid.NewGuid().ToString();
        enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate, csr, EncodingType.XCN_CRYPT_STRING_BASE64, pwd); // and install it back as the response
        var base64encoded = enroll.CreatePFX(pwd, PFXExportOptions.PFXExportChainWithRoot);

        // instantiate the target class with the PKCS#12 data 
        return new X509Certificate2(Convert.FromBase64String(base64encoded), pwd, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);

【讨论】:

    猜你喜欢
    • 2012-07-09
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-15
    • 2013-11-19
    • 2010-11-30
    • 1970-01-01
    相关资源
    最近更新 更多