0

I have set up a test C# Https Listener using a self-signed certificate (followed advice in Httplistener with https support ).

The service (runs as a windows service under Local System) and initially (after startup) works well. After a certain period of time (~1.5 hours) calls to https endpoint stop working with Edge/IE complaining:

Can’t connect securely to this page This might be because the site uses outdated or unsafe TLS security settings. If this keeps happening, try contacting the website’s owner.

Chrome complains as follows:

This site can’t be reached The connection was reset. ERR_CONNECTION_RESET

When this happens, checking the certificate store shows that certificates (in Root and My stores) are still present.

Checking the

netsh http show sslcert

Also shows that the registration of the certificate to the port is still in place.

Re-starting the application (re-creating, re-installing and re-bindin certificate to port on which C# http(s) listener listens) helps, but until the next hiccup which may happen in some time (~1.5..2 hours?).

I know that the listener thread is still alive as requests on an unsecure port still work.

Something happens in the meantime and I can't figure out what...

Code:

All starts with a generation of a self-signed certificate:

        // 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);

Followed by adding a newly-generated certificate to localhost Root and Private store:

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

Followed by registering a newly created and installed cert to a port on which Http Listener listens:

netsh.exe http add sslcert ipport=0.0.0.0:{port} certhash={certThumbprint} appid={appid_guid} 
Igor Malin
  • 582
  • 1
  • 7
  • 24
  • After some further extensive search it seems like the reason can be that I don't persist the private key: https://support.microsoft.com/en-us/help/950090/installing-a-pfx-file-using-x509certificate-from-a-standard--net-appli Testing now. – Igor Malin Oct 09 '17 at 00:37

1 Answers1

0

The problem I experienced was as follows:

When you create a self-signed certificate in .NET you construct an instance of X509Certificate(2).

If you don't specify the

X509KeyStorageFlags.PersistKeySet

flag, then the private key that corresponds to your certificate will be eventually removed by garbage collection after all references to your certificate go our of scope (and hence the problem was reproducing not immediately but after a period of time).

To persist the certificate's private key you need to specify the flag as mentioned above.

This is mentioned by Microsoft here: https://support.microsoft.com/en-us/help/950090/installing-a-pfx-file-using-x509certificate-from-a-standard--net-appli

The final working code for self-signed certificate creation is as follows:

        // 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);
Igor Malin
  • 582
  • 1
  • 7
  • 24