Mensagem de erro
Signature verification failed
PyJWT (Python) — jwt.exceptions.InvalidSignatureError
O que significa
O PyJWT decodificou a estrutura do token e executou a verificação da assinatura, que falhou: a assinatura não corresponde à chave e ao payload. InvalidSignatureError é subclasse de DecodeError, então um except DecodeError amplo demais pode classificar isso erroneamente como problema de parsing — trata-se de uma divergência entre chave e conteúdo. Para tokens ES256 há uma sutileza extra: a assinatura JWS precisa estar na forma bruta r‖s de 64 bytes, então tokens gerados por signatários feitos à mão que emitiram ASN.1 DER falham aqui mesmo quando a chave está certa.
Por que acontece
Uma chave diferente da que o emissor usou
comumUm par de chaves rotacionado em que o verificador manteve a chave pública antiga, o PEM errado copiado e colado ou dois serviços que leem versões diferentes do segredo no gerenciador de segredos. Com emissores baseados em JWKS, uma divergência de kid tem o mesmo efeito.
Um signatário feito à mão produziu a assinatura errada
ocasionalAlém de DER em vez de r‖s bruto, um signatário caseiro que calcula o hash com SHA-384 sob um cabeçalho ES256, ou que assina o JSON em vez do texto base64url header.payload, gera uma assinatura bem formada que nunca passa na verificação. Uma chave danificada ao passar pelas camadas de configuração não cai neste erro: um PEM com sequências \n literais falha ao carregar com “Unable to load PEM file”, e aspas ou indentação ao redor de um PEM intacto são toleradas.
Token alterado depois da assinatura
ocasionalUm middleware que serializa de novo o JSON do payload ou muda um único caractere altera os bytes assinados. A assinatura cobre exatamente o texto base64url — mudanças cosméticas não são cosméticas. (Padding base64 adicionado é rejeitado antes, como “Invalid payload padding”, e um token cortado, como “Not enough segments”.)
Como corrigir
- 1.
Calcular a impressão digital da chave nos dois lados
Imprima uma impressão digital SHA-256 da chave pública que o verificador carregou e compare-a com a do emissor. Uma diferença encerra a busca; uma correspondência aponta, em vez disso, para os bytes do token.
python import hashlib from cryptography.hazmat.primitives import serialization public_key = serialization.load_pem_public_key(pem_bytes) # the key the verifier uses spki = public_key.public_bytes(serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo) print(hashlib.sha256(spki).hexdigest()) # compare with the issuer's - 2.
Verificar o kid antes de supor corrupção
Leia o cabeçalho não verificado e confirme que você está verificando com a chave que o token indica. Com várias chaves do emissor, resolva pelo kid por meio do PyJWKClient.
python import jwt header = jwt.get_unverified_header(token) print(header) # {'alg': 'ES256', 'kid': '...'} from jwt import PyJWKClient key = PyJWKClient("https://issuer.example/.well-known/jwks.json") \ .get_signing_key_from_jwt(token).key payload = jwt.decode(token, key, algorithms=["ES256"], audience="api://orders")