Skip to main content

Command Palette

Search for a command to run...

Tutorial completo: como usar `-Djavax.net.debug=ssl,handshake` para diagnosticar TLS/SSL em aplicações Java

Updated
21 min readView as Markdown

Quando uma aplicação Java falha ao fazer uma conexão HTTPS, LDAPS, SMTPS, conexão JDBC sobre TLS ou qualquer outra comunicação baseada em TLS, a exceção normalmente é pouco esclarecedora:

javax.net.ssl.SSLHandshakeException: PKIX path building failed

ou:

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

ou ainda:

javax.net.ssl.SSLException: Received fatal alert: protocol_version

Nesses casos, uma das ferramentas mais úteis do próprio Java é:

-Djavax.net.debug=ssl,handshake

Ela ativa o debug interno do JSSE (Java Secure Socket Extension), permitindo acompanhar praticamente passo a passo o estabelecimento da conexão TLS. A documentação oficial do Java descreve javax.net.debug como o mecanismo de dynamic debug tracing específico do JSSE. (Oracle Docs)


1. O que é javax.net.debug?

javax.net.debug é uma System Property da JVM usada para habilitar informações de diagnóstico do mecanismo SSL/TLS utilizado pelo Java.

Por exemplo:

java -Djavax.net.debug=ssl,handshake -jar minha-aplicacao.jar

O ponto importante é entender que:

-Djavax.net.debug=ssl,handshake

não é uma configuração do Spring Boot, Tomcat, Maven ou HTTP Client.

É uma propriedade da JVM.

Portanto, ela precisa ser passada antes da execução da aplicação.


2. O que significa cada parte?

Vamos decompor:

-Djavax.net.debug=ssl,handshake

-D

Indica que estamos definindo uma System Property da JVM.

É equivalente, dentro do Java, a:

System.setProperty("javax.net.debug", "ssl,handshake");

Mas existe uma diferença importante: colocar a propriedade na linha de comando é muito mais apropriado para diagnóstico, porque a JVM já inicia com o debug habilitado.


javax.net.debug

É o nome da propriedade reconhecida pelo JSSE.


ssl

Ativa o mecanismo de debug relacionado ao SSL/TLS.


handshake

Solicita que sejam exibidas as mensagens do TLS handshake.

Assim:

-Djavax.net.debug=ssl,handshake

pode ser interpretado conceitualmente como:

"Ative o diagnóstico do JSSE e mostre o que está acontecendo durante o handshake TLS."

A documentação atual do JSSE lista handshake como uma das categorias de debug disponíveis sob ssl. (Oracle Docs)


3. Antes de tudo: SSL x TLS

Apesar de o parâmetro se chamar:

javax.net.debug=ssl

isso não significa que sua aplicação esteja necessariamente usando SSL antigo.

O nome SSL permaneceu na API Java por compatibilidade histórica.

Por exemplo, uma aplicação moderna pode estar usando:

TLSv1.3

e ainda assim o debug será ativado através de:

-Djavax.net.debug=ssl

É comum encontrar no log coisas como:

ClientHello
ServerHello
TLSv1.3
Certificate
Finished

4. O que é o TLS handshake?

Antes de entender o log, é importante compreender o processo que ele está mostrando.

Imagine:

Java Application
       |
       | HTTPS
       v
    Servidor

Antes de transmitir os dados da aplicação, os dois lados precisam negociar diversos parâmetros de segurança.

Simplificadamente:

CLIENTE                              SERVIDOR

   | -------- ClientHello ----------> |
   |                                  |
   | <-------- ServerHello ---------- |
   | <--------- Certificate ---------- |
   |                                  |
   | -------- ClientKeyExchange ----> |
   |                                  |
   | -------- Finished -------------> |
   |                                  |
   | <--------- Finished ------------ |
   |                                  |
   | ===== conexão segura =========== |

Em TLS 1.3 o processo é diferente e mais compacto, mas a ideia geral permanece: os lados negociam parâmetros criptográficos, autenticam o servidor e estabelecem chaves de sessão.

