2018年3月31日 星期六

Java - HTTPS 檢查證書的安全連線


如何在JDK1.5中支援TLSv1.2這篇文章中,有講到如何對TLSv1.2的server進行HTTPS request,但這只是最簡單的實作例子,沒有考慮到憑證安全的檢查,如果被例如中間人攻擊的話,可能會連線請求中間被竄改了資料而不自知。

在前例中,可以看到在自訂的TrustManager 中,所有的method (getAcceptedIssuers(), checkServerTrusted(), checkClientTrusted())都沒有被實作內容,
如果發生了中間人攻擊,也就是在client端和server端的連線中間被某中間人攔截,
這時中間人可能會修改某些資料偽裝server端回傳資料給我們(client)端,
如果我們沒有對其憑證進行檢查的話,可能就無法察覺此攻擊了。

尤其是行動裝置的場合,例如手機在連到了駭客分享的wifi,程式如果不檢查HTTPS連線有無被劫持,就很容昜造成資安問題。

經過了查詢資料及自行實作後,我在這邊提供一個如何對 HTTPS 的 憑證進行驗證的方法。


  1. 為了簡單化,我使用JDK1.8,跟JDK1.5不同,JDK1.8支持TLSv1.2,
    所以不用跟上一篇文(如何在JDK1.5中支援TLSv1.2) 一樣使用BoncyCastle。
    並且把Excpetion全部throws出去,不用try-catch來使程式碼較為清晰。

    Note:
    其實JDK1.8在對TLSv1.2連線時,是不用自已建立TrustManager的,並且自己就會檢查憑證,並在檢查憑證有問題時拋出錯誤。
    但是要注意如果自己建立了TrustManager來用在連線中,卻不檢查憑證的話,在憑證有問題時是不會拋出錯誤的。
    而JDK1.5因為要使用BoncyCastle並會需要自已建立TrustManager,所以就要特別使用檢查憑證的TrustManager。
  2. 我們可以使用封包監測工具,例如Fiddler,來模擬中間人攻擊,如果我們把Java的HTTPS request 掛上Proxy,讓連線中間經過Fiddler的話,相當於就是Fiddler當了中間人,這時對使用了沒有進行憑證檢查的TrustManager的Java程式,是不會發現有任何異狀的。

