import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsServer; import java.io.EOFException; import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.security.*; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import javax.net.ssl.*; /** Build an SSLContext from three PEM files and talk to ourselves over TLS. */ public class TlsFromPem { static List readAll(String pemText) throws Exception { List out = new ArrayList<>(); try (InputStream in = new java.io.ByteArrayInputStream(pemText.getBytes())) { PEMDecoder d = PEMDecoder.of(); while (true) { try { out.add(d.decode(in, X509Certificate.class)); } catch (EOFException end) { break; } } } return out; } public static void main(String[] args) throws Exception { PEMDecoder d = PEMDecoder.of(); Fx.h("read a two-certificate chain file"); List chain = readAll(Fx.read("server-chain.pem")); for (Certificate c : chain) System.out.println(" " + ((X509Certificate) c).getSubjectX500Principal()); Fx.check(chain.size() == 2, "the stream decoder returns the two certificates one by one"); Fx.h("server side: key + chain -> KeyStore -> KeyManagerFactory -> SSLContext"); PrivateKey key = d.decode(Fx.read("server-key.pem"), PrivateKey.class); char[] pw = new char[0]; KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(null, null); ks.setKeyEntry("server", key, pw, chain.toArray(new Certificate[0])); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, pw); SSLContext serverCtx = SSLContext.getInstance("TLS"); serverCtx.init(kmf.getKeyManagers(), null, null); HttpsServer server = HttpsServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); server.setHttpsConfigurator(new HttpsConfigurator(serverCtx)); server.createContext("/", ex -> { byte[] body = "hello over TLS\n".getBytes(); ex.sendResponseHeaders(200, body.length); ex.getResponseBody().write(body); ex.close(); }); server.start(); URI uri = URI.create("https://localhost:" + server.getAddress().getPort() + "/"); try { Fx.h("client trusting only the CA certificate from ca.pem"); KeyStore ts = KeyStore.getInstance("PKCS12"); ts.load(null, null); ts.setCertificateEntry("ca", d.decode(Fx.read("ca.pem"), X509Certificate.class)); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ts); SSLContext clientCtx = SSLContext.getInstance("TLS"); clientCtx.init(null, tmf.getTrustManagers(), null); HttpClient trusting = HttpClient.newBuilder().sslContext(clientCtx).build(); HttpResponse r = trusting.send(HttpRequest.newBuilder(uri).build(), HttpResponse.BodyHandlers.ofString()); System.out.println("HTTP " + r.statusCode() + " " + r.body().strip() + " (" + r.sslSession().get().getProtocol() + ")"); System.out.println("server presented: " + ((X509Certificate) r.sslSession().get().getPeerCertificates()[0]).getSubjectX500Principal()); Fx.check(r.statusCode() == 200, "request over TLS succeeds"); Fx.h("same request with the JVM's default trust store (the CA is not in it)"); try { HttpClient.newHttpClient().send(HttpRequest.newBuilder(uri).build(), HttpResponse.BodyHandlers.ofString()); } catch (Exception e) { System.out.println(e); } } finally { server.stop(0); } } }