O JSSE é justamente a camada Java responsável por implementar esse processo. (Oracle Docs)


5. Primeiro teste: ssl,handshake

Para uma aplicação Java simples:

java -Djavax.net.debug=ssl,handshake -jar app.jar

Se você estiver executando uma aplicação Spring Boot:

java -Djavax.net.debug=ssl,handshake -jar minha-api.jar

Se estiver usando Maven:

mvn spring-boot:run \
  -Dspring-boot.run.jvmArguments="-Djavax.net.debug=ssl,handshake"

No Gradle:

./gradlew bootRun \
  --args=''

ou configure a JVM da tarefa bootRun para receber:

-Djavax.net.debug=ssl,handshake

6. Atenção: -D não é argumento da aplicação

Um erro bastante comum é colocar:

java -jar app.jar -Djavax.net.debug=ssl,handshake

Isso está errado para esse propósito.

O correto é:

java -Djavax.net.debug=ssl,handshake -jar app.jar

A diferença é:

java
  |
  +-- opções da JVM
  |      |
  |      +-- -Djavax.net.debug=...
  |
  +-- -jar
         |
         +-- aplicação

7. O primeiro nível: ssl

Você pode começar simplesmente com:

-Djavax.net.debug=ssl

Exemplo:

java -Djavax.net.debug=ssl -jar app.jar

Isso fornece informações gerais do mecanismo SSL/TLS, sem necessariamente mostrar todo o conteúdo detalhado do handshake.

A documentação define ssl como o modo de debug SSL que exclui determinados dumps mais detalhados, como data, packet e plaintext. (Oracle Docs)


8. ssl,handshake: o modo mais útil para começar

Na prática, este é um dos comandos que eu mais recomendo para investigação:

-Djavax.net.debug=ssl,handshake

Ele permite enxergar mensagens como:

ClientHello
ServerHello
Certificate
CertificateVerify
Finished

e informações relacionadas à negociação.

Por exemplo, você pode encontrar algo parecido com:

"ClientHello": {
  "client version"      : "TLSv1.2",
  "random"              : "...",
  "session id"          : "...",
  "cipher suites"       : "[TLS_AES_256_GCM_SHA384, ...]",
  "extensions"          : [...]
}

Isso já permite responder perguntas extremamente importantes.

Por exemplo:

  • Qual versão TLS o cliente está oferecendo?
  • Qual cipher suite está sendo negociada?
  • O servidor respondeu?
  • O certificado foi recebido?
  • O handshake chegou ao Finished?
  • Em qual etapa ocorreu a falha?

9. Uma das variações mais importantes: trustmanager

Se o problema envolve certificado, eu normalmente aumentaria o nível de diagnóstico para:

-Djavax.net.debug=ssl,handshake,trustmanager

Exemplo:

java \
  -Djavax.net.debug=ssl,handshake,trustmanager \
  -jar app.jar

Essa combinação é extremamente útil para problemas como:

PKIX path building failed
unable to find valid certification path
unable to find valid certification path to requested target
SunCertPathBuilderException

10. Por que trustmanager é tão importante?

Durante uma conexão TLS, o servidor normalmente envia seu certificado:

Servidor
   |
   | Certificate
   v
Java

O Java precisa decidir:

"Eu confio nesse certificado?"

Para isso entra em ação o TrustManager.

Simplificadamente:

Certificate recebido
        |
        v
    TrustManager
        |
        v
  Truststore Java
        |
        v
  certificado confiável?
      /       \
    SIM        NÃO
     |          |
     v          v
 continua     falha

O debug:

-Djavax.net.debug=ssl,handshake,trustmanager

pode revelar informações sobre a decisão do TrustManager.

Isso é particularmente importante quando você está trabalhando com:

  • certificados internos;
  • CA corporativa;
  • certificados autoassinados;
  • certificados intermediários;
  • ambientes de homologação;
  • proxies corporativos;
  • TLS inspection;
  • servidores LDAP/LDAPS;
  • APIs internas.

