2013-07-31 46 views
8

我正在從SSL套接字讀取,但主機與證書不匹配(例如,host = "localhost")。我期待着這個例外,但下面的代碼愉快地與遠程服務器交談時沒有任何問題。SSLSocket忽略域名不匹配

try (
    final Socket socket = SSLSocketFactory.getDefault().createSocket(host, port); 
    final OutputStream os = socket.getOutputStream(); 
    final InputStream is = socket.getInputStream()) { 

    os.write(("HEAD/HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n\r\n").getBytes()); 
    os.flush(); 

    final byte[] bytes = new byte[1024]; 
    int n; 
    while ((n = is.read(bytes)) != -1) { 
     System.out.print(new String(bytes, 0, n)); 
    } 
    System.out.println(); 
} catch (final IOException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
} 

因此,我已經試過另一種方法:

try { 
    final HttpURLConnection conn = (HttpURLConnection) new URL("https://" + host + ":" + port + "/").openConnection(); 

    try (InputStream is = conn.getInputStream()) { 
     IOUtils.copy(is, System.out); 
    } catch (final IOException e1) { 
     try (InputStream es = conn.getErrorStream()) { 
      if (es != null) { 
       IOUtils.copy(es, System.out); 
      } 
     } 
    } 
} catch (final IOException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
} 

不幸的是我還沒有得到SSL例外,只是WARN在日誌中: 2013-07-31 16:02:27,182 WARN nio - javax.net.ssl.SSLException: Received fatal alert: certificate_unknown

如何獲得SSL異常,如果證書不符合?

回答

16

SSL/TLS協議規範是模塊化的,並與用於驗證遠程主機的規範分離。這些其他規範分爲兩類:驗證證書本身可以信任(RFC 3280/5280)並驗證證書中的身份(RFC 6125或RFC 2818中的HTTPS)。

JSSE在SSLSocket(或SSLEngine)API中集成了SSL協議和證書驗證,但不處理驗證標識符(同樣重要)。

這主要是由於SSLSocket/SSLEngine可以應用於任何應用協議(例如HTTP,IMAP,SMTP,LDAP等),但是用於驗證標識符的規則是不同規格的小的變化),直到RFC 6125(這仍然是相當新的)。

HttpsURLConnection處理兩者,因爲它也使用遵循HTTPS規範(RFC 2818,第3.1節)的HostnameVerifier。這與SSLSocket/SSLEngine API分開進行。 對於其他協議,您可能需要實現協議規範所說的內容。

這就是說,自Java 7以來,有一種機制可以直接作爲SSLSocket/SSLEngine API的一部分驗證證書的身份。

SSLParameters sslParams = new SSLParameters(); 
sslParams.setEndpointIdentificationAlgorithm("HTTPS"); 
sslSocket.setSSLParameters(sslParams); 

如果主機名稱不匹配,使用此應該會引發異常。

HTTPS與RFC 6125中更統一的規範(除了後者認爲IP地址超出範圍之外)之間沒有大的區別。即使您沒有使用HTTPS,對其他協議使用其標識規範通常也是有意義的。 (也許一個「RFC 6125」端點識別算法可能會出現在更高版本的Java中。)

+0

感謝您提供詳細信息。 –

2

主機名匹配不是SSL的一部分,它是HTTPS的一部分。請參閱javax.net.ssl.HostnameVerifier。

您引用的日誌表示HttpsURLConnection拋出的SSLException

+0

追查到該日誌後,我發現它記錄在'org.eclipse.jetty.io.nio.SelectChannelEndPoint' (這個測試用例和服務器在同一個JVM上)。此外,我發現讀取connection.getInputStream()時拋出的IOException實際上是'javax.net.ssl.SSLHandshakeException',這正是我需要的:-) –