步驟:

  1. 這裡要連的實驗對像跟之前一樣是號稱只支持TLSv1.2的fancyssl網站,首先去此網站上下載憑證。
    下載方式如下圖,按下F12打開開發者console後,選Security --> View Certificate --> 詳細資料 --> 複制到檔案。
  2. 使用java的工具 keytool 加入憑證到你要的憑證檔案中
    keytool -import -alias {別名} -keystore {要被放入憑證的憑證檔} -file {從網站上下載的憑證}
    例如:
    keytool -import -alias fancyssl -keystore D:\test_cer -file D:\fancyssl_cer.cer
    之後會要你打密碼,第一次產生憑證檔自己取。如果是本來就有的檔案通常密碼沒改就是預設 : changeit
  3. 接下來我展示在Java1.8下的三種寫法:
    1. HttpsTest_notSafe.java
      (自己建立的,沒有檢查憑證的TrustManager)
      import java.io.BufferedReader;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.InputStreamReader;
      import java.net.HttpURLConnection;
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.security.KeyManagementException;
      import java.security.KeyStore;
      import java.security.KeyStoreException;
      import java.security.NoSuchAlgorithmException;
      import java.security.NoSuchProviderException;
      import java.security.SecureRandom;
      import java.security.cert.Certificate;
      import java.security.cert.CertificateException;
      import java.security.cert.CertificateFactory;
      import java.security.cert.X509Certificate;
      
      import javax.net.ssl.HostnameVerifier;
      import javax.net.ssl.HttpsURLConnection;
      import javax.net.ssl.SSLContext;
      import javax.net.ssl.SSLSession;
      import javax.net.ssl.TrustManager;
      import javax.net.ssl.TrustManagerFactory;
      import javax.net.ssl.X509TrustManager;
      
      public class HttpsTest_notSafe {
      
       public static void main(String[] args)
         throws NoSuchAlgorithmException, KeyManagementException, MalformedURLException, IOException, NoSuchProviderException, KeyStoreException, CertificateException {
        // 將request導向 Fiddler (127.0.0.1:8888) 可模擬中間人攻擊
        System.setProperty("http.proxyHost", "127.0.0.1");
        System.setProperty("http.proxyPort", "8888");
        System.setProperty("https.proxyHost", "127.0.0.1");
        System.setProperty("https.proxyPort", "8888");
        //
        
        // 自訂的TrustManager,沒有檢查憑證,不安全
        TrustManager trustManager = new X509TrustManager() {
         public X509Certificate[] getAcceptedIssuers() {
          return new X509Certificate[0];
         }
         public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
          // do nothing
         }
         public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
          // do nothing
         }
        };
      
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        // 自訂的TrustManager,沒有檢查憑證,不安全
        sslContext.init(null, new TrustManager[] { trustManager }, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        
        HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) (new URL("https://fancynossl.hboeck.de/")).openConnection();
        httpsUrlConnection.connect();
      
        // 印出Response
        printFromInputStream(httpsUrlConnection.getInputStream());
        
        httpsUrlConnection.disconnect();
        
       }
       
       static void printFromInputStream(InputStream in) throws IOException {
        BufferedReader responseBufferedReader = new BufferedReader((new InputStreamReader(in)));
        StringBuffer responseTextStringBuffer = new StringBuffer();
        String tempString = null;
        while ((tempString = responseBufferedReader.readLine()) != null) {
         responseTextStringBuffer.append(tempString + "\n");
        }
        String responseText = responseTextStringBuffer.toString();
        System.out.println(responseText);
       }
      }
    2. HttpsTest_safe1.java
      (JDK1.8 直接使用自帶TLSv1.2的https支持,不用建立TrustManager)
      import java.io.BufferedReader;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.InputStreamReader;
      import java.net.HttpURLConnection;
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.security.KeyManagementException;
      import java.security.KeyStore;
      import java.security.KeyStoreException;
      import java.security.NoSuchAlgorithmException;
      import java.security.NoSuchProviderException;
      import java.security.SecureRandom;
      import java.security.cert.Certificate;
      import java.security.cert.CertificateException;
      import java.security.cert.CertificateFactory;
      import java.security.cert.X509Certificate;
      
      import javax.net.ssl.HostnameVerifier;
      import javax.net.ssl.HttpsURLConnection;
      import javax.net.ssl.SSLContext;
      import javax.net.ssl.SSLSession;
      import javax.net.ssl.TrustManager;
      import javax.net.ssl.TrustManagerFactory;
      import javax.net.ssl.X509TrustManager;
      
      public class HttpsTest_safe1 {
      
       public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException, MalformedURLException, IOException, NoSuchProviderException, KeyStoreException, CertificateException {
        // 將request導向 Fiddler (127.0.0.1:8888) 可模擬中間人攻擊
        System.setProperty("http.proxyHost", "127.0.0.1");
        System.setProperty("http.proxyPort", "8888");
        System.setProperty("https.proxyHost", "127.0.0.1");
        System.setProperty("https.proxyPort", "8888");
        //
      
        //建立連線
        HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) (new URL("https://fancynossl.hboeck.de/")).openConnection();
        httpsUrlConnection.connect();
      
        // 印出Response
        printFromInputStream(httpsUrlConnection.getInputStream());
        
        httpsUrlConnection.disconnect();  
       }
       
       static void printFromInputStream(InputStream in) throws IOException {
        BufferedReader responseBufferedReader = new BufferedReader((new InputStreamReader(in)));
        StringBuffer responseTextStringBuffer = new StringBuffer();
        String tempString = null;
        while ((tempString = responseBufferedReader.readLine()) != null) {
         responseTextStringBuffer.append(tempString + "\n");
        }
        String responseText = responseTextStringBuffer.toString();
        System.out.println(responseText);
       }
      }
    3. HttpsTest_safe2.java
      (自已建立的,使用憑證檔產生出TrustManager)
      import java.io.BufferedReader;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.InputStreamReader;
      import java.net.HttpURLConnection;
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.security.KeyManagementException;
      import java.security.KeyStore;
      import java.security.KeyStoreException;
      import java.security.NoSuchAlgorithmException;
      import java.security.NoSuchProviderException;
      import java.security.SecureRandom;
      import java.security.cert.Certificate;
      import java.security.cert.CertificateException;
      import java.security.cert.CertificateFactory;
      import java.security.cert.X509Certificate;
      
      import javax.net.ssl.HostnameVerifier;
      import javax.net.ssl.HttpsURLConnection;
      import javax.net.ssl.SSLContext;
      import javax.net.ssl.SSLSession;
      import javax.net.ssl.TrustManager;
      import javax.net.ssl.TrustManagerFactory;
      import javax.net.ssl.X509TrustManager;
      
      public class HttpsTest_safe2 {
      
       public static void main(String[] args)
         throws NoSuchAlgorithmException, KeyManagementException, MalformedURLException, IOException, NoSuchProviderException, KeyStoreException, CertificateException {
        // 將request導向 Fiddler (127.0.0.1:8888) 可模擬中間人攻擊
        System.setProperty("http.proxyHost", "127.0.0.1");
        System.setProperty("http.proxyPort", "8888");
        System.setProperty("https.proxyHost", "127.0.0.1");
        System.setProperty("https.proxyPort", "8888");
        //  
        
              //從憑證產生TrustManager
              String password = "changeit";  //預設密碼為changeit
              //讀取憑證
              File file = new File("D:/test_cer");
              //通常會將憑證都加到%Java_Home%/lib/security/cacerts/中
              //File file = new File(System.getProperty("java.home") + "/lib/security/cacerts");
              InputStream in = new FileInputStream(file);
              KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
              keyStore.load(in, password.toCharArray());
              in.close();
              //建立TrustManager
              TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
              trustManagerFactory.init(keyStore);
      
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
      
        HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) (new URL("https://fancynossl.hboeck.de/")).openConnection();
        httpsUrlConnection.connect();
      
        // 印出Response
        printFromInputStream(httpsUrlConnection.getInputStream());
        
        httpsUrlConnection.disconnect();
        
       }
       
       static void printFromInputStream(InputStream in) throws IOException {
        BufferedReader responseBufferedReader = new BufferedReader((new InputStreamReader(in)));
        StringBuffer responseTextStringBuffer = new StringBuffer();
        String tempString = null;
        while ((tempString = responseBufferedReader.readLine()) != null) {
         responseTextStringBuffer.append(tempString + "\n");
        }
        String responseText = responseTextStringBuffer.toString();
        System.out.println(responseText);
       }
      }
