我必須使用無效的SSL證書調用託管在Web服務器上的HTTP服務。在開發中,我正在導入keytool的證書,但是每個客戶端安裝的證書都不相同,所以我不能將其捆綁在一起。忽略Java中的SSL驗證
前言:我DO知道跳過SSL驗證真的很醜。在這種特殊情況下,我甚至不需要SSL,系統中的所有其他通信都通過簡單的HTTP。所以我真的不關心MITM攻擊等等。攻擊者不需要去打破SSL,因爲數據沒有SSL。這是對我無法控制的遺留系統的支持。
我使用的是HttpURLConnection
和SSLSocketFactory
,它有一個NaiveTrustManager
和一個NaiveHostnameVerifier
。這適用於我嘗試過的一些自簽名服務器,但不適用於客戶的網站。我得到的錯誤是:
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代碼忽略無效證書。
有什麼想法?
您是否意識到忽略證書(和主機名)驗證會打開與潛在的MITM攻擊的關聯?如果您必須忽略證書錯誤,您的(管理)安全程序似乎存在缺陷。另外,您應該查看Certicom TLS選項,因爲您顯然沒有使用默認的JSSE提供程序。 – Bruno
我很清楚MITM的攻擊。事實上,如果有人能夠在該網絡上執行MITM攻擊,我們就會遇到比攔截通信更大的問題。起初,我正在考慮在軟件中取回證書的一個步驟,如果用戶驗證,請將其存儲以供進一步使用。然而,這是一個比我試圖解決的小問題更大的挑戰。我會看看Certicom TLS選項。 –
WebLogic使用SSL完成了可怕的,可怕的事情,我很遺憾您必須嘗試處理它。他們不遵循Java標準,有自己的搞砸和打破一切的方式。 – erickson