Python 3.13: CA cert does not include key usage extension (tested)
Python 3.13 turns on ssl.VERIFY_X509_STRICT by default, so a CA certificate without a keyUsage extension is rejected. Re-issue the CA with keyUsage=critical,keyCertSign,cRLSign, or clear the flag on the SSLContext for that host.
After upgrading to Python 3.13, HTTPS to an internal server (private CA, corporate proxy, self-made test CA) can start failing with certificate verify failed: CA cert does not include key usage extension, even though nothing changed on the server. We reproduced it with a local HTTPS server whose CA was made with a plain openssl req -x509. common.py starts that server on localhost:8963:
import subprocess, sys, time, atexit, os
srv = subprocess.Popen([sys.executable, "/tmp/us397/srv.py", os.environ.get("LEAF", "/tmp/us397/cert.pem"), "/tmp/us397/key.pem"])
atexit.register(srv.terminate)
time.sleep(0.8)
URL = "https://localhost:8963/simple/"
OLD_CA = "/tmp/us397/ca.pem" # made with: openssl req -x509 (no keyUsage)
NEW_CA = "/tmp/us397/ca2.pem"
1. Python 3.13 fails, Python 3.11 works
import ssl, sys, urllib.request
from common import URL, OLD_CA
print(sys.version.split()[0], ssl.OPENSSL_VERSION)
ctx = ssl.create_default_context(cafile=OLD_CA)
try:
print("status:", urllib.request.urlopen(URL, context=ctx).status)
except Exception as e:
print(type(e).__name__, e)
Output:
3.13.5 OpenSSL 3.5.7 9 Jun 2026
URLError <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: CA cert does not include key usage extension (_ssl.c:1029)>
The same script under Python 3.11.17:
3.11.17 OpenSSL 3.5.9 29 Sep 2026
status: 200
2. Why: VERIFY_X509_STRICT is on by default in 3.13
import ssl
ctx = ssl.create_default_context()
print("VERIFY_X509_STRICT set:", bool(ctx.verify_flags & ssl.VERIFY_X509_STRICT))
print("VERIFY_X509_PARTIAL_CHAIN set:", bool(ctx.verify_flags & ssl.VERIFY_X509_PARTIAL_CHAIN))
Output:
VERIFY_X509_STRICT set: True
VERIFY_X509_PARTIAL_CHAIN set: True
Python 3.11.17 printed False for both. Since Python 3.13, ssl.create_default_context() sets VERIFY_X509_STRICT, which makes OpenSSL enforce RFC 5280: a CA certificate must carry a keyUsage extension with keyCertSign. requests (via urllib3) and Poetry failed with the same message in our tests, because they build on this default context.
3. Check the CA certificate
import subprocess
for name in ["ca.pem", "ca2.pem"]:
out = subprocess.run(["openssl", "x509", "-in", f"/tmp/us397/{name}", "-noout", "-ext", "basicConstraints,keyUsage"],
capture_output=True, text=True)
print(f"--- {name}\n{(out.stdout + out.stderr).strip()}")
Output:
--- ca.pem
X509v3 Basic Constraints: critical
CA:TRUE
--- ca2.pem
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
ca.pem (the failing CA) has CA:TRUE but no Key Usage. ca2.pem was created with -addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign".
4. Proper fix: re-issue the CA with keyUsage
import os
os.environ["LEAF"] = "/tmp/us397/cert2.pem" # leaf re-issued by the CA that has keyUsage
import requests
from common import URL, NEW_CA
print(requests.get(URL, verify=NEW_CA).status_code)
Output:
200
With a leaf certificate signed by the new CA, requests verified it normally. If you do not control the CA (a vendor appliance, a corporate proxy), ask its owners to re-issue it; old CAs without keyUsage are exactly what the strict mode is meant to reject.
5. Workaround: clear VERIFY_X509_STRICT for that context
import ssl, urllib.request
from common import URL, OLD_CA
ctx = ssl.create_default_context(cafile=OLD_CA)
ctx.verify_flags &= ~ssl.VERIFY_X509_STRICT
print("strict off:", urllib.request.urlopen(URL, context=ctx).status)
Output:
strict off: 200
Clearing the flag on your own SSLContext restores the 3.12 behaviour while still verifying the chain and hostname. Only do this in code that talks to the affected internal host; do not disable verification entirely (verify=False).
Related: Python SSL: CERTIFICATE_VERIFY_FAILED, self signed certificate in certificate chain, Poetry ignore SSL errors, requests SSLError HTTPSConnectionPool.
Frequently asked questions
Why does Python 3.13 say CA cert does not include key usage extension?
ssl.create_default_context() sets VERIFY_X509_STRICT from 3.13 on; OpenSSL then requires keyUsage on CA certificates. 3.11 accepted the same CA.
How do I fix CA cert does not include key usage extension?
Re-issue the CA with keyUsage=critical,keyCertSign,cRLSign (and basicConstraints CA:TRUE), then re-sign the server certificate.
Can I disable VERIFY_X509_STRICT in Python?
Yes, per context: ctx.verify_flags &= ~ssl.VERIFY_X509_STRICT. Verification of the chain and hostname stays on.
Written with AI assistance. Measured on 2026-10-23 with Python 3.13.5 (OpenSSL 3.5.7), Python 3.11.17 (OpenSSL 3.5.9) and requests 2.34.2 against a local HTTPS server. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python is free.