Ubuntu – ssl.SSLError: [SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:1108)

pythonssl

I'm stuck with an ssl error!?

Upgraded from 19.10 to 20.04 and get this error:

ssl.SSLError: [SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:1108)

It is raised by a python script calling a rest API to oanda.com.

Connecting to the service with Postman or OANDA's java app both work without fault. Also, have the python script running on an RPi OK, after one change, see below!

20.04 – OpenSSL 1.1.1f 31 Mar 2020

RPi – OpenSSL 1.1.1d 10 Sep 2019

The problem was also on the RPi and research found a couple of suggestions to change the setting CipherString = DEFAULT@SECLEVEL=2 to CipherString = DEFAULT@SECLEVEL=1 in the /etc/ssl/openssl.cnf file. This worked on the RPi but did not work on Ubuntu 20.04.

Any ideas on how to resolve this?


Error report:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 665, in urlopen
    httplib_response = self._make_request(
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 376, in _make_request
    self._validate_conn(conn)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 996, in _validate_conn
    conn.connect()
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 352, in connect
    self.sock = ssl_wrap_socket(
  File "/usr/lib/python3/dist-packages/urllib3/util/ssl_.py", line 370, in ssl_wrap_socket
    return context.wrap_socket(sock, server_hostname=server_hostname)
  File "/usr/lib/python3.8/ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
  File "/usr/lib/python3.8/ssl.py", line 1040, in _create
    self.do_handshake()
  File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:1108)

Best Answer

I experienced this error after upgrading from 18.04 LTS to 20.04 LTS, whilst trying to connect to an Exchange server. The cause is because the default upstream Debian OpenSSL settings have become more secure. In OpenSSL, CipherString is now set to DEFAULT@SECLEVEL=2

The background/rationale for this change is detailed here: https://weakdh.org/

Ideally, the server's security should be improved rather than implementing this work-around on the client. However, in many cases, this will not be possible.

I was able to workaround it by adjusting the ciphers used for the connection by creating an SSLContext. Here is an example of the code (for SMTPS connection - sending an e-mail):

connection = smtplib.SMTP(
    config['Email']['host'] + ':' + config['Email']['port'])
context=ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
# Either of the following context settings worked for me - choose one
# context.set_ciphers('HIGH:!DH:!aNULL')
context.set_ciphers('DEFAULT@SECLEVEL=1')
connection.starttls(context=context)
connection.login(config['Email']['user'], config['Email']['pw'])
connection.sendmail(config['Email']['sender'], recipients, msg.as_string())
connection.close()

The important lines are these - chose one of these CipherString adjustments:

context.set_ciphers('HIGH:!DH:!aNULL')
context.set_ciphers('DEFAULT@SECLEVEL=1')

BUT prefer a server fix, if at all possible!

To adapt the above for urllib3, see this answer:

How to select specific the cipher while sending request via python request module

Related Question