11. Diagnóstico clássico: PKIX path building failed

Imagine:

javax.net.ssl.SSLHandshakeException:
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target

Uma primeira tentativa seria:

java \
  -Djavax.net.debug=ssl,handshake,trustmanager \
  -jar app.jar

Procure no log por palavras como:

trust
TrustManager
certificate
X509
PKIX
trusted
server certificate

Você poderá descobrir, por exemplo, que o servidor apresentou:

CN=api.empresa.local

mas a CA que assinou esse certificado não está no truststore utilizado pela JVM.


12. Descobrindo qual truststore o Java está utilizando

Outra propriedade extremamente útil é:

javax.net.ssl.trustStore

Por exemplo:

-Djavax.net.ssl.trustStore=/opt/app/certs/truststore.p12

Você pode combinar:

java \
  -Djavax.net.debug=ssl,handshake,trustmanager \
  -Djavax.net.ssl.trustStore=/opt/app/certs/truststore.p12 \
  -jar app.jar

Se for necessário informar o tipo:

-Djavax.net.ssl.trustStoreType=PKCS12

E eventualmente:

-Djavax.net.ssl.trustStorePassword=senha

Cuidado: evitar colocar senha diretamente na linha de comando em ambientes compartilhados, porque ela pode ficar exposta em informações de processo ou histórico.


13. keymanager: quando o problema é certificado do cliente

Agora imagine o cenário inverso.

O servidor exige:

"Cliente, apresente um certificado."

Isso é mTLS (Mutual TLS).

Nesse caso:

             TLS
Java <----------------> Servidor
 |                         |
 | certificado cliente     |
 +------------------------>|

Você pode precisar investigar o KeyManager.

Use:

-Djavax.net.debug=ssl,handshake,keymanager

Por exemplo:

java \
  -Djavax.net.debug=ssl,handshake,keymanager \
  -jar app.jar

Isso ajuda a investigar problemas relacionados a:

  • certificado de cliente;
  • keystore;
  • chave privada;
  • seleção do alias;
  • certificado incompatível;
  • ausência de certificado apropriado.

14. keymanager x trustmanager

Essa distinção é fundamental.

TrustManager

Pergunta:

"Em quem eu confio?"

Normalmente relacionado ao certificado recebido do servidor.

Servidor
   |
   | certificado
   v
TrustManager

KeyManager

Pergunta:

"Qual certificado/chave eu devo apresentar?"

Normalmente relacionado ao certificado do próprio cliente.

KeyManager
    |
    | certificado + chave privada
    v
Servidor

Portanto:

Problema Debug
Não confia no certificado do servidor trustmanager
Não consegue escolher certificado do cliente keymanager
mTLS keymanager + trustmanager
Negociação TLS handshake

15. sslctx

Outra opção interessante:

-Djavax.net.debug=ssl,sslctx

ou:

-Djavax.net.debug=ssl,handshake,sslctx

Ela permite investigar informações relacionadas ao SSLContext.

Isso é útil quando você suspeita que a aplicação está utilizando uma configuração diferente daquela que você imaginava.

Por exemplo:

SSLContext
   |
   +-- KeyManager
   |
   +-- TrustManager
   |
   +-- protocolos
   |
   +-- cipher suites

16. defaultctx

Para investigar a inicialização do contexto SSL padrão:

-Djavax.net.debug=ssl,defaultctx

Pode ser útil quando você está tentando entender:

"De onde o Java está pegando essa configuração SSL?"


17. session

Use:

-Djavax.net.debug=ssl,session

ou:

-Djavax.net.debug=ssl,handshake,session

Isso ajuda a investigar informações relacionadas às sessões TLS.

É particularmente interessante quando você está analisando:

  • reutilização de sessão;
  • criação de sessões;
  • comportamento de conexões persistentes;
  • diferenças entre primeira conexão e conexões subsequentes.

18. sessioncache

Relacionado ao cache de sessões:

-Djavax.net.debug=ssl,sessioncache

