SSL exception: "No subject alternative names matching IP address ..." & "No name

When you want to establish an SSL connection like this;

URL url = new URL("https://localhost:9443/soap_rpc");

 

You may get an exception like this;

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching localhost found
     at com.sun.net.ssl.internal.ssl.Alerts.getSSLException ( Alerts.java: 174 )
     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal ( SSLSocketImpl.java: 1591 )
     at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE ( Handshaker.java: 187 )
     at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE ( Handshaker.java: 181 )
     at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate ( ClientHandshaker.java: 975 )
     at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage ( ClientHandshaker.java: 123 )
     at com.sun.net.ssl.internal.ssl.Handshaker.processLoop ( Handshaker.java: 516 )
     at com.sun.net.ssl.internal.ssl.Handshaker.process_record ( Handshaker.java: 454 )
     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord ( SSLSocketImpl.java: 884 )
     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake ( SSLSocketImpl.java: 1096 )
     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake ( SSLSocketImpl.java: 1123 )
     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake ( SSLSocketImpl.java: 1107 )
     at sun.net.www.protocol.https.HttpsClient.afterConnect ( HttpsClient.java: 405 )
     at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect ( AbstractDelegateHttpsURLConnection.java: 166 )
     at sun.net.www.protocol.http.HttpURLConnection.getOutputStream ( HttpURLConnection.java: 832 )
     at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream ( HttpsURLConnectionImpl.java: 230 )
     at SSLPost.test ( SSLPost.java: 74 )
     at SSLPost.<init> ( SSLPost.java: 29 )
     at SSLPost.main ( SSLPost.java: 98 )
Caused by: java.security.cert.CertificateException: No name matching localhost found
     at sun.security.util.HostnameChecker.matchDNS ( HostnameChecker.java: 210 )
     at sun.security.util.HostnameChecker.match ( HostnameChecker.java: 77 )
     at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity ( X509TrustManagerImpl.java: 264 )
     at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted ( X509TrustManagerImpl.java: 250 )
     at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate ( ClientHandshaker.java: 954 )
     ...  14  more

 

But, you have installed the server certificate, generated keystore and all work fine. So, what may be the problem?

 

Let's come to the solution;

While making an SSL connection, HttpsClient steps in and does basic server authentication to protect against URL spoofing which includes verifying that the name of the server is found in the certificate. HttpsClient#checkURLSpoofing method checks server identity according to "RFC 2818: HTTP over TLS" - "Section 3.1 Server Identity".

HttpsClient basically uses HostNameChecker first to check the hostname against the names specified in the certificate. Then, if it fails, HostNameVerifier's turn comes and it's used to verify the host name. As mentioned above, while not overridden, SUN's default behaviour is returning false for this verification. This means, if your HostNameChecker fails, you will get one of the exceptions written in the title according to your URL's hostname type.

So, what can be done to "not-fail" HostNameChecker?

HostNameChecker#match method's implementation is like below;

sun.security.util.HostNameChecker
public  void  match ( String hostName, X509Certificate x509certificate throws  CertificateException  {
     if  ( isIpAddress ( hostName )) {
         matchIP ( hostName, x509certificate ) ;
     else  {
         matchDNS ( hostName, x509certificate ) ;
     }
}

If the incoming hostname is IP, (by matchIP method), it will be searched in available subject alternative names and throw CertificateException("No subject alternative names matching IP address ...") if no matching ip value found.

On the other hand, if the incoming hostname is DNS, (by matchDNS method), it will be searched in available subject alternative names but, different from IP matching algorithm, DNS matching will compare the hostname with the CommonName value from certificate if available. If neither matches with the hostname, a CertificateException("No name matching ... found") will be thrown.

What we can conclude from these details is;

  • if you'd like to connect via using IP as hostname; your certificate should include that ip value as a subject alternative name value (of type IPAddress : key=7).
  • if you'd like to connect via using DNS as hostname; your certificate should either include that DNS name as a subject alternative name value (of type DNS : key=2) or as a CommonName(CN) value.

Hope it helps...

 

 

Java 中,`java.security.cert.CertificateException: No subject alternative names matching IP address 127.0.0.1 found` 和 `javax.net.ssl.SSLHandshakeException` 错误通常在 SSL/TLS 握手过程中出现,原因是服务器证书的主题备用名称(Subject Alternative Names,SAN)中不包含客户端尝试连接的 IP 地址。以下是一些可能的解决方法: ### 1. 更新服务器证书 确保服务器证书的主题备用名称(SAN)中包含客户端尝试连接的 IP 地址(如 127.0.0.1)。当服务器证书的 SAN 不匹配客户端使用的主机名或 IP 时,会抛出 `CertificateException` [^2]。 ### 2. 使用主机名而非 IP 地址 如果可能,使用主机名而不是 IP 地址进行连接。因为 DNS 匹配算法会将主机名与证书中的通用名称(CommonName)进行比较 [^2]。 ### 3. 配置信任库 确保客户端信任服务器的证书。可以通过配置 `ssl.truststore` 和 `ssl.truststore.password` 来指定信任库及其密码。虽然 `ssl.truststore.password` 在技术上是可选的,但强烈建议使用,否则信任库的完整性检查将被禁用 [^3]。 示例代码如下: ```java import javax.net.ssl.*; import java.io.FileInputStream; import java.io.IOException; import java.security.*; import java.security.cert.CertificateException; public class SSLConnectionExample { public static void main(String[] args) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException { // 加载信任库 KeyStore trustStore = KeyStore.getInstance(&quot;JKS&quot;); trustStore.load(new FileInputStream(&quot;path/to/truststore.jks&quot;), &quot;truststorepassword&quot;.toCharArray()); // 创建信任管理器 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); // 创建 SSL 上下文 SSLContext sslContext = SSLContext.getInstance(&quot;TLS&quot;); sslContext.init(null, trustManagers, new SecureRandom()); // 创建 SSLSocketFactory SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); // 使用 SSLSocketFactory 进行连接 try (SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(&quot;127.0.0.1&quot;, 443)) { // 进行其他操作 } } } ``` ### 4. 禁用主机名验证(不推荐用于生产环境) 在开发或测试环境中,可以临时禁用主机名验证。但这会降低安全性,不建议在生产环境中使用。 示例代码如下: ```java import javax.net.ssl.*; import java.io.IOException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; public class DisableHostnameVerification { public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException, IOException { // 创建信任所有证书的信任管理器 TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { } } }; // 创建 SSL 上下文 SSLContext sslContext = SSLContext.getInstance(&quot;TLS&quot;); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); // 创建主机名验证器,始终返回 true HostnameVerifier allHostsValid = (hostname, session) -&gt; true; // 设置默认的 SSLSocketFactory 和 HostnameVerifier HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); // 进行连接 java.net.URL url = new java.net.URL(&quot;https://127.0.0.1&quot;); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.connect(); } } ``` ### 5. 检查 SSL 协议和密码套件 确保客户端和服务器使用兼容的 SSL 协议和密码套件。可以通过 `ssl.enabled.protocols` 和 `ssl.cipher.suites` 进行配置 [^3]。 示例代码如下: ```java import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.net.URL; public class SSLProtocolAndCipherSuiteExample { public static void main(String[] args) throws IOException { URL url = new URL(&quot;https://127.0.0.1&quot;); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); // 设置 SSL 协议 conn.setEnabledProtocols(new String[]{&quot;TLSv1.2&quot;, &quot;TLSv1.1&quot;, &quot;TLSv1&quot;}); // 设置密码套件 conn.setEnabledCipherSuites(new String[]{&quot;TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256&quot;}); conn.connect(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值