Deep Dive into Java CertificateException "No subject alternative names matching IP address ... found" and Solutions

Dec 02, 2025 · Programming · 11 views · 7.8

Keywords: Java | SSL/TLS | CertificateException | Subject Alternative Name | IP Address Validation

Abstract: This article comprehensively examines the common error "No subject alternative names matching IP address ... found" encountered in Java applications when establishing SSL/TLS connections with self-signed certificates. It begins by analyzing the root cause of the exception: the absence of matching Subject Alternative Names (SAN) for the target IP address in the certificate. By comparing the certificate validation mechanisms between web browsers and the Java Virtual Machine (JVM), it explains why the same certificate works in browsers but fails in Java. The core section presents two primary solutions: modifying the certificate generation process to include the IP address as an IPAddress-type SAN, and bypassing strict hostname verification through a custom HostnameVerifier. The article also discusses the security implications and applicable scenarios of these methods, providing detailed code examples and configuration steps to help developers fundamentally resolve IP address validation issues.

Problem Context and Error Analysis

In Java applications, developers often encounter the exception javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names matching IP address ... found when attempting to connect to a server using a self-signed certificate via HTTPS. This error indicates that the SSL/TLS handshake fails due to certificate validation. Specifically, Java's Java Secure Socket Extension (JSSE) framework, during server certificate verification, finds that the certificate lacks a Subject Alternative Name (SAN) matching the target IP address of the connection.

From the provided Q&A data, the user's certificate SAN field only contains DNS-type entries: DNS:192.168.178.71, DNS:www.example.com, DNS:mail.example.com, DNS:ftp.example.com. Although the first entry textually resembles an IP address, it is encoded as a DNS type rather than an IPAddress type (with key identifier 7). Java's certificate validation logic strictly distinguishes between these types; when the connection target is an IP address (e.g., https://192.168.178.71/), the validator looks for an IPAddress-type SAN and ignores DNS-type entries, leading to validation failure.

Differences in Browser and Java Validation Mechanisms

Why does the same certificate work in Firefox and Chrome but fail in Java? This stems from differences in the leniency of certificate validation across environments. Modern browsers, when verifying certificates, may fall back to checking the Common Name (CN) in the certificate subject in addition to the SAN field. In the user's case, the certificate's CN value is CN=192.168.178.71, and browsers might accept this as a valid identifier, allowing the connection. However, Java adheres to stricter RFC 2818 and RFC 6125 standards; since Java 1.7, it no longer relies primarily on CN for hostname verification, instead mandating that the SAN field contain matching entries. This design enhances security by preventing CN-related attacks but also causes compatibility issues with older or misconfigured certificates.

Core Solution: Adding IPAddress-Type SAN

As guided by the best answer (Answer 2), the fundamental solution is to add the target IP address as an IPAddress-type SAN during certificate generation. This ensures the Java validator can find an exact match. Below is an example procedure using OpenSSL to generate a self-signed certificate with an IPAddress SAN:

# Generate private key and Certificate Signing Request (CSR), specifying IPAddress SAN in config file
cat > san.cnf << EOF
[req]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = v3_req

[req_distinguished_name]
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name (full name)
localityName = Locality Name (eg, city)
organizationName = Organization Name (eg, company)
commonName = Common Name (e.g., server FQDN or YOUR name)

[v3_req]
subjectAltName = @alt_names

[alt_names]
IP.1 = 192.168.178.71
DNS.1 = www.example.com
EOF

# Generate private key and CSR
openssl req -new -nodes -keyout server.key -out server.csr -config san.cnf

# Generate self-signed certificate
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt -extensions v3_req -extfile san.cnf

In this configuration, IP.1 = 192.168.178.71 explicitly adds the IP address as an IPAddress-type SAN. After generating the certificate, import it into a Java KeyStore (JKS) for use:

# Export certificate and private key to PKCS12 file
openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -name "server"

# Import PKCS12 file into JKS keystore
keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS

With the updated certificate, the Java application should validate successfully, as the SAN field now includes a matching IPAddress entry for the connection target.

Alternative Approach: Custom HostnameVerifier

If modifying the certificate is not feasible (e.g., in testing environments or as a temporary fix), refer to Answer 1 by implementing a custom HostnameVerifier to bypass strict hostname verification. This method allows the application to accept any hostname but reduces security, so it is recommended only for non-production environments. Below is an implementation example:

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.net.URL;

public class SSLExample {
    public static void main(String[] args) throws Exception {
        // Set custom HostnameVerifier
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                // Accept all hostnames, including IP addresses
                return true;
            }
        });

        // Perform HTTPS connection
        URL url = new URL("https://192.168.178.71/");
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        // Read response...
    }
}

This code overrides the default validation logic, treating all connections as valid. However, it exposes the application to man-in-the-middle attack risks since server identity is no longer verified. Whenever possible, prefer the SAN addition method.

Security Considerations and Best Practices

Answer 3 mentions general advice on using CN or SAN but lacks depth on IPAddress-type specifics. In real-world deployments, follow these best practices:

By understanding Java's certificate validation mechanisms and properly configuring SANs, developers can resolve IP address connection issues while maintaining application security. This is not just a technical implementation but also an adherence to SSL/TLS best practices.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.