Pode ser útil em problemas mais avançados de performance e reutilização de sessões TLS.


19. record

Agora começamos a entrar em um nível mais baixo.

-Djavax.net.debug=ssl,record

Enquanto:

handshake

mostra as mensagens do handshake, record mostra informações sobre os TLS records.

A estrutura conceitual é:

Aplicação
    |
    v
TLS Handshake
    |
    v
TLS Records
    |
    v
TCP

Portanto:

handshake

é geralmente mais amigável para diagnóstico.

Já:

record

é mais detalhado.


20. packet

Você pode adicionar:

-Djavax.net.debug=ssl,record,packet

Isso permite visualizar informações de pacotes TLS em nível mais baixo.

Por exemplo:

java \
  -Djavax.net.debug=ssl,record,packet \
  -jar app.jar

É útil principalmente quando você já está investigando um problema complexo de protocolo.

Para problemas comuns de certificado, normalmente não é necessário.


21. plaintext

Existe também:

-Djavax.net.debug=ssl,record,plaintext

Isso pode produzir dumps do conteúdo de plaintext relacionado aos TLS records.

É uma opção que exige muito cuidado.

Dependendo do contexto, informações sensíveis podem aparecer nos logs.

Nunca trate esse nível de debug como algo apropriado para produção.


22. data

Outra opção:

-Djavax.net.debug=ssl,handshake,data

Ela adiciona dumps hexadecimais das mensagens de handshake.

Exemplo conceitual:

0000: 16 03 03 ...
0010: 01 00 ...
0020: ...

Isso é útil quando você precisa analisar os bytes reais de uma mensagem TLS.

Para a maioria dos desenvolvedores Java:

ssl,handshake

é suficiente.


23. verbose

Você também pode usar:

-Djavax.net.debug=ssl,handshake,verbose

Isso aumenta a quantidade de detalhes exibidos durante o handshake.

Uma combinação possível:

-Djavax.net.debug=ssl,handshake,verbose

E ainda mais detalhada:

-Djavax.net.debug=ssl,handshake,verbose,data

24. all

Existe:

-Djavax.net.debug=all

Esse é o modo "metralhadora".

Ele pode produzir uma quantidade enorme de informações.

A documentação oficial define all como a ativação de todo o debug disponível. (Oracle Docs)

Eu recomendo não começar com all.

Prefira:

-Djavax.net.debug=ssl,handshake

e vá aumentando conforme a necessidade.


25. help

Existe uma opção especialmente útil:

-Djavax.net.debug=help

Por exemplo:

java -Djavax.net.debug=help MinhaAplicacao

Ela mostra as opções disponíveis na implementação JSSE daquele JDK.

Um detalhe importante: quando help é usado, a aplicação não continua normalmente; o mecanismo de debug imprime as opções e encerra. A documentação oficial também alerta para esse comportamento. (Oracle Docs)

Isso é importante porque você pode estar usando Java 17, 21, 25 etc., e as opções disponíveis podem variar entre versões/implementações.


26. ssl,handshake x ssl:handshake

Você pode encontrar os dois formatos:

-Djavax.net.debug=ssl,handshake

e:

-Djavax.net.debug=ssl:handshake

Ambos são aceitos.

Também é possível encontrar:

-Djavax.net.debug=SSL,handshake

A documentação indica que separadores como vírgula ou dois-pontos podem ser utilizados e que a ordem das opções não é relevante. (Oracle Docs)

Eu prefiro:

-Djavax.net.debug=ssl,handshake

por ser mais fácil de ler.


27. A combinação que eu mais recomendo

Para problemas comuns de HTTPS:

-Djavax.net.debug=ssl,handshake

Para problemas de certificado:

-Djavax.net.debug=ssl,handshake,trustmanager

Para mTLS:

-Djavax.net.debug=ssl,handshake,keymanager,trustmanager

Para investigação mais profunda:

-Djavax.net.debug=ssl,handshake,verbose

Para análise de protocolo:

-Djavax.net.debug=ssl,handshake,record,packet

