2012-11-21 82 views
4

我有這樣的代碼:爲什麼HttpsURLConnection.getServerCertificates()在Java6與Java7中返回不同的結果?

// configure the SSLContext with a TrustManager 
    SSLContext ctx = SSLContext.getInstance("TLS"); 
    ctx.init(new KeyManager[0], 
      new TrustManager[] {new DefaultTrustManager()}, 
      new SecureRandom()); 
    SSLContext.setDefault(ctx); 

    URL url = new URL(urlString); // https://abc.myhost.com 
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); 
    conn.setHostnameVerifier(new HostnameVerifier() { 
      @Override 
      public boolean verify(String arg0, SSLSession arg1) { 
       System.out.println("verify:" + arg0); 
       return true; 
      } 
     }); 

    System.out.println("HTTP status: " + conn.getResponseCode()); 
    Certificate[] certs = conn.getServerCertificates(); 
    int c=0; 
    for (Certificate cert : certs){ 
     String t = cert.getType(); 
     System.out.println(String.format("\ncert[%d]: %s",c,t)); 
     c++; 
     if (pi.verbose) { 
      System.out.println(cert); 
     } 
     else if (cert instanceof X509Certificate) { 
      X509Certificate x509cert = (X509Certificate) cert; 
      System.out.println(x509cert.getSubjectDN().getName()); 
     } 
    } 

運行鍼對特定網站的代碼,在Java 6,我得到一個不同的證書,比我獲得的Java 7,假設主機名是abc.myhost.com。

上的Java6我得到:

cert[0]: X.509 
CN=example.com,OU=Secure Link SSL Pro,O=Company Name Here, 
    STREET=2001 Space Odyssey Dr,L=Weirton,ST=Wv,2.5.4.17=#13053330303034,C=US 

上Java7我得到:

cert[0]: X.509 
CN=abc.myhost.com,OU=Secure Link SSL Pro,O=Company Name Here, 
    STREET=2001 Space Odyssey Dr,L=Weirton,ST=Wv,2.5.4.17=#13053330303034,C=US 

如果我打印出來的有效日期,這些也不同。正如序列號一樣。這些是不同的證書。

在Java 7上它看起來是正確的;在Java 6上,我在主機名和CN之間有分歧。證書看起來不對。

該服務器完全有可能位於代理之後。該服務器的所有者(我正在開發的項目中的合作伙伴)最近也更改了證書。有可能有兩個證書,一個在代理服務器上,另一個在代理服務器後面的服務器上。我讓他們看着這個。

我的問題是,爲什麼我不會在Java7上獲得與Java6上相同的結果? Java在HttpsURLConnection.getServerCertificates()中做了些什麼改變?


對於好奇,這只是一個診斷工作。真正的錯誤是:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: 
    No subject alternative DNS name matching abc.myhost.com found. 

這種情況下的問題通常是主機名和證書中的CN之間存在分歧。我已驗證不同意見,,但僅限於Java 6。我想了解爲什麼Java6和Java7是不同的。


編輯a Python 2.7.1 script返回相同的證書爲的Java6。 SSLConnection.get_peer_cert()向我展示了CN不匹配的證書。

回答

10

我懷疑這是由於在Java客戶端上推出的服務器名稱指示支持7

SNI允許客戶端在SSL中指定的主機名/ TLS初始請求,特別是能夠使用不同的證書在相同的IP地址/端口上託管多個主機名(Apache Httpd調用基於名稱的虛擬主機)。在使用任何HTTP流量(在HTTP級別使用HTTP標頭,但對於HTTPS來說太晚)之前,在SSL/TLS握手期間瞭解所請求的主機名稱可以讓服務器爲正確的證書提供服務。

當客戶端不支持它時,服務器不知道客戶端真正需要哪個主機名,通常會回退到默認主機值並提供默認證書。

(請注意,您將遇到與Win XP以及可能某些移動瀏覽器上的任何版本IE相同的問題。)

編輯:在您編輯之後(URL url = new URL(urlString); // https://abc.myhost.com)。

這似乎證實了SNI問題。 (您可以使用Wireshark查看TLS客戶端Hello消息中是否有服務器名稱擴展名。)

使用Java 7和任何支持SNI的客戶端,在請求https://abc.myhost.com時,您確實會獲得有效的abc.myhost.com證書(提供服務器配置正確),因爲HttpsURLConnection還告知JSSE(Java SSL/TLS堆棧)使用服務器擴展名並使用該URL的主機名啓動SSL/TLS連接。

使用Java 6和任何不支持SNI的客戶端(Python 2.7,至少沒有其他庫),您將獲得服務器在連接到該IP地址和端口時默認提供的證書。

它與HttpsURLConnection.getServerCertificates()SSLConnection.get_peer_cert()無關。相反,這是因爲服務器期望客戶端支持SNI,而一些較老的客戶端/平臺則不支持。

如果您需要在Windows XP上支持Java 6,Python 2.x,Internet Explorer(或其他使用默認MS API的客戶端),您將無法使用SNI。在這種情況下,您應該聯繫服務器管理員更改配置以不使用SNI(如果需要這些多個主機,可能需要額外的IP地址)。

相關問題