忽略Java中的SSLvalidation

我必须使用无效的SSL证书调用托管在Web服务器上的HTTP服务。 在开发中,我使用keytool导入证书,但每个客户端安装的证书都不同,所以我不能捆绑它。

前言:我知道跳过SSLvalidation真的很难看。 在这种特定情况下,我甚至不需要SSL,系统中的所有其他通信都是简单的HTTP。 所以我真的不关心MITM攻击等。 攻击者无需破解SSL,因为数据没有SSL。 这是对我无法控制的遗留系统的支持。

我正在使用带有NaiveTrustManagerNaiveHostnameVerifierSSLSocketFactory HttpURLConnection 。 这适用于我尝试过的一些自签名服务器,但不适用于客户的网站。 我得到的错误是:

 javax.net.ssl.SSLKeyException: [Security:090477]Certificate chain received from xxxxxxxxxx was not trusted causing SSL handshake failure. at com.certicom.tls.interfaceimpl.TLSConnectionImpl.fireException(Unknown Source) at com.certicom.tls.interfaceimpl.TLSConnectionImpl.fireAlertSent(Unknown Source) at com.certicom.tls.record.handshake.HandshakeHandler.fireAlert(Unknown Source) at com.certicom.tls.record.handshake.HandshakeHandler.fireAlert(Unknown Source) at com.certicom.tls.record.handshake.ClientStateReceivedServerHello.handle(Unknown Source) at com.certicom.tls.record.handshake.HandshakeHandler.handleHandshakeMessage(Unknown Source) at com.certicom.tls.record.handshake.HandshakeHandler.handleHandshakeMessages(Unknown Source) at com.certicom.tls.record.MessageInterpreter.interpretContent(Unknown Source) at com.certicom.tls.record.MessageInterpreter.decryptMessage(Unknown Source) at com.certicom.tls.record.ReadHandler.processRecord(Unknown Source) at com.certicom.tls.record.ReadHandler.readRecord(Unknown Source) at com.certicom.tls.record.ReadHandler.readUntilHandshakeComplete(Unknown Source) at com.certicom.tls.interfaceimpl.TLSConnectionImpl.completeHandshake(Unknown Source) at com.certicom.tls.record.WriteHandler.write(Unknown Source) at com.certicom.io.OutputSSLIOStreamWrapper.write(Unknown Source) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65) at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123) at java.io.FilterOutputStream.flush(FilterOutputStream.java:123) at weblogic.net.http.HttpURLConnection.writeRequests(HttpURLConnection.java:154) at weblogic.net.http.HttpURLConnection.getInputStream(HttpURLConnection.java:358) at weblogic.net.http.SOAPHttpsURLConnection.getInputStream(SOAPHttpsURLConnection.java:37) at weblogic.net.http.HttpURLConnection.getResponseCode(HttpURLConnection.java:947) at (my own code) 

我的SimpleSocketFactory看起来像:

 public static final SSLSocketFactory getSocketFactory() { if ( sslSocketFactory == null ) { try { // get ssl context SSLContext sc = SSLContext.getInstance("SSL"); // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{ new NaiveTrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { log.debug("getAcceptedIssuers"); return new java.security.cert.X509Certificate[0]; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { log.debug("checkClientTrusted"); } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { log.debug("checkServerTrusted"); } } }; sc.init(null, trustAllCerts, new java.security.SecureRandom()); // EDIT: fixed the following line that was redeclaring SSLSocketFactory sslSocketFactory, returning null every time. Same result though. sslSocketFactory = sc.getSocketFactory(); HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory); // EDIT: The following line has no effect //HttpsURLConnection.setDefaultHostnameVerifier(new NaiveHostNameVerifier()); } catch (KeyManagementException e) { log.error ("No SSL algorithm support: " + e.getMessage(), e); } catch (NoSuchAlgorithmException e) { log.error ("Exception when setting up the Naive key management.", e); } } return sslSocketFactory; } 

NaiveHostnameVerifier有一种限制有效主机的方法,但它保留为null,所以基本上接受任何东西:

 public class NaiveHostnameVerifier implements HostnameVerifier { String[] patterns; public NaiveHostnameVerifier () { this.patterns=null; } public NaiveHostnameVerifier (String[] patterns) { this.patterns = patterns; } public boolean verify(String urlHostName,SSLSession session) { if (patterns==null || patterns.length==0) { return true; } else { for (String pattern : patterns) { if (urlHostName.matches(pattern)) { return true; } } return false; } } } 

用法是这样的:

  try { conn = (HttpURLConnection)url.openConnection(); if (conn instanceof HttpsURLConnection) { ((HttpsURLConnection)conn).setSSLSocketFactory(SimpleSSLSocketFactory.getSocketFactory()); // EDIT: added this line, the HV has to be set on connection, not on the factory. ((HttpsURLConnection)conn).setHostnameVerifier(new NaiveHostnameVerifier()); } conn.setDoInput(true); conn.setDoOutput(true); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-type","application/x-www-form-urlencoded"); conn.connect(); StringBuffer sbContent = new StringBuffer(); // (snip) DataOutputStream stream = new DataOutputStream(conn.getOutputStream ()); stream.writeBytes(sbContent.toString()); stream.flush(); stream.close(); } catch (ClassCastException e) { log.error("The URL does not seem to point to a HTTP connection"); return null; } catch (IOException e) { log.error("Error accessing the requested URL", e); return null; } 

当我在搜索错误消息时,大多数人只是在他们的商店中导入证书,但我再也不能这样做,因为我不知道它将是哪个证书。 如果这不起作用,我唯一的选择是创建一个可以下载证书并以更简单的方式添加它的工具,但是我宁愿让我的Java代码忽略无效的证书。

任何的想法 ?

事实上,上面的代码没有任何问题。 问题似乎在于Weblogic和Certicom TLS模块。 当我查看服务器选项, SSLAdvanced时,我发现我可以指定自定义HostnameVerifier(SSLMBean.HostnameVerifier),但是不建议使用能够干扰证书validation的唯一元素。

我在Weblogic之外尝试了上面的代码并且它运行得很漂亮(虽然修复了post中的HostnameVerifier)。

然后我尝试在ipolevoy的另一个问题中将“-DUseSunHttpHandler = true”添加到Weblogic参数中。 它开始工作了。

话虽这么说,在Oracle Service Bus服务器上切换HTTP处理程序似乎有点冒险。 几周之内可能会有一些副作用让我感到困惑……

我还尝试定义自己的trustStore并将其指向包含所需密钥的jssecacert。 Weblogic也忽略了它,因为它为每个服务器都有自己的trustStore设置。 所以我要求管理员手动导入所需的密钥或将Weblogic指向我自己的商店。

实际上,这是10.3.5以下的Weblogic版本中的一个已知错误,其中有一个可从Oracle获得的补丁。 有关详细信息,请参阅My Oracle Support中的文档1474989.1。

上面的修复是Oracle推荐的(但受支持的)解决方法,它可以工作,但不是首选的解决方案。

首选解决方案是下载Oracle文章中提到的补丁,并将SSL主机名validation程序替换为新的一个,它也是Weblogic 10.3.5及更高版本的一部分。 如果您希望在支持方面与Oracle保持一致,那么这就是您的选择。