java|HttpsURLConnection實現https請求

huasc發表於2017-11-16

轉載一篇關於實現https請求的博文,寫的非常詳盡,原文出處:http://www.blogjava.net/etlan/archive/2006/06/29/55767.html

摘 要

JSSE是一個SSL和TLS的純Java實現,通過JSSE可以很容易地程式設計實現對HTTPS站點的訪問。但是,如果該站點的證照未經權威機構的驗證,JSSE將拒絕信任該證照從而不能訪問HTTPS站點。本文在簡要介紹JSSE的基礎上提出了兩種解決該問題的方法。

引言

過去的十幾年,網路上已經積累了大量的Web應用。如今,無論是整合原有的Web應用系統,還是進行新的Web開發,都要求通過程式設計來訪問某些Web頁面。傳統的方法是使用Socket介面,但現在很多開發平臺或工具如.NET、Java或PHP等都提供了簡單的Web訪問介面,使用這些介面很容易程式設計實現與Web應用系統的互動訪問,即使要訪問那些採用了HTTPS而不是HTTP的Web應用系統。

HTTPS,即安全的超文字傳輸協議,採用了SSL技術,被廣泛使用以保證Web應用系統的安全性。訪問Web應用的程式設計介面大多封裝了SSL,使得訪問HTTPS和訪問HTTP一樣簡單。但是很多中、小型應用系統或基於區域網、校園網的應用系統所使用的證照並不是由權威的認證機構發行或者被其驗證,直接使用這些程式設計介面將不能訪問HTTPS。

本文將在簡要介紹JSSE的基礎上,詳細描述使用JSSE訪問HTTPS的方法,主要說明了如何訪問帶有未經驗證證照的HTTPS站點。

JSSE簡介

Java安全套接擴充套件 (Java Secure Socket Extension, JSSE)是實現Internet安全通訊的一系列包的集合。它是一個SSL和TLS的純Java實現,可以透明地提供資料加密、伺服器認證、資訊完整性等功能,可以使我們像使用普通的套接字一樣使用JSSE建立的安全套接字。JSSE是一個開放的標準,不只是Sun公司才能實現一個JSSE,事實上其他公司有自己實現的JSSE。

在深入瞭解JSSE之前,需要了解一個有關Java安全的概念:客戶端的TrustStore檔案。客戶端的TrustStore檔案中儲存著被客戶端所信任的伺服器的證照資訊。客戶端在進行SSL連線時,JSSE將根據這個檔案中的證照決定是否信任伺服器端的證照。

JSSE中,有一個信任管理器類負責決定是否信任遠端的證照,這個類有如下的處理規則:

⑴ 如果系統屬性javax.net.sll.trustStore指定了TrustStore檔案,那麼信任管理器就去jre安裝路徑下的lib/security/目錄中尋找並使用這個檔案來檢查證照。

⑵ 如果該系統屬性沒有指定TrustStore檔案,它就會去jre安裝路徑下尋找預設的TrustStore檔案,這個檔案的相對路徑為:lib/security/jssecacerts。

⑶ 如果 jssecacerts不存在,但是cacerts存在(它隨J2SDK一起發行,含有數量有限的可信任的基本證照),那麼這個預設的TrustStore檔案就是cacerts。

直接使用類HttpsURLConnection訪問Web頁面

Java提供了一種非常簡潔的方法來訪問HTTPS網頁,即使用類HttpsURLConnection、URL等。這幾個類為支援HTTPS對JSSE相關類做了進一步的封裝,例子如下所示:

URL reqURL = new URL("https://www.sun.com" ); //建立URL物件
HttpsURLConnection httpsConn = (HttpsURLConnection)reqURL.openConnection();

/*下面這段程式碼實現向Web頁面傳送資料,實現與網頁的互動訪問
httpsConn.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(huc.getOutputStream(), "8859_1");
out.write( "……" );
out.flush();
out.close();
*/

