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


20 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))