Pular para o conteúdo

OpenSSL

redes
Janela do terminal
# arquivo local
openssl x509 -in cert.pem -noout -text # tudo
openssl x509 -in cert.pem -noout -subject -issuer -dates
openssl x509 -in cert.pem -noout -ext subjectAltName # os nomes que o cert cobre
openssl x509 -in cert.pem -noout -fingerprint -sha256
# de um servidor
openssl s_client -connect exemplo.com.br:443 -servername exemplo.com.br </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName

-servername é obrigatório em host com SNI (praticamente todos). Sem ele, você inspeciona o certificado padrão do servidor, que quase nunca é o que você quer.

Janela do terminal
# cadeia completa que o servidor entrega
openssl s_client -showcerts -connect host:443 -servername host </dev/null
# protocolo específico
openssl s_client -connect host:443 -tls1_2 </dev/null
openssl s_client -connect host:443 -tls1_3 </dev/null
# mTLS: apresentando certificado de cliente
openssl s_client -connect host:443 -cert cliente.pem -key cliente.key
# STARTTLS (SMTP, IMAP, Postgres)
openssl s_client -connect smtp.exemplo.com.br:587 -starttls smtp
# cifras aceitas
openssl ciphers -v 'HIGH:!aNULL' | head

Na saída, olhe: Verify return code: 0 (ok) indica cadeia válida; qualquer outro número aponta o problema (21 = não foi possível verificar o primeiro certificado — cadeia incompleta).

Janela do terminal
# expira em quantos dias?
fim=$(openssl s_client -connect host:443 -servername host </dev/null 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
echo $(( ( $(date -d "$fim" +%s) - $(date +%s) ) / 86400 )) dias
# expira nos próximos 30 dias? (código de saída 0 = sim)
openssl x509 -in cert.pem -noout -checkend $((30*86400))
# valida contra uma CA
openssl verify -CAfile ca.pem -untrusted intermediaria.pem cert.pem
# a chave privada corresponde ao certificado? (os hashes devem ser iguais)
openssl x509 -noout -modulus -in cert.pem | openssl sha256
openssl rsa -noout -modulus -in cert.key | openssl sha256

A comparação de módulo é o teste definitivo para “certificado e chave não batem”, um dos erros mais frequentes ao instalar certificado manualmente.

Janela do terminal
# chave privada
openssl genrsa -out servico.key 4096
openssl ecparam -genkey -name prime256v1 -out servico.key # ECDSA, menor e mais rápida
# CSR sem prompts, com SAN (obrigatório hoje)
openssl req -new -key servico.key -out servico.csr \
-subj "/C=BR/ST=SP/L=Sao Paulo/O=Empresa/CN=api.exemplo.com.br" \
-addext "subjectAltName=DNS:api.exemplo.com.br,DNS:www.api.exemplo.com.br"
openssl req -in servico.csr -noout -text # confira antes de enviar à CA
# autoassinado para desenvolvimento (nunca para produção)
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout dev.key -out dev.crt \
-subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

Navegadores modernos ignoram o CN e exigem subjectAltName. Certificado sem SAN não funciona.

De → para Comando
PEM → DER openssl x509 -in cert.pem -outform der -out cert.der
DER → PEM openssl x509 -in cert.der -inform der -out cert.pem
PEM → PKCS#12 openssl pkcs12 -export -out pacote.pfx -inkey chave.key -in cert.pem -certfile cadeia.pem
PKCS#12 → PEM openssl pkcs12 -in pacote.pfx -out tudo.pem -nodes
PKCS#8 openssl pkcs8 -topk8 -in chave.key -out chave-pkcs8.key -nocrypt
Ver chave openssl rsa -in chave.key -noout -text

PEM é o texto com -----BEGIN ...-----. DER é binário. PKCS#12 (.pfx, .p12) empacota certificado e chave, e é o formato pedido por Windows e por alguns balanceadores.

Janela do terminal
openssl rand -base64 32 # segredo aleatório
openssl rand -hex 16
openssl dgst -sha256 arquivo # hash
openssl base64 -d <<< "dGVzdGU=" # decodifica base64 (Secret do Kubernetes)
openssl s_time -connect host:443 # desempenho do handshake
Sintoma Verificação
Funciona no navegador, falha no curl Cadeia incompleta: s_client -showcerts
certificate has expired -checkend 0 e o relógio das duas pontas
hostname mismatch -ext subjectAltName cobre o nome usado?
unable to get local issuer certificate Falta a intermediária, ou a CA interna não está distribuída
handshake failure Protocolo ou cifra incompatível: teste -tls1_2
Certificado e chave não batem Comparação de módulo (acima)
  • Sem -servername, você testa o certificado errado em servidores com SNI.
  • A ordem no arquivo de cadeia importa: folha primeiro, depois intermediárias.
  • Chave privada com permissão frouxa é recusada por muitos serviços — use 600.
  • Relógio errado no cliente invalida qualquer certificado.
  • Certificado autoassinado em produção não é “TLS resolvido”: ele não prova identidade.
  • -nodes significa “sem senha na chave” (no DES), não “sem nós”.