Mensagem de erro
verify error:num=20:unable to get local issuer certificate
Também aparece como
- Verify return code: 20 (unable to get local issuer certificate)
- error 20 at 0 depth lookup: unable to get local issuer certificate
O s_client imprime “verify error:num=20:…” durante o handshake e “Verify return code: 20 (…)” no resumo; o openssl verify imprime “error 20 at <depth> depth lookup: …”. Por padrão, o s_client continua depois de erros de verificação; adicione -verify_return_error para que ele falhe.
OpenSSL CLI (openssl s_client / openssl verify) — X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
O que significa
Erro 20 do OpenSSL (X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY): ao montar a cadeia, o OpenSSL chegou a um certificado cujo emissor não conseguiu encontrar, nem entre os certificados que o servidor enviou, nem no trust store. A linha de profundidade (depth) impressa logo antes do erro indica o certificado cujo emissor está faltando. É o mesmo código por trás do (60) do curl, do CERTIFICATE_VERIFY_FAILED do Python e do UNABLE_TO_GET_ISSUER_CERT_LOCALLY do Node.js.
Por que acontece
A cadeia termina em uma raiz que não está no trust store
comumUma raiz privada ou da empresa (ou um proxy com inspeção de TLS que envia só o próprio intermediário). A linha de maior profundidade indica um intermediário cujo emissor você não tem.
O servidor não envia o intermediário
comumSó aparece depth=0, e o emissor desse certificado é uma CA intermediária. O OpenSSL nunca baixa intermediários ausentes, então o servidor precisa enviá-los.
Nenhum trust store foi usado no teste
ocasionalO openssl verify com um certificado isolado, ou o s_client em uma imagem sem ca-certificates, não tem raízes com que completar a cadeia, mesmo para sites públicos.
Como corrigir
- 1.
Ler as linhas de profundidade
A profundidade 0 é o certificado do servidor; números maiores são os intermediários que ele enviou. A última linha de profundidade antes do erro corresponde ao certificado cujo emissor está faltando.
bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>&1 | grep -E '^depth|verify' # depth=1 CN=Example Corp Issuing CA <- the highest certificate received # verify error:num=20:... <- its issuer is not in the trust store - 2.
Testar com a raiz esperada
O -CAfile com a raiz correta (e -verify_return_error para tornar as falhas fatais) mostra se essa raiz completa a cadeia; “Verify return code: 0 (ok)” significa que sim.
bash # Verify against a specific root (exit code and "Verify return code: 0 (ok)" confirm it) echo | openssl s_client -connect api.example.com:443 -servername api.example.com \ -CAfile corp-root-ca.pem -verify_return_error 2>&1 | grep 'Verify return code' - 3.
Verificar arquivos de certificado offline
Passe ao openssl verify a raiz como -CAfile e os intermediários como -untrusted; um certificado folha sozinho não pode ser verificado.
bash # Offline: root as trust anchor, intermediate(s) as untrusted helpers openssl verify -CAfile corp-root-ca.pem -untrusted intermediate.pem server.pem - 4.
Instalar a raiz no repositório do sistema
Quando a cadeia termina legitimamente em uma raiz da empresa, adicione-a ao repositório da distribuição; as ferramentas baseadas em OpenSSL da máquina passam a confiar nela.
bash sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt sudo update-ca-certificates # openssl reads /etc/ssl/certs by default on Debian/Ubuntu
Erros relacionados
verify error:num=21:unable to verify the first certificateOpenSSL CLI (openssl s_client)verify error:num=19:self-signed certificate in certificate chainOpenSSL CLI (openssl s_client / openssl verify)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local…Python ssl module (OpenSSL)