Para investigação extremamente detalhada:

-Djavax.net.debug=all

28. Tabela prática das principais opções

Opção O que mostra Quando usar
ssl Debug geral JSSE Primeiro diagnóstico
handshake Mensagens TLS handshake Quase sempre
trustmanager Validação de certificados PKIX/certificados
keymanager Seleção de certificados/chaves locais mTLS
sslctx SSLContext Configuração SSL
defaultctx Inicialização do contexto padrão Configuração padrão
session Sessões TLS Reuso de sessão
sessioncache Cache de sessões Performance/sessões
record TLS records Diagnóstico avançado
packet Pacotes TLS Baixo nível
data Hex dump do handshake Análise de protocolo
plaintext Dados plaintext Diagnóstico extremamente detalhado
verbose Mais detalhes Investigação avançada
all Tudo Último recurso
help Lista opções Descobrir recursos disponíveis

As categorias acima são documentadas pelo JSSE, incluindo handshake, keymanager, trustmanager, record, session, sslctx, data, verbose, packet e plaintext. (Oracle Docs)


29. Como interpretar o ClientHello

Um dos primeiros pontos importantes no log será algo relacionado a:

ClientHello

Esse é o cliente Java dizendo ao servidor:

"Estas são as versões TLS, extensões e cipher suites que eu consigo utilizar."

Você pode encontrar informações como:

ClientHello
    client version
    random
    session id
    cipher suites
    compression methods
    extensions

As extensões são especialmente importantes.

Por exemplo:

server_name
supported_groups
signature_algorithms
supported_versions
key_share

30. supported_versions

Em versões modernas do TLS, você pode encontrar algo semelhante a:

"supported_versions": [
    TLSv1.3,
    TLSv1.2
]

Isso significa que o cliente está anunciando suporte a essas versões.

Se o servidor só suporta:

TLSv1.0

por exemplo, pode ocorrer incompatibilidade.


31. Identificando problemas de versão TLS

Imagine:

Client:
TLSv1.3
TLSv1.2

e o servidor:

TLSv1.0

Dependendo das configurações de segurança do JDK, a conexão pode falhar.

Você poderá encontrar:

SSLHandshakeException

ou:

protocol_version

ou:

handshake_failure

Nesse caso, o debug permite descobrir que o problema aconteceu durante a negociação do protocolo, antes mesmo de uma possível validação normal do certificado.


32. Identificando problemas de cipher suite

Outro ponto importante do ClientHello são as cipher suites.

Você pode encontrar algo como:

cipher suites:
[
 TLS_AES_256_GCM_SHA384,
 TLS_AES_128_GCM_SHA256,
 TLS_CHACHA20_POLY1305_SHA256
]

O servidor precisa conseguir selecionar uma combinação compatível.

Se não houver interseção:

Java                  Servidor

AES/GCM       <----X---->    cipher incompatível
ChaCha20      <----X---->    cipher incompatível
...

pode aparecer:

handshake_failure

O debug ajuda a descobrir que o problema está na negociação criptográfica.


33. Identificando o certificado do servidor

Durante o handshake você verá uma mensagem:

Certificate

A partir dela você consegue investigar coisas como:

Subject
Issuer
Validity
Key type
Certificate chain

Por exemplo:

Subject: CN=api.exemplo.com
Issuer: CN=Minha CA Corporativa

Isso é extremamente útil quando:

curl funciona

mas:

Java não funciona

porque pode haver diferenças entre as autoridades certificadoras confiáveis pelos dois ambientes.


34. O erro mais comum: Java não confia na CA

Um cenário extremamente frequente:

Servidor
   |
   +-- certificado
       |
       +-- CA corporativa

O navegador funciona.

O curl funciona.

Mas Java retorna:

PKIX path building failed

Nesse caso, investigue:

-Djavax.net.debug=ssl,handshake,trustmanager

e descubra:

  1. Qual certificado o servidor enviou.
  2. Qual é o Issuer.
  3. Qual cadeia foi construída.
  4. Qual truststore foi utilizado.
  5. Qual certificado/CA está faltando.