我們可以打開 Fiddler,並在 Tools --> Options --> HTTPS,勾選 "Decrypt HTTPS traffic 設定攔截 HTTPS request 模擬中間人,
並且在上述的三個Java程式中,對設定Proxy的程式碼註解或不註解來測試。
可以發現以下結果:
有拋出錯誤沒有拋出錯誤有拋出錯誤沒有拋出錯誤
有經過Fiddler沒有經過Fiddler
HttpsTest_notSafe.java沒有拋出錯誤沒有拋出錯誤
HttpsTest_safe1.java有拋出錯誤沒有拋出錯誤
HttpsTest_safe2.java有拋出錯誤沒有拋出錯誤

可以看到,如果是JDK1.8如果使用了自帶TLSv1.2的支持寫法,即不自己建立TrustManager的話,程式是可以自行檢查出憑證錯誤的。

但如果使用了自已建立的TrustManager時,就要使用有檢查憑證的TrustManager才行,此篇文章使用的是由憑證檔產生TrustManager。
除了由憑證檔產生TrustManager以外,也可由自己撰寫檢查憑證相關的TrustManager method ((getAcceptedIssuers(), checkServerTrusted(), checkClientTrusted())) 。

原始碼下載:
HttpsTest.7z

參考資料
  1. Java 使用自签证书访问https站点
  2. 苹果核 - Android App 安全的HTTPS 通信
  3. Sun Java System Application Server Enterprise Edition 8.2 管理指南
  4. Java Keytool的使用及申請憑證(以Microsoft Active Directory Certificate Services為例)
  5. java中 SSL认证和keystore使用

Java使用Selenium進行瀏覽器自動化操作

Selenium是一個瀏覽器自動化的工具,可以使用它來控制瀏覽器執行各種操作,
在這篇文中我簡單介紹JAVA如何使用Selenium及展示一個範例。

前置步驟:
  1. 下載 Selenium 的 Java Lib
    到Selenium的下載頁面下載
    1. Selenium Client & WebDriver Language Bindings
    2. Selenium Standalone Server (我下載的為3.11.0)
  2. 下載各瀏覽器的WebDriver
    1. Chrome : ChromeDriver
    2. Firefox : geckodriver
  3. 在Java程式中引入Library和指定WebDriver就可以開始控制瀏覽器了。