//取得該連線的輸入流,以讀取響應內容
InputStreamReader insr = new InputStreamReader(httpsConn.getInputStream();

//讀取伺服器的響應內容並顯示
int respInt = insr.read();
while( respInt != -1){
 System.out.print((char)respInt);
 respInt = insr.read();
}

這段程式碼能夠正常執行,然而把訪問的URL改為https://login.bjut.edu.cn時,程式將丟擲異常javax.net.ssl.SSLException,這是由於https://login.bjut.edu.cn站點的安全證照不被JSSE所信任。根據JSSE簡介中對信任管理器的分析,一種解決這個問題的方法是按照信任管理器的處理規則,把站點的證照放到證照庫檔案jssecacerts中,或者把證照存放到任一TrustStore檔案中,然後設定系統屬性javax.net.sll.trustStore指向該檔案。另一種解決方法則是自己實現信任管理器類,讓它信任我們指定的證照。下面分別介紹這兩種方法。

將證照匯入到TrustStore檔案中

Java提供了命令列工具keytool用於建立證照或者把證照從其它檔案中匯入到Java自己的TrustStore檔案中。把證照從其它檔案匯入到TrustStore檔案中的命令列格式為:

 keytool -import -file src_cer_file –keystore dest_cer_store

其中,src_cer_file為存有證照資訊的原始檔名,dest_cer_store為目標TrustStore檔案。

在使用keytool之前,首先要取得源證照檔案,這個原始檔可使用IE瀏覽器獲得,IE瀏覽器會把訪問過的HTTPS站點的證照儲存到本地。從IE瀏覽器匯出證照的方法是開啟“Internet 選項”,選擇“內容”選項卡,點選“證照…”按鈕,在開啟的證照對話方塊中,選中一個證照,然後點選“匯出…”按鈕,按提示一步步將該證照儲存到一檔案中。最後就可利用keytool把該證照匯入到Java的TrustStore檔案中。為了能使Java程式找到該檔案,應該把這個檔案複製到jre安裝路徑下的lib/security/目錄中。

這樣,只需在程式中設定系統屬性javax.net.sll.trustStore指向檔案dest_cer_store,就能使JSSE信任該證照,從而使程式可以訪問使用未經驗證的證照的HTTPS站點。

使用這種方法,程式設計非常簡單,但需要手工匯出伺服器的證照。當伺服器證照經常變化時,就需要經常進行手工匯出證照的操作。下面介紹的實現X509證照信任管理器類的方法將避免手工匯出證照的問題。
X509證照信任管理器類的實現及應用

在JSSE中,證照信任管理器類就是實現了介面X509TrustManager的類。我們可以自己實現該介面,讓它信任我們指定的證照。

介面X509TrustManager有下述三個公有的方法需要我們實現:

⑴ void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException

該方法檢查客戶端的證照,若不信任該證照則丟擲異常。由於我們不需要對客戶端進行認證,因此我們只需要執行預設的信任管理器的這個方法。JSSE中,預設的信任管理器類為TrustManager。

⑵ void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException

該方法檢查伺服器的證照,若不信任該證照同樣丟擲異常。通過自己實現該方法,可以使之信任我們指定的任何證照。在實現該方法時,也可以簡單的不做任何處理,即一個空的函式體,由於不會丟擲異常,它就會信任任何證照。

⑶ X509Certificate[] getAcceptedIssuers()返回受信任的X509證照陣列。

自己實現了信任管理器類,如何使用呢?類HttpsURLConnection似乎並沒有提供方法設定信任管理器。其實,HttpsURLConnection通過SSLSocket來建立與HTTPS的安全連線,SSLSocket物件是由SSLSocketFactory生成的。HttpsURLConnection提供了方法setSSLSocketFactory(SSLSocketFactory)設定它使用的SSLSocketFactory物件。SSLSocketFactory通過SSLContext物件來獲得,在初始化SSLContext物件時,可指定信任管理器物件。下面用一個圖簡單表示這幾個JSSE類的關係:

假設自己實現的X509TrustManager類的類名為:MyX509TrustManager,下面的程式碼片斷說明了如何使用MyX509TrustManager:


//建立SSLContext物件,並使用我們指定的信任管理器初始化
TrustManager[] tm = {new MyX509TrustManager ()};
SSLContext sslContext = SSLContext.getInstance("SSL","SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());

//從上述SSLContext物件中得到SSLSocketFactory物件
SSLSocketFactory ssf = sslContext.getSocketFactory();

//建立HttpsURLConnection物件,並設定其SSLSocketFactory物件
HttpsURLConnection httpsConn = (HttpsURLConnection)myURL.openConnection();
httpsConn.setSSLSocketFactory(ssf);

這樣,HttpsURLConnection物件就可以正常連線HTTPS了,無論其證照是否經權威機構的驗證,只要實現了介面X509TrustManager的類MyX509TrustManager信任該證照。

小結

本文主要介紹了在HTTPS的證照未經權威機構認證的情況下,訪問HTTPS站點的兩種方法,一種方法是把該證照匯入到Java的TrustStore檔案中,另一種是自己實現並覆蓋JSSE預設的證照信任管理器類。兩種方法各有優缺點,第一種方法不會影響JSSE的安全性,但需要手工匯入證照;第二種方法雖然不用手工匯入證照,但需要小心使用,否則會帶來一些安全隱患。摘 要 JSSE是一個SSL和TLS的純Java實現,通過JSSE可以很容易地程式設計實現對HTTPS站點的訪問。但是,如果該站點的證照未經權威機構的驗證,JSSE將拒絕信任該證照從而不能訪問HTTPS站點。本文在簡要介紹JSSE的基礎上提出了兩種解決該問題的方法。


(完)


相關文章