35. Cadeia de certificados

Considere:

Root CA
   |
   v
Intermediate CA
   |
   v
api.exemplo.com

O Java precisa conseguir construir uma cadeia confiável:

api.exemplo.com
       |
       v
Intermediate CA
       |
       v
Root CA
       |
       v
Truststore

Se faltar:

Intermediate CA

ou a CA raiz não for confiável, a validação poderá falhar.

O trustmanager é particularmente útil para enxergar esse processo.


36. Outro problema clássico: certificado expirado

No handshake você poderá encontrar informações indicando:

NotBefore
NotAfter

Se o certificado estiver fora do período de validade, o handshake poderá ser encerrado.

Esse tipo de problema pode ser especialmente confuso quando:

  • a aplicação está em um servidor;
  • o servidor possui relógio incorreto;
  • o certificado acabou de ser renovado;
  • existem múltiplos servidores atrás de um load balancer.

37. SNI: um detalhe importante

Quando uma aplicação acessa:

https://api.exemplo.com

o nome do host pode ser enviado no TLS através da extensão:

server_name

Isso é conhecido como SNI — Server Name Indication.

O debug pode mostrar algo semelhante a:

server_name
    type=host_name
    value=api.exemplo.com

Isso é importante porque servidores modernos podem hospedar vários certificados no mesmo IP.

Por exemplo:

                 IP 10.0.0.10
                      |
          +-----------+-----------+
          |                       |
     api.foo.com             api.bar.com
          |                       |
      certificado             certificado

Se houver algum problema de SNI, o servidor pode entregar um certificado inesperado.


38. Um caso clássico de diagnóstico

Imagine:

RestClient
   |
   v
https://api.empresa.com

e você recebe:

SSLHandshakeException:
PKIX path building failed

Comece:

-Djavax.net.debug=ssl,handshake

Se ainda não estiver claro:

-Djavax.net.debug=ssl,handshake,trustmanager

Depois procure:

ClientHello
ServerHello
Certificate
TrustManager

O raciocínio é:

ClientHello
    |
    v
Servidor respondeu?
    |
    +-- NÃO --> problema de rede/protocolo
    |
    +-- SIM
         |
         v
Certificate
         |
         v
TrustManager
         |
         +-- rejeitou --> problema de confiança
         |
         +-- aceitou
               |
               v
            Finished

Essa abordagem é muito mais eficiente do que simplesmente procurar a última linha da stack trace.


39. Como localizar rapidamente a causa em um log gigantesco

Suponha que você execute:

java -Djavax.net.debug=ssl,handshake,trustmanager -jar app.jar \
  > ssl.log 2>&1

Depois:

grep -Ei "exception|fatal|error|failed|unable|trust|certificate" ssl.log

Ou:

grep -Ei "PKIX|certificate|handshake_failure|protocol_version|unknown_ca" ssl.log

No Linux, isso é muito útil.

Você também pode:

less ssl.log

e procurar com:

/handshake_failure

40. Salvando o debug em arquivo

Uma estratégia que recomendo:

java \
  -Djavax.net.debug=ssl,handshake,trustmanager \
  -jar app.jar \
  > ssl-debug.log 2>&1

Agora você tem:

ssl-debug.log

Para acompanhar em tempo real:

tail -f ssl-debug.log

Ou:

tail -f ssl-debug.log | grep -Ei "certificate|trust|error|fatal"

41. Cuidado com informações sensíveis

Esse é um ponto muito importante.

O debug TLS pode revelar:

  • nomes de hosts;
  • certificados;
  • detalhes da infraestrutura;
  • informações de negociação;
  • dados hexadecimais;
  • eventualmente informações sensíveis, dependendo das opções ativadas.

Por isso:

não deixe javax.net.debug=all permanentemente habilitado em produção.

Especialmente evite:

plaintext

sem uma justificativa clara.


42. Como usar em Spring Boot

Suponha:

