Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java HTTPS project: a trusted local certificate and a rejected hostname

Last updated: 29 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

TLS certificate trust and hostname verification are separate checks: trusting a certificate does not make it valid for every server name.

Download Java source kit

Java 11+. The program uses JDK classes and requires no preview flags.

Trust only this fixture’s certificate

The program creates an ephemeral local PKCS12 store using the current JDK’s keytool. Its certificate has a localhost DNS subject alternative name. The server owns the private key, while the client trust store receives only that certificate. The password is a synthetic fixture value, not a service credential.

The first request addresses localhost and succeeds. The second addresses the numeric loopback address, which is absent from the certificate’s identities, and must fail hostname verification. The client does not install an accept-all trust manager or disable endpoint identification to make the request pass.

All files are created in the fixture’s temporary directory and removed on exit. The generated key is not bundled in the public source ZIP. In a deployed service, certificate issuance, rotation, trust roots and expiry handling need an operational owner; this local test does not provision a public certificate.

Verify the rejection path

A successful encrypted response alone is insufficient evidence that hostname checks are enabled. Keep a negative identity test in the integration suite. Transport exceptions also need classification: a certificate failure is not a transient 503 that should be retried until it works.

Working program

Java
import com.sun.net.httpserver.*;
import javax.net.ssl.*;
import java.net.*;
import java.net.http.*;
import java.nio.file.*;
import java.security.KeyStore;
import java.time.Duration;
import java.util.concurrent.*;
public class LocalTlsIdentity {
    public static void main(String[] args)throws Exception{
        Path directory=Files.createTempDirectory("aitrove-tls-"),store=directory.resolve("fixture.p12"),log=directory.resolve("keytool.log");char[] password="fixture-only".toCharArray();
        HttpsServer server=null;ExecutorService handlers=Executors.newSingleThreadExecutor();
        try{
            String keytool=Path.of(System.getProperty("java.home"),"bin","keytool").toString();
            Process creation=new ProcessBuilder(keytool,"-genkeypair","-alias","server","-keyalg","RSA","-keysize","2048","-storetype","PKCS12","-keystore",store.toString(),"-storepass",new String(password),"-dname","CN=localhost","-ext","SAN=dns:localhost","-validity","2","-noprompt").redirectErrorStream(true).redirectOutput(log.toFile()).start();
            if(!creation.waitFor(10,TimeUnit.SECONDS)){creation.destroyForcibly();throw new IllegalStateException("Key creation timeout");}if(creation.exitValue()!=0)throw new IllegalStateException("Key creation failed");
            KeyStore keys=KeyStore.getInstance("PKCS12");try(java.io.InputStream input=Files.newInputStream(store)){keys.load(input,password);}
            KeyManagerFactory keyManagers=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());keyManagers.init(keys,password);
            SSLContext serverContext=SSLContext.getInstance("TLS");serverContext.init(keyManagers.getKeyManagers(),null,null);
            KeyStore trusted=KeyStore.getInstance("PKCS12");trusted.load(null,null);trusted.setCertificateEntry("fixture",keys.getCertificate("server"));
            TrustManagerFactory trustManagers=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagers.init(trusted);
            SSLContext clientContext=SSLContext.getInstance("TLS");clientContext.init(null,trustManagers.getTrustManagers(),null);
            server=HttpsServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(),0),0);server.setHttpsConfigurator(new HttpsConfigurator(serverContext));server.setExecutor(handlers);
            server.createContext("/receipt",exchange->{byte[] body="tls=verified".getBytes(java.nio.charset.StandardCharsets.UTF_8);exchange.sendResponseHeaders(200,body.length);try(java.io.OutputStream output=exchange.getResponseBody()){output.write(body);}});server.start();
            HttpClient client=HttpClient.newBuilder().sslContext(clientContext).connectTimeout(Duration.ofSeconds(2)).build();int port=server.getAddress().getPort();
            HttpRequest valid=HttpRequest.newBuilder(new URI("https",null,"localhost",port,"/receipt",null,null)).timeout(Duration.ofSeconds(3)).GET().build();
            System.out.println(client.send(valid,HttpResponse.BodyHandlers.ofString()).body());
            HttpRequest invalid=HttpRequest.newBuilder(new URI("https",null,server.getAddress().getAddress().getHostAddress(),port,"/receipt",null,null)).timeout(Duration.ofSeconds(3)).GET().build();
            try{client.send(invalid,HttpResponse.BodyHandlers.ofString());throw new IllegalStateException("Wrong hostname accepted");}
            catch(SSLHandshakeException rejected){System.out.println("hostname rejected");}
        }finally{
            if(server!=null)server.stop(0);handlers.shutdownNow();if(!handlers.awaitTermination(2,TimeUnit.SECONDS))throw new IllegalStateException("TLS worker");
            Files.deleteIfExists(store);Files.deleteIfExists(log);Files.deleteIfExists(directory);
        }
    }
}

Output

Output
tls=verified
hostname rejected

Costs and boundaries

Key generation, handshake and server startup dominate this small integration fixture; no latency ranking is inferred. The keytool subprocess has a ten-second creation bound and requests have explicit timeouts. A real service needs public certificate lifecycle, protocol policy and engine-specific testing beyond this isolated loopback exchange.

Common Mistakes

  • Trusted does not mean valid for every hostname.
  • Do not disable verification to hide a certificate-name mismatch.
  • Never treat the fixture password or certificate as a production credential.

Read next

Client operations, Failure classification.

java
tls-local-project
Storage details