Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01CifR2BE7PBvRy5hT8TS9oz
88 lines
4.1 KiB
Java
88 lines
4.1 KiB
Java
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<Certificate> readAll(String pemText) throws Exception {
|
|
List<Certificate> 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<Certificate> 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<String> 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);
|
|
}
|
|
}
|
|
}
|