java -jar minha-api.jar

Use:

java \
  -Djavax.net.debug=ssl,handshake,trustmanager \
  -jar minha-api.jar

Se estiver usando Docker:

ENTRYPOINT [
  "java",
  "-Djavax.net.debug=ssl,handshake,trustmanager",
  "-jar",
  "app.jar"
]

Mas, para diagnóstico temporário, prefiro configurar através da execução:

docker run \
  ... \
  minha-imagem

ou por uma variável/configuração específica do ambiente, evitando deixar a imagem permanentemente em modo debug.


43. Spring Boot + Maven

Durante desenvolvimento:

mvn spring-boot:run \
  -Dspring-boot.run.jvmArguments="-Djavax.net.debug=ssl,handshake"

Para incluir TrustManager:

mvn spring-boot:run \
  -Dspring-boot.run.jvmArguments="-Djavax.net.debug=ssl,handshake,trustmanager"

44. IntelliJ IDEA

No IntelliJ, você pode colocar:

-Djavax.net.debug=ssl,handshake

no campo:

VM options

Não coloque em:

Program arguments

A distinção é:

VM options
    ↓
-Djavax.net.debug=ssl,handshake

Program arguments
    ↓
argumentos da sua aplicação

45. Eclipse

Na configuração de execução da aplicação:

Run Configurations
    ↓
Arguments
    ↓
VM arguments

adicione:

-Djavax.net.debug=ssl,handshake

46. Kubernetes

Em Kubernetes, se você estiver usando uma aplicação Java:

java -Djavax.net.debug=ssl,handshake -jar app.jar

é importante considerar que o volume de logs pode aumentar bastante.

Para uma investigação temporária, é melhor habilitar o debug somente no pod/ambiente necessário e desativá-lo depois.


47. Uma abordagem profissional de diagnóstico

Eu recomendo seguir esta sequência.

Nível 1 — handshake

-Djavax.net.debug=ssl,handshake

Pergunta:

Onde o handshake está falhando?


Nível 2 — certificados

-Djavax.net.debug=ssl,handshake,trustmanager

Pergunta:

O Java confia no certificado apresentado?


Nível 3 — mTLS

-Djavax.net.debug=ssl,handshake,keymanager,trustmanager

Pergunta:

O cliente apresentou o certificado correto e o servidor é confiável?


Nível 4 — detalhes

-Djavax.net.debug=ssl,handshake,verbose

Pergunta:

Qual informação adicional está faltando?


Nível 5 — protocolo

-Djavax.net.debug=ssl,handshake,record,packet

Pergunta:

O que exatamente está acontecendo no nível dos TLS records?


Nível 6 — tudo

-Djavax.net.debug=all

Somente quando realmente necessário.


48. Diagnóstico por tipo de erro

Uma "cola" útil:

Erro Primeira tentativa
PKIX path building failed ssl,handshake,trustmanager
unable to find valid certification path ssl,handshake,trustmanager
certificate_unknown ssl,handshake,trustmanager
handshake_failure ssl,handshake
protocol_version ssl,handshake
no appropriate protocol ssl,handshake
no cipher suites in common ssl,handshake
bad_certificate ssl,handshake,trustmanager
problema de certificado cliente ssl,handshake,keymanager
mTLS ssl,handshake,keymanager,trustmanager
problema de sessão ssl,handshake,session
problema muito baixo nível ssl,handshake,record,packet

49. Uma observação importante sobre Java 21

Como você utiliza Java 21 em seus projetos, vale observar que o comportamento e a quantidade/formatação do debug podem variar entre versões do JDK.

A própria documentação do JSSE alerta que o output do debug é não padronizado e pode mudar entre releases. Além disso, a implementação SunJSSE é a principal referência para essas opções; outros providers podem ter comportamento diferente. (Oracle Docs)

Por isso, se estiver investigando um problema em:

Java 21

é uma boa prática realizar o diagnóstico utilizando o mesmo JDK 21 que executa a aplicação, e não necessariamente outro Java instalado na máquina.


