How to Bypass OpenSSL’s “ee key too small” Error for Legacy Client Certificates (1024-bit RSA)


5 views

Modern OpenSSL versions (since 1.1.1, specifically 1.1.1a in 2018) enforce minimum RSA key sizes of 2048 bits for security compliance. This change reflects NIST's deprecation of 1024-bit keys due to advances in computational power making them vulnerable to brute-force attacks.

Add this to your Python SSL context configuration:

import ssl
context = ssl.create_default_context()
context.set_ciphers('DEFAULT:@SECLEVEL=1')  # Lowers security level

Create or modify /etc/ssl/openssl.cnf:

[system_default_sect]
MinProtocol = TLSv1.2
CipherString = DEFAULT:@SECLEVEL=1

For legacy systems where certificate replacement isn't feasible, consider these approaches:

# Python requests workaround
import requests
session = requests.Session()
session.verify = False  # Disables cert verification (INSECURE)

While workarounds exist, you should:

  • Phase out 1024-bit certificates
  • Implement certificate rotation scripts
  • Use OpenSSL's batch processing for mass reissuance

Lowering security levels exposes systems to:

Risk Impact
MITM Attacks High
Decryption Attacks Moderate
Compliance Violations Severe

Modern OpenSSL versions (1.1.1+) enforce stricter security policies regarding key lengths. The error SSL routines:SSL_CTX_use_certificate:ee key too small specifically blocks certificates with RSA keys shorter than 2048 bits, a change introduced in OpenSSL 1.1.1 to phase out vulnerable 1024-bit keys. This security policy became effective with OpenSSL's 2018 release.

For Python applications using ssl or requests, you can modify the OpenSSL security level:


import ssl
import urllib3
from requests.adapters import HTTPAdapter

class LegacySSLAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        context = ssl.create_default_context()
        context.set_ciphers('DEFAULT@SECLEVEL=1')  # Lower security level
        kwargs['ssl_context'] = context
        return super().init_poolmanager(*args, **kwargs)

# Usage example:
session = requests.Session()
session.mount("https://", LegacySSLAdapter())
response = session.get("https://legacy-api.example.com", cert=("client.crt", "client.key"))

For non-Python applications or system-level changes:

  1. Edit /etc/ssl/openssl.cnf
  2. Add/modify:

[system_default_sect]
CipherString = DEFAULT@SECLEVEL=1

OpenSSL defines five security levels (0-4). Level 2 (default in 1.1.1+) requires:

  • Minimum 2048-bit RSA/DH keys
  • Minimum 224-bit ECC keys
  • SHA-2 signatures

Setting SECLEVEL=1 reduces these requirements while maintaining other security features.

While this bypass works, consider:

  • 1024-bit RSA can be broken with sufficient resources
  • This creates MITM vulnerability potential
  • Best practice: Update certificates where possible

For temporary legacy support, combine with network-level protections like VPN tunnels.

If you must maintain backward compatibility but want to mitigate risks:


import hashlib

def verify_legacy_cert(cert):
    # Hardcode expected fingerprint of your legacy cert
    trusted_fingerprint = "a1b2c3d4e5f6..."
    der_cert = cert.public_bytes(encoding=serialization.Encoding.DER)
    current_fp = hashlib.sha256(der_cert).hexdigest()
    return current_fp == trusted_fingerprint

context = ssl.create_default_context()
context.load_verify_locations(cafile="ca.pem")
context.verify_mode = ssl.CERT_REQUIRED
context.set_ciphers('DEFAULT@SECLEVEL=1')
context.verify_flags |= ssl.VERIFY_ALLOW_PROXY_CERTS
context.set_verify_callback(lambda conn, cert, errno, depth, ok: 
    ok and verify_legacy_cert(cert))