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:
- Edit
/etc/ssl/openssl.cnf
- 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))