I've written wrappers for Java's X509TrustManager and X509KeyManager classes, and added a Provider so that you can get it hooked into the system automatically.

It's available at https://github.com/tersesystems/debugjsse.

This means you can say:

import com.tersesystems.debugjsse.DebugJSSEProvider ; DebugJSSEProvider debugJSSEProvider = new DebugJSSEProvider (); Security . addProvider ( debugJSSEProvider ); debugJSSEProvider . setAsDefault ();

and then anything you do after that point with JSSE will have entry/exit/exception on it.

For example, if you instantiate a trust manager and call getAcceptedIssuers :

TrustManagerFactory trustManagerFactory = TrustManagerFactory . getInstance ( TrustManagerFactory . getDefaultAlgorithm ()); trustManagerFactory . init ( ks ); TrustManager [] trustManagers = trustManagerFactory . getTrustManagers (); X509ExtendedTrustManager trustManager = ( X509ExtendedTrustManager ) trustManagers [ 0 ]; X509Certificate [] acceptedIssuers = trustManager . getAcceptedIssuers ();

will result in:

You can also wrap your trust manager explicitly, of course:

X509ExtendedTrustManager delegate = ... Debug debug = ... X509ExtendedTrustManager tm = new DebugX509ExtendedTrustManager ( delegate , debug );

but if you have libraries that instantiate their own trust managers, such as Kafka Client's SslFactory, then hooking in the provider is the only way to get at it.

Note that this debugging mechanism is distinct from using javax.net.debug in a few ways. Here's what is wrong with JSSE debugging:

javax.net.debug is hardcoded to System.out.println .

is hardcoded to . javax.net.debug mixes outputs coming from different threads.

mixes outputs coming from different threads. javax.net.debug does not give you timestamp information.

does not give you timestamp information. javax.net.debug does not give you method level granularity.

does not give you method level granularity. javax.net.debug settings cannot be changed at runtime.

settings cannot be changed at runtime. javax.net.debug does not respect verbosity settings.

By contrast, DebugJSSE:

Works with any logging framework.

Has flexibility to log only by delegate or by individual method or argument.

Lets you swap out the results of the delegate's method call.

I've been meaning to write this for a while. What really cracked it was the X509ExtendedKeyManager abstract class adds two methods, chooseEngineClientAlias and chooseEngineServerAlias , but silently returns null for both of them. And then javax.net.debug doesn't integrate with SLF4J, and an async appender and println do not get along.

It is all fixed. It is done. May you never have to go through this.

You may also like https://github.com/scholzj/AliasKeyManager which was used as inspiration, and will pick out a preferred client alias from a key manager. Also see the previous JSSE posts if you are morbidly curious.