ComponentRole
SSLContextFactory for SSLSocketFactory and SSLServerSocketFactory; holds key material and trust anchors
KeyStoreRepository of certificates and private keys (JKS, PKCS12)
KeyManagerSelects which client certificate to present during handshake
TrustManagerDecides whether to trust the server's certificate chain
SSLSocket / SSLServerSocketTLS-wrapped stream sockets
SSLSessionNegotiated cipher suite, protocol version, peer certificates
HostnameVerifierValidates the server's hostname against the certificate CN/SAN

For connections to public HTTPS servers the default SSLContext trusts the JVM's built-in CA bundle — no configuration needed.

import javax.net.ssl.*; import java.io.*; // Client connecting to a public server SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); try (SSLSocket socket = (SSLSocket) factory.createSocket("example.com", 443)) { socket.startHandshake(); // negotiate TLS SSLSession session = socket.getSession(); System.out.println("Protocol : " + session.getProtocol()); // TLSv1.3 System.out.println("Cipher : " + session.getCipherSuite()); System.out.println("Peer cert: " + session.getPeerCertificates()[0]); // use like a normal socket var out = new PrintWriter(socket.getOutputStream(), true); var in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out.println("GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n"); System.out.println(in.readLine()); // HTTP/1.1 200 OK }

When connecting to servers with self-signed or private CA certificates you must build a custom SSLContext.

import java.security.*; import javax.net.ssl.*; import java.io.*; public SSLContext buildSslContext(String keystorePath, char[] keystorePassword, String truststorePath, char[] truststorePassword) throws Exception { // 1. Load the KeyStore (contains our private key + certificate chain) KeyStore keyStore = KeyStore.getInstance("PKCS12"); try (InputStream ks = new FileInputStream(keystorePath)) { keyStore.load(ks, keystorePassword); } // 2. Load the TrustStore (contains trusted CA certificates) KeyStore trustStore = KeyStore.getInstance("JKS"); try (InputStream ts = new FileInputStream(truststorePath)) { trustStore.load(ts, truststorePassword); } // 3. Initialise KeyManager and TrustManager KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, keystorePassword); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); // 4. Build the SSLContext SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); return ctx; } // Use the custom context SSLContext ctx = buildSslContext("client.p12", "keystorePass".toCharArray(), "truststore.jks", "trustPass".toCharArray()); SSLSocketFactory factory = ctx.getSocketFactory(); try (SSLSocket socket = (SSLSocket) factory.createSocket("internal-server", 8443)) { socket.startHandshake(); // ... read/write }
SSLContext ctx = buildSslContext("server.p12", "pass".toCharArray(), "truststore.jks", "pass".toCharArray()); SSLServerSocketFactory ssf = ctx.getServerSocketFactory(); try (SSLServerSocket server = (SSLServerSocket) ssf.createServerSocket(8443)) { // require TLS 1.2+ server.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.3"}); System.out.println("TLS server on port 8443"); while (true) { SSLSocket client = (SSLSocket) server.accept(); new Thread(() -> { try (client; var in = new BufferedReader(new InputStreamReader(client.getInputStream())); var out = new PrintWriter(client.getOutputStream(), true)) { out.println("Hello over TLS!"); System.out.println("Client: " + in.readLine()); } catch (IOException e) { e.printStackTrace(); } }).start(); } }

Hostname verification ensures the server's certificate matches the hostname you are connecting to — it prevents man-in-the-middle attacks even when the certificate is otherwise valid.

SSLSocket does not perform hostname verification by default — you must enable it explicitly using SSLParameters:

SSLSocket socket = (SSLSocket) factory.createSocket("myserver.example.com", 443); // Enable HTTPS-style hostname verification (checks CN and SANs) SSLParameters params = socket.getSSLParameters(); params.setEndpointIdentificationAlgorithm("HTTPS"); socket.setSSLParameters(params); socket.startHandshake(); // throws SSLHandshakeException if hostname doesn't match Never disable hostname verification in production code. If you see code like setHostnameVerifier((h, s) -> true), it makes the connection vulnerable to MITM attacks.

In mTLS both the client and server authenticate with certificates. The server calls setNeedClientAuth(true) and the client must have a key loaded in its KeyManager.

// Server: require client certificate try (SSLServerSocket server = (SSLServerSocket) ssf.createServerSocket(8443)) { server.setNeedClientAuth(true); // reject connections without a valid client cert SSLSocket client = (SSLSocket) server.accept(); client.startHandshake(); // inspect client identity java.security.cert.Certificate[] certs = client.getSession().getPeerCertificates(); System.out.println("Client cert: " + certs[0]); } // Client: must have a private key + certificate in its KeyStore SSLContext ctx = buildSslContext("client.p12", "clientPass".toCharArray(), "server-ca.jks", "caPass".toCharArray()); SSLSocketFactory sf = ctx.getSocketFactory(); SSLSocket socket = (SSLSocket) sf.createSocket("server", 8443); socket.startHandshake(); // presents client certificate automatically
SSLSocket socket = (SSLSocket) factory.createSocket("host", 443); // Restrict to TLS 1.3 only socket.setEnabledProtocols(new String[]{"TLSv1.3"}); // List available cipher suites System.out.println(Arrays.toString(socket.getSupportedCipherSuites())); // Enable only strong cipher suites socket.setEnabledCipherSuites(new String[]{ "TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256", "TLS_CHACHA20_POLY1305_SHA256" });
ProtocolRecommendation
TLSv1.3✅ Prefer — faster handshake, stronger security, no weak ciphers
TLSv1.2✅ Acceptable when TLS 1.3 not supported by peer
TLSv1.1❌ Deprecated (RFC 8996) — disable explicitly
TLSv1.0❌ Deprecated (RFC 8996) — disable explicitly
SSLv3❌ Broken (POODLE) — never use

Quick commands to create a self-signed server certificate for local testing:

# Generate a self-signed server key + cert in PKCS12 format keytool -genkeypair -alias server -keyalg RSA -keysize 2048 \ -validity 365 -dname "CN=localhost,O=Test,C=US" \ -keystore server.p12 -storetype PKCS12 -storepass changeit # Export the server cert as PEM for use as a trust anchor keytool -exportcert -alias server -keystore server.p12 \ -storepass changeit -rfc -file server-cert.pem # Import server cert into client's truststore keytool -importcert -alias server-ca -file server-cert.pem \ -keystore client-truststore.jks -storepass changeit -noprompt For local development with self-signed certs, set the system property -Djavax.net.debug=ssl:handshake to see the full TLS handshake log — it shows which certificates are presented and which ciphers are negotiated.