python-3.xgosslpython-requeststls1.2

How to Test TLSv1 Connection for Python Requests to GoLang TLSv1 HTTPS Server


I have a very old Windows 2003 server and I need to test TLSv1 connections to it.

I found some simple HTTPS server example code for GoLang to configure it to use TLSv1. I also created a simple Python3 HTTPS client with the requests module that I am running on my Windows 11 machine.

When I try to connect to the GoLang server on the Windows 2003 server with TLSv1 using the Python3 application, I get 'tls internal error' (see screenshot below).

How can I get the TLSv1 connection to work?

SSL Error

Error:

GoLang server code:

package main

import (
    "crypto/tls"
    "fmt"
    "log"
    "net/http"
    "runtime"
)

func main() {

    fmt.Printf("Hello %s/%s\n", runtime.GOOS, runtime.GOARCH)

    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
        w.Write([]byte("This is an example server.\n"))
    })
    cfg := &tls.Config{
        MinVersion: tls.VersionTLS10,
        MaxVersion: tls.VersionTLS10,
    }
    srv := &http.Server{
        Addr:         ":443",
        Handler:      mux,
        TLSConfig:    cfg,
        TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
    }
    log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
}

Python3 client code:

from requests_toolbelt import SSLAdapter
import requests
import ssl

s = requests.Session()
p = ssl.PROTOCOL_TLSv1
s.mount('https://', SSLAdapter(p))
r = s.get('https://10.2.2.14/', verify=False)

Python3 Client output:

Traceback (most recent call last):
  File "C:<>Programs\Python\Python311\Lib\site-packages\urllib3\connectionpool.py", line 703, in urlopen
    httplib_response = self._make_request(
                       ^^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\urllib3\connectionpool.py", line 386, in _make_request
    self._validate_conn(conn)
  File "C:<>Programs\Python\Python311\Lib\site-packages\urllib3\connectionpool.py", line 1042, in _validate_conn
    conn.connect()
  File "C:<>Programs\Python\Python311\Lib\site-packages\urllib3\connection.py", line 414, in connect
    self.sock = ssl_wrap_socket(
                ^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\urllib3\util\ssl_.py", line 453, in ssl_wrap_socket
    ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\urllib3\util\ssl_.py", line 495, in _ssl_wrap_socket_impl
    return ssl_context.wrap_socket(sock)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\ssl.py", line 517, in wrap_socket
    return self.sslsocket_class._create(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\ssl.py", line 1075, in _create
    self.do_handshake()
  File "C:<>Programs\Python\Python311\Lib\ssl.py", line 1346, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL] internal error (_ssl.c:1002)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:<>Programs\Python\Python311\Lib\site-packages\requests\adapters.py", line 489, in send
    resp = conn.urlopen(
           ^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen
    retries = retries.increment(
              ^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\urllib3\util\retry.py", line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='10.2.2.14', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL] internal error (_ssl.c:1002)')))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:tls_1.0_test_server\test-tls-connect.py", line 24, in <module>
    r = s.get('https://10.2.2.14/', verify=False)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\requests\sessions.py", line 600, in get
    return self.request("GET", url, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\requests\sessions.py", line 587, in request
    resp = self.send(prep, **send_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\requests\sessions.py", line 701, in send
    r = adapter.send(request, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:<>Programs\Python\Python311\Lib\site-packages\requests\adapters.py", line 563, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='10.2.2.14', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL] internal error (_ssl.c:1002)')))

Solution

  • Recent systems have TLS 1.0 disabled by default. One need to explicitly allow this by setting the SECLEVEL in the cipher string. Simply using SSLAdapter with the protocol version is not sufficient for this.

    from requests.adapters import HTTPAdapter
    from requests.packages.urllib3.poolmanager import PoolManager
    import requests
    import ssl
    
    class MyAdapter(HTTPAdapter):
        def init_poolmanager(self, *args, **kwargs):
            context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
            # set low Security Level to allow TLSv1
            context.set_ciphers('DEFAULT:@SECLEVEL=0') 
            super().init_poolmanager(*args, **kwargs, ssl_context=context)
    
    s = requests.Session()
    s.mount('https://', MyAdapter())
    r = s.get('https://10.2.2.14/', verify=False)