Your Java client, SOAP call, or HttpsURLConnection reaches the server, trust checks pass, and then the handshake dies with No subject alternative names present or No subject alternative names matching IP address … found. The certificate is not necessarily invalid — Java reports this hostname-verification failure differently from browsers, and automated Java clients usually fail immediately instead of giving you a click-through warning.
Use the table below to jump to the cause that fits your URL and certificate. Each section reproduces the failure on Ubuntu with OpenJDK 25 and shows the keytool fix. For a full walkthrough of generating a correct self-signed cert, see Self-signed certificate with localhost and IP SAN.
Tested on: Ubuntu 26.04 LTS; OpenJDK 25.0.3; kernel 7.0.0-27-generic.
Prerequisites
- OpenJDK with
keytool— Install keytool on Ubuntu — see keytool command. opensslfors_clientand SAN inspection in the pre-check steps (OpenSSL).
What the error actually means
After TLS trust succeeds, Java runs hostname verification (RFC 2818 / RFC 6125). The rules differ for DNS names and IP literals:
| URL you connect to | What Java checks |
|---|---|
https://app.example.com |
DNS:app.example.com in SAN, or CN fallback when no SAN extension exists |
https://127.0.0.1 |
IP:127.0.0.1 in SAN — CN is not used for IP literals |
https://192.168.1.10 |
IP:192.168.1.10 in SAN |
When the certificate has no subjectAltName extension and you connect by IP, OpenJDK 25 reports:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) No subject alternative names present
caused by: java.security.cert.CertificateException: No subject alternative names presentWhen SAN exists but lists only DNS names and you connect by IP:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) No subject alternative names matching IP address 127.0.0.1 found
caused by: java.security.cert.CertificateException: No subject alternative names matching IP address 127.0.0.1 foundThis is not a missing CA chain (chain from reply) and not an untrusted issuer (PKIX path building failed). The certificate simply does not name the host your client used.
Quick pre-check
Compare the URL your Java code opens with what is in the keystore file and what the running endpoint actually presents.
Inspect the keystore on disk:
keytool -list -v -keystore server.p12 -storetype PKCS12 -storepass PASS -alias serverLook for SubjectAlternativeName under Extensions. Export and double-check with OpenSSL:
keytool -exportcert -alias server -keystore server.p12 -storetype PKCS12 \
-storepass PASS -rfc -file server.pem
openssl x509 -in server.pem -noout -text | grep -A2 "Subject Alternative Name"Also inspect the certificate Java actually receives from the server:
openssl s_client -connect 127.0.0.1:8443 -servername localhost -showcerts </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -ext subjectAltNamesubject=CN=localhost
issuer=CN=localhost
X509v3 Subject Alternative Name:
DNS:localhost, IP Address:127.0.0.1If the SAN shown here differs from the keystore you edited, the service is reading another keystore, alias, secret, ingress certificate, or load balancer certificate.
For virtual-hosted TLS endpoints, set -servername to the same DNS name used by the Java client; otherwise the server may return a default certificate.
When testing an IP URL, replace 127.0.0.1:8443 with the same IP and port your Java client uses. SNI may not apply to IP-only endpoints, but the presented certificate must still contain the matching IP: SAN.
| Observation | Likely problem |
|---|---|
No SubjectAlternativeName block |
IP connections will fail; DNS may fall back to CN only |
DNSName: localhost only |
https://127.0.0.1 fails — needs IP:127.0.0.1 |
CN=127.0.0.1 but no SAN |
IP URL fails because CN is ignored for IP; localhost fails because the name differs |
| SAN names do not include your URL host | Wrong hostname in certificate |
Keystore SAN differs from s_client output |
Service is not using the keystore you edited |
Find your cause
| Likely cause | Clues | Go to |
|---|---|---|
| No SAN, client uses IP | https://127.0.0.1 or LAN IP in Java URL |
No SAN, connect by IP |
| DNS SAN only, client uses IP | Cert has DNS:localhost; URL uses 127.0.0.1 |
DNS SAN only |
| CN is IP, no IP SAN | CN=192.168.1.10 in cert; still fails on IP URL |
CN is IP without SAN |
| Wrong DNS name in SAN | DNS:www.example.com but URL is api.example.com |
Hostname mismatch |
| CA-signed cert lacks SAN | CSR or signing omitted -ext SAN |
CA-signed cert missing SAN |
| Docs say localhost, config uses IP | Tomcat/Kafka/Spring bind or call by IP | localhost vs 127.0.0.1 split |
| Service presents another certificate | Keystore has correct SAN; openssl s_client shows old or different cert |
Service using another cert |
| Different error text | Trust or chain problem | Not a SAN error |
No SAN extension, connecting by IP
The most common Stack Overflow and vendor KB scenario: keytool -genkeypair with only -dname "CN=localhost" and no -ext flag. Chrome and other browsers also require SAN for hostname matching; when your CA issued the cert outside keytool, add SAN with Subject Alternative Names in OpenSSL. Java services fail programmatically with this exact exception instead of a browser warning.
Generate a cert without SAN:
mkdir -p ~/san-lab && cd ~/san-lab
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -validity 365 \
-dname "CN=localhost" -keystore no-san.p12 -storetype PKCS12 \
-storepass changeit -keypass changeit -nopromptConfirm SAN is absent:
keytool -list -v -keystore no-san.p12 -storetype PKCS12 -storepass changeit -alias server \
| grep -i "Subject Alternative"No output means no SAN extension. A Java HttpsURLConnection to https://127.0.0.1:8443/ fails:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) No subject alternative names present
caused by: java.security.cert.CertificateException: No subject alternative names presentThe same keystore may still work for https://localhost:8443/ because Java can match CN=localhost when no SAN extension exists. Do not rely on that — add explicit SAN entries.
Fix — regenerate with DNS and IP SAN:
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -validity 365 \
-dname "CN=localhost" \
-ext "SAN=DNS:localhost,IP:127.0.0.1" \
-keystore with-san.p12 -storetype PKCS12 \
-storepass changeit -keypass changeit -nopromptVerify:
keytool -list -v -keystore with-san.p12 -storetype PKCS12 -storepass changeit -alias server \
| grep -A5 "SubjectAlternativeName"SubjectAlternativeName [
DNSName: localhost
IPAddress: 127.0.0.1
]After pointing Tomcat or Spring Boot at with-san.p12, both https://127.0.0.1 and https://localhost succeed under Java's default hostname verifier.
DNS SAN only, but client uses IP
Your certificate has SAN=DNS:localhost but the Java client opens https://127.0.0.1. SAN exists, so the error text changes:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) No subject alternative names matching IP address 127.0.0.1 found
caused by: java.security.cert.CertificateException: No subject alternative names matching IP address 127.0.0.1 foundFix options:
- Preferred: Reissue the cert with
-ext "SAN=DNS:localhost,IP:127.0.0.1". - Workaround: Change the client URL to
https://localhostwhen DNS SAN matches and/etc/hostsresolves it — fragile for production.
CN is an IP address without IP SAN
Putting the IP in Common Name alone does not satisfy Java for IP URLs. A cert with CN=127.0.0.1 and no -ext SAN still fails on https://127.0.0.1:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) No subject alternative names presentConnecting to https://localhost with that cert fails differently:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) No name matching localhost foundFix — use an IP SAN entry, not CN alone:
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -validity 365 \
-dname "CN=127.0.0.1" \
-ext "SAN=IP:127.0.0.1,DNS:localhost" \
-keystore ip-san.p12 -storetype PKCS12 -storepass changeit -nopromptFor LAN deployments, list every IP Java clients use:
-ext "SAN=DNS:app.lab.local,IP:192.168.1.10,IP:10.0.0.5"For IPv6 URLs, add the IPv6 literal as an IP: SAN entry too. Do not put IPv6 addresses under DNS:.
Service is using another certificate
If keytool -list -v shows the correct SAN but Java still fails, inspect the live endpoint with openssl s_client (see Quick pre-check). The running service may be using a different keystore, alias, Kubernetes TLS secret, ingress certificate, or load balancer certificate than the file you edited on disk.
Check:
- Tomcat
certificateKeystoreFileandcertificateKeyAlias - Spring Boot
server.ssl.key-storeandserver.ssl.key-alias - Kubernetes
Secretmounted into the pod - Ingress or load balancer TLS configuration
In Kubernetes, the Java client validates the certificate presented by the Service, Ingress, or load balancer — not necessarily the certificate inside the backend pod. If the URL is https://10.96.x.x or an ingress IP, the certificate needs an IP: SAN for that address, or the client should use a DNS name that exists as DNS: SAN.
Restart or reload the service after replacing the certificate. Until the listener reloads, s_client keeps showing the old SAN even when the keystore file on disk is already fixed.
Hostname does not match SAN
When a certificate includes SAN entries, Java checks SAN first. A cert with SAN=DNS:www.example.com rejects https://api.example.com even if CN=api.example.com is in the subject.
Check what the client actually uses:
openssl x509 -in server.pem -noout -ext subjectAltNameReissue with every DNS name in the URL — wildcards only when your CA supports DNS:*.example.com and the hostname matches the wildcard rules.
CA-signed certificate missing SAN
Internal PKI teams sometimes sign a CSR that omitted SAN. OpenSSL does not copy extensions from the bootstrap self-signed cert by default — you must pass -ext SAN=... on keytool -certreq and ensure the CA preserves SAN at signing time.
Correct CSR flow:
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -validity 365 \
-dname "CN=app.lab.local" \
-ext "SAN=DNS:app.lab.local,IP:10.0.0.5" \
-keystore server.p12 -storetype PKCS12 -storepass changeit -noprompt
keytool -certreq -alias server -keystore server.p12 -storetype PKCS12 \
-storepass changeit -file server.csr \
-ext "SAN=DNS:app.lab.local,IP:10.0.0.5"After the CA returns the signed .crt, import with chain import order and confirm SAN survived:
keytool -list -v -keystore server.p12 -storetype PKCS12 -storepass changeit -alias serverIf SAN disappeared after CA signing, ask the CA to reissue with the SAN extension from your CSR.
localhost vs 127.0.0.1: both need SAN entries
Kafka, Elasticsearch, and Tomcat docs often say localhost while health checks hit 127.0.0.1. Java treats those as different identities.
| Client URL | Required SAN |
|---|---|
https://localhost:8443 |
DNS:localhost |
https://127.0.0.1:8443 |
IP:127.0.0.1 |
-ext "SAN=..." string at generation or CSR time. keytool cannot add SAN to an already issued certificate.
Quick lab reproduction
Reproduce the IP failure in a throwaway directory:
cd ~/san-lab
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -validity 365 \
-dname "CN=localhost" -keystore no-san.p12 -storetype PKCS12 \
-storepass changeit -nopromptWire no-san.p12 into a Java HTTPS listener, then call https://127.0.0.1:PORT/ from HttpsURLConnection with default hostname verification. Expect No subject alternative names present.
Safe fix:
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -validity 365 \
-dname "CN=localhost" \
-ext "SAN=DNS:localhost,IP:127.0.0.1" \
-keystore fixed-san.p12 -storetype PKCS12 -storepass changeit -nopromptRestart the service with fixed-san.p12 and retry the same IP URL.
Distinguish from other Java SSL errors
| Error | Meaning | Guide |
|---|---|---|
No subject alternative names present |
No SAN; IP URL | This page |
No subject alternative names matching IP address X found |
SAN lacks that IP | This page |
No name matching hostname found |
SAN/CN do not match DNS URL | This page |
Public keys in reply and keystore don't match |
Wrong CSR alias | Public keys mismatch |
References
- Java Secure Socket Extension (JSSE) Reference Guide (Oracle)
- keytool documentation (Oracle Java SE 25)
- RFC 2818 — HTTP Over TLS (IP address in SAN)
Summary
No subject alternative names present means Java could not match your HTTPS URL to the certificate SAN extension — most often because you connected by IP and the cert has no IPAddress SAN. Confirm the live endpoint with openssl s_client, not only the keystore file. Regenerate or reissue with keytool -genkeypair or keytool -certreq plus -ext "SAN=DNS:hostname,IP:127.0.0.1" for every DNS name and IP literal your clients use. CN alone is not enough for IP URLs.