在這邊展示一個簡單的程式,控制瀏覽器進到Google首頁,在搜尋框中打上關鍵字,然後將10頁的查詢結果Title和Link url印出來。

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class SeleniumTest1 {

 public static void main(String[] args) {
  System.out.println("Start");
  //自己選擇要用的瀏覽器 WebDriver
  //用Chrome
  System.setProperty("webdriver.chrome.driver","D:\\JavaLib\\Selenium\\webdrivers\\Chrome\\chromedriver.exe");
  WebDriver driver = new ChromeDriver();
  
  //用Firefox
  //System.setProperty("webdriver.gecko.driver","D:\\javaLib\\selenium-java-3.10.0\\Firefox\\geckodriver.exe");
  //WebDriver driver = new FirefoxDriver();
  
  //使用implicitlyWait,抓取DOM時,會等DOM出現才抓,最多等10秒
  driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
  driver.get("https://www.google.com.tw/"); //開啟瀏覽器到 Google 首頁
  
  //抓取DOM element,#lst-ib 為Google搜尋框
  WebElement searchInput = driver.findElement(By.id("lst-ib"));
  
  //執行Javascript範例
  //將Google搜尋框打上字,"keyword"
  JavascriptExecutor javascriptExecutor = (JavascriptExecutor) driver;
  javascriptExecutor.executeScript("arguments[0].value='keyword';", searchInput);
  
  //用WebElement物件直接操做DOM element範例
  //抓取DOM element,name=btnK 為Google搜尋按鈕
  WebElement searchBtn = driver.findElement(By.name("btnK"));
  searchBtn.click();
  
  //印出十頁的所有搜尋結果Title和Link url
  for (int i = 0; i < 10; i++) {
   //抓取DOM elements, (.r a) 為Google搜尋結果的link
   List<WebElement> searchReultATagList = driver.findElements(By.cssSelector(".r a"));
   for (WebElement searchReultATag : searchReultATagList) {
    System.out.println(searchReultATag.getText() + " : ");
    System.out.println(searchReultATag.getAttribute("href"));
    System.out.println("=======================");
   }
   //抓取DOM element, #pnnext 為Google搜尋下一頁按鈕
   WebElement nextPageBtn = driver.findElement(By.id("pnnext"));
   nextPageBtn.click();
  }
  
  driver.quit(); //關閉瀏覽器
 }

}



sss
參考資料

  1. Selenium using Java - The path to the driver executable must be set by the webdriver.gecko.driver system property
  2. How to get selenium to wait for ajax response?
  3. Selenium驱动火狐、IE、Edge和Chrome浏览器的方法

2018年3月20日 星期二

使用代理 + fiddler 來監看 java 的 http/https 封包 / 模擬中間人攻擊

在 Java 中進行如下 propxy 的設置,將網路請求都先經過本地端的 fiddler (這裡fiddler的port為8888),就可以在fiddler中監看 java 發出的封包

System.setProperty("http.proxyHost", "127.0.0.1");
System.setProperty("http.proxyPort", "8888");

System.setProperty("https.proxyHost", "127.0.0.1");
System.setProperty("https.proxyPort", "8888");

而此時也可以模擬中間人攻擊,因為http/https請求經過了Fiddler,可以在此情況下測試自己的防範中間人攻擊的程式

或是使用 org.apache.commons.httpclient.HttpClient 的話,用
HttpClient client = new HttpClient();
client.getHostConfiguration().setProxy("127.0.0.1", 8888);


在 Fiddler Scripit 裡的
static function OnBeforeRequest(oSession: Session) { }
裡寫
if(oSession.HostnameIs("xxx.com") && oSession.isHTTPS){
            oSession.oRequest.headers.UriScheme = "http";
            oSession.hostname="127.0.0.1";
}

可以把 xxx.com 的 HTTPS request 轉向導到 127.0.0.1 HTTP request

其他例子:
if(oSession.host == "forum.cyberlink.com"){
            oSession.oRequest.headers.UriScheme = "http";
            oSession.host="127.0.0.1:8081";
}


如何在JDK1.5中支援TLSv1.2

使用Java 傳 HTTPS request 時,如果接收端的server (例如別家公司開放的web api)是只使用 TLSv1.2 時,可能會因為舊版的 jdk 沒有支援,而造成連線錯誤。

如果是使用 JDK1.8,其支持 TLSv1.2,並且且預設就使用 TLSv1.2 來進行 HTTPS 的連線,
寫法可以參考之前寫的這篇文章 "以Java 由Url下載圖片" 裡面的 "getHttpURLConnectionFromHttps()" 方法來進行 HTTPS 的請求。

但是例如如果為較舊的 JDK1.5,則沒有支持 TLSv1.2,此時還用 JDK1.8的寫法去請求 TLSv1.2的server時,就會產生錯誤。

在這篇文章中,我紀錄了我找到的方法,如何讓 JDK1.5 也能進行對 TLSv1.2 server的請求。

=================================

