Using twapi::tls_socket on Windows 7

When I was trying to use twapi::tls_socket on Windows 7 to connect to a remote site, it failed with a "The function requested is not supported" error. Using Wireshark I found that it was trying to connect using TLS 1.0. Many sites have now disabled TLS 1.0 due to security concerns. During a very interesting debug session with Ashok in the Tcl Chatroom, the problem was resolved.

TL;DR: The following two things needed to be fixed:

  1. Enable TLS 1.2
  2. Update the Trusted Root CA certificate

Debug session

Apparently, TLS 1.2 is not enabled by default on Windows 7. This can be checked in the registry:

registry get "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\TLS 1.2\\Client" DisabledByDefault

If that produces an error: "unable to open key: The system cannot find the file specified", or it returns a value other than 0, then TLS 1.2 is disabled. To enable TLS 1.2, run tcl(kit) as administrator, and execute:

registry set "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\TLS 1.2\\Client" DisabledByDefault 0 dword

As this is Windows, it's probably a good idea to reboot before continuing.


The above fix changed the behavior. The twapi::tls_socket command now failed with "The received certificate has expired". A quick test was to disable the certificate check:

proc alwaysok {args} {return 1}
twapi::tls_socket -verifier alwaysok $host 443

This succeeded. The next step was to determine what the problem with the certificate may be:

proc verify {remote_name chan ctx} {
    set remote_cert [twapi::sspi_remote_cert $ctx]
    try {
        if {$remote_name eq ""} {
            set status [twapi::cert_tls_verify $remote_cert -revocationcheck none]
        } else {
            set status [twapi::cert_tls_verify $remote_cert -revocationcheck none -server $remote_name]
        }
        puts "status: $status"
        return [string equal $status ok]
    } finally {
        twapi::cert_release $remote_cert
    }
}
     
set so [twapi::tls_socket -verifier [list verify $host] $host 443]

This reported: "untrustedroot". Using a browser, it was determined that the site used was certified by Let's Encrypt, which uses ISRG Root X1 as its root CA. The browser also allowed the certificate for this Root CA to be downloaded as a pem file. The idea was to load this into the Windows certificate store using mmc. Unfortunately mmc doesn't want to read pem files. This was fixed by converting the pem file to crt. I did that on linux:

openssl x509 -outform der -in isrg-root-x1.pem -out isrg-root-x1.crt

To import the certificate in the Windows certificate store:

  • Start mmc
  • File -> Add/Remove Snap-in...
  • Add Certificates to the Selected snap-ins.
  • Select Computer accounts and Next, then Finish and OK.
  • Open Console Root -> Certificates (Local Computer) -> Trusted Root Certification Authorities -> Certificates.
  • Delete any expired ISRG Root X1 certificate.
  • Go to: Action -> All Tasks -> Import...
  • Next -> Browse to select the crt file -> Next -> Next -> Finish