50. Uma diferença importante: debug Java x captura de rede

Existem duas formas complementares de investigar TLS.

Debug do Java

-Djavax.net.debug=ssl,handshake

Mostra:

o que o JSSE está fazendo

Captura de rede

Ferramentas como Wireshark mostram:

o que efetivamente passou pela rede

Conceitualmente:

                 JAVA
                  |
       javax.net.debug
                  |
                  v
              JSSE/TLS
                  |
                  |
                  v
                TCP
                  |
          captura de rede
                  |
                  v
              SERVIDOR

Quando um problema é muito difícil, usar ambos pode ser extremamente poderoso.


51. Um "kit" de comandos para guardar

Diagnóstico básico

-Djavax.net.debug=ssl,handshake

Certificados

-Djavax.net.debug=ssl,handshake,trustmanager

mTLS

-Djavax.net.debug=ssl,handshake,keymanager,trustmanager

Handshake detalhado

-Djavax.net.debug=ssl,handshake,verbose

Hexadecimal

-Djavax.net.debug=ssl,handshake,data

TLS records

-Djavax.net.debug=ssl,record

Pacotes

-Djavax.net.debug=ssl,record,packet

Tudo

-Djavax.net.debug=all

Descobrir opções disponíveis

-Djavax.net.debug=help

52. Minha recomendação prática para desenvolvedores Java

Não comece assim:

-Djavax.net.debug=all

Comece assim:

-Djavax.net.debug=ssl,handshake

Se aparecer algo relacionado a certificado:

-Djavax.net.debug=ssl,handshake,trustmanager

Se for mTLS:

-Djavax.net.debug=ssl,handshake,keymanager,trustmanager

Só depois avance para:

-Djavax.net.debug=ssl,handshake,verbose,record

e, em último caso:

-Djavax.net.debug=all

A ideia é aumentar progressivamente o nível de informação, em vez de produzir milhares de linhas de log desde o início.


53. Fluxograma mental para usar o javax.net.debug

Guarde este raciocínio:

              Aplicação Java
                    |
                    v
             Conexão TLS
                    |
                    v
          -Djavax.net.debug=
              ssl,handshake
                    |
                    v
             ClientHello?
              /          \
            NÃO          SIM
             |            |
       problema de        v
       conexão/protocolo ServerHello?
                         /       \
                       NÃO       SIM
                        |         |
                     protocolo    v
                               Certificate?
                                /      \
                              NÃO      SIM
                               |        |
                          protocolo     v
                                    TrustManager
                                         |
                                  certificado aceito?
                                     /       \
                                   NÃO       SIM
                                    |         |
                                  PKIX        v
                                         KeyManager?
                                            |
                                      (se mTLS)
                                            |
                                            v
                                         Finished
                                            |
                                            v
                                      TLS OK

Esse modelo mental é muito mais útil do que tentar decorar centenas de linhas do log.


54. Resumo final

O parâmetro:

-Djavax.net.debug=ssl,handshake

é uma das ferramentas mais importantes para diagnosticar problemas TLS em aplicações Java.

As quatro combinações que eu deixaria salvas são:

# 1. Diagnóstico geral
-Djavax.net.debug=ssl,handshake
# 2. Problemas de certificado
-Djavax.net.debug=ssl,handshake,trustmanager
# 3. mTLS / certificado do cliente
-Djavax.net.debug=ssl,handshake,keymanager,trustmanager
# 4. Investigação avançada
-Djavax.net.debug=ssl,handshake,verbose,record

E lembre-se da regra mais importante:

handshake mostra como a negociação TLS está acontecendo; trustmanager ajuda a descobrir por que o Java confia ou não no certificado; keymanager ajuda a descobrir qual certificado o próprio cliente está tentando apresentar.

A documentação oficial do Oracle Java/JSSE é a referência para as opções disponíveis e seus significados. (Oracle Docs)

Java Secure Socket Extension (JSSE) Reference Guide

Java SE 21 Security Developer's Guide