我使用的JDK為 jdk1.5.0_22
測試的網站為 https://fancyssl.hboeck.de/
此網站聲稱只為TLSv1.2的request開放
可以使用線上SSL檢測工具來查看此網站的SSL相關設定
https://www.ssllabs.com/ssltest/analyze.html?d=fancyssl.hboeck.de

需要的 jar

  1. Bouncy Castle 的 Library : 
    1. Provider 
    2. DTLS/TLS API/JSSE Provider
  2. 解開Java密鑰長度限制的
    "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files"
    1. JDK 1.5 可到這裡下載 "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 5.0"。


步驟:

  1. 導入Bouncy Castle的 jar
    Bouncy Castle 是一個提供許多密碼演法等的 Java Library,而 TLSv1.2 跟 舊版的差別其中就有密碼演算等不同,其中更深的原理我沒有鑽研,在此只展示找到的可行解決方案。

    首先先到 Bouncy Castle官網 下載 Provider 及 DTLS/TLS API/JSSE Provider 的 jar 檔,我這邊選擇的為 bcprov-jdk15on-159.jarbctls-jdk15on-159.jar。 請下載並加到 Java 專案的 Library中。
  2. 測試程式進對指定的url進行TLSv1.2 HTTPS 請求,並將回應印出來,在這裡會將整個網頁的源始碼印出來。

    測試程式碼如下:
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.security.SecureRandom;
    import java.security.Security;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
    
    public class HttpsClientRequestTest {
    
        public static void main(String[] args)
                throws Exception
            {
                Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
                Security.insertProviderAt(new BouncyCastleProvider(), 1);
    
                Security.removeProvider(BouncyCastleJsseProvider.PROVIDER_NAME);
                Security.insertProviderAt(new BouncyCastleJsseProvider(), 2);
    
                /*
                 * TEST CODE ONLY. If writing your own code based on this test case, you should configure
                 * your trust manager(s) using a proper TrustManagerFactory, or else the server will be
                 * completely unauthenticated.
                 */
                TrustManager tm = new X509TrustManager()
                {
                    public X509Certificate[] getAcceptedIssuers()
                    {
                        return new X509Certificate[0];
                    }
    
                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
                    {
                     //do nothing
                    }
    
                    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
                    {
                     //do nothing
                    }
                };
                
                HostnameVerifier customHostnameVerifier = new HostnameVerifier() {
                     public boolean verify(String arg0, SSLSession arg1) {        
                          return true;
                     }
                };
    
                SSLContext sslContext = SSLContext.getInstance("TLSv1.2", BouncyCastleJsseProvider.PROVIDER_NAME);
                sslContext.init(null, new TrustManager[]{ tm }, new SecureRandom());
                HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
                HttpsURLConnection.setDefaultHostnameVerifier(customHostnameVerifier);
                
                HttpURLConnection httpUrlConnection = (HttpURLConnection) (new URL("https://fancynossl.hboeck.de")).openConnection();
                
                httpUrlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 4.2.1; Nexus 7 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166  Safari/535.19");
                httpUrlConnection.connect();
    
                InputStream is = null;
         if (httpUrlConnection.getResponseCode() >= 400) {  
             is = httpUrlConnection.getErrorStream();  
         } else {  
             is = httpUrlConnection.getInputStream();  
         }
          
                BufferedReader responseBufferedReader = new BufferedReader((new InputStreamReader(is)));
                StringBuffer responseTextStringBuffer = new StringBuffer();
                String tempString = null;
                while((tempString = responseBufferedReader.readLine()) != null) {
                    responseTextStringBuffer.append(tempString + "\n");
                }
                String responseText = responseTextStringBuffer.toString();
                httpUrlConnection.disconnect();
          
                System.out.println(responseText);
            }
    }
    
  3. 因為還沒有開放 Java 的密鑰長度限制,所以應該會出現以下錯誤java.security.InvalidKeyException: Illegal key sizeException
    此時將下載的 "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 5.0" 裡面的 "local_policy.jar" 和 "US_export_policy.jar" 覆蓋到JAVA_HOME\jre\lib\security\ 之下的 "local_policy.jar" 和 "US_export_policy.jar" ,再執行一次,應該就會成功了。
    下載頁面如圖所示:
    程式成功時輸出如圖,可以看到網頁的html源始碼被印了出來 (Eclipse):



參考資料:
  1. java.lang.IllegalArgumentException: TLSv1.2 on JRE 1.5
  2. The Legion of the Bouncy Castle
  3. bcgit/bc-java
  4. 解决java.io.IOException: HTTPS hostname wrong: should be
  5. AES加密时抛出java.security.InvalidKeyException: Illegal key size or default parameters