2012-11-20 332 views
10

我必須使用無效的SSL證書調用託管在Web服務器上的HTTP服務。在開發中,我正在導入keytool的證書,但是每個客戶端安裝的證書都不相同,所以我不能將其捆綁在一起。忽略Java中的SSL驗證

前言:我DO知道跳過SSL驗證真的很醜。在這種特殊情況下,我甚至不需要SSL,系統中的所有其他通信都通過簡單的HTTP。所以我真的不關心MITM攻擊等等。攻擊者不需要去打破SSL,因爲數據沒有SSL。這是對我無法控制的遺留系統的支持。

我使用的是HttpURLConnectionSSLSocketFactory,它有一個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代碼忽略無效證書。

有什麼想法?

+4

您是否意識到忽略證書(和主機名)驗證會打開與潛在的MITM攻擊的關聯?如果您必須忽略證書錯誤,您的(管理)安全程序似乎存在缺陷。另外,您應該查看Certicom TLS選項,因爲您顯然沒有使用默認的JSSE提供程序。 – Bruno

+0

我很清楚MITM的攻擊。事實上,如果有人能夠在該網絡上執行MITM攻擊,我們就會遇到比攔截通信更大的問題。起初,我正在考慮在軟件中取回證書的一個步驟,如果用戶驗證,請將其存儲以供進一步使用。然而,這是一個比我試圖解決的小問題更大的挑戰。我會看看Certicom TLS選項。 –

+2

WebLogic使用SSL完成了可怕的,可怕的事情,我很遺憾您必須嘗試處理它。他們不遵循Java標準,有自己的搞砸和打破一切的方式。 – erickson

回答

2

上面的代碼實際上沒有錯。這個問題似乎與Weblogic和這個Certicom TLS模塊有關。當我看服務器選項,SSL高級我看到我可以指定自定義HostnameVerifier(SSLMBean.HostnameVerifier),但建議干擾證書驗證的唯一元素已被棄用。

我在Weblogic之外嘗試了上面的代碼,它工作得很漂亮(雖然在帖子中修復了HostnameVerifier)。

然後我嘗試在this other question的ipolevoy建議的Weblogic參數中添加「-DUseSunHttpHandler = true」。它開始工作。

這就是說,在Oracle Service Bus服務器上切換HTTP處理程序似乎有點風險。有可能是在幾周內回來咬我的副作用...

我還試圖定義我自己的trustStore並將其指向包含所需密鑰的jssecacert。它也被Weblogic忽略,因爲它爲每個服務器都有自己的trustStore設置。所以我打算讓管理員手動導入所需的密鑰或將Weblogic指向我自己的商店。

+0

順便說一句,我剛剛發現HostnameVerifier無法自定義,並與「javax.net.ssl.SSLKeyException:[安全性:090504]從intranet.company.com收到的證書鏈 - xxxx失敗的主機名驗證檢查失敗。包含* .company.com,但檢查預期的intranet.company.com「。 Weblogic,我恨你。我真的這樣做。 –

0

實際上,這是Weblogic版本低於10.3的一個已知錯誤。5,其中有一個可從Oracle獲得的補丁。有關詳細信息,請參閱My Oracle Support中的文檔1474989.1。

上面的修復程序是Oracle推薦的(但支持的)解決方法,它可以工作,但不是首選解決方案。

首選的解決方案是下載Oracle文章中提到的修補程序,並將SSL主機名驗證程序替換爲也是Weblogic 10.3.5及更高版本的一部分的新主機名驗證程序。如果您希望在支持方面保持與Oracle的兼容性,這是一條路。