前言:我知道跳过SSL验证真的很丑陋.在这种具体情况下,我甚至不需要SSL,系统中的所有其他通信都是通过简单的HTTP.所以我真的不在乎MITM攻击等等.攻击者不需要去破坏SSL,因为数据没有SSL.这是对我无法控制的遗留系统的支持.
我正在使用具有NaiveTrustManager和NaiveHostnameVerifier的SSLSocketFactory的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有一种方法来限制有效的主机,但它是空的,所以基本上接受任何东西:
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之外尝试了上面的代码,它工作得很好(固定了Postname中的HostnameVerifier).
然后,我试图在this other question中提出由ipolevoy提出的“-DUseSunHttpHandler = true”到Weblogic参数.它开始工作.
话虽如此,在Oracle Service Bus服务器上切换HTTP处理程序似乎有点冒险.几个星期的时间里,可能会有副作用让我再次咬我
我还试图定义我自己的trustStore,并将其指向包含所需密钥的jssecacert. Weblogic也被忽略,因为它有自己的每个服务器的trustStore设置.所以我要求管理员手动导入所需的键或将Weblogic指向我自己的商店.