https使用了很多年,而且人們對安全的渴望讓https更加的普及,個人覺得https主要解決了2個主要的安全問題。
1. 通過數字證書保證通信數據發給正確的接收方。
2. 通過對稱加密來保障通信過程中,數據不被竊聽。
其實沒有數字證書也是可以完成通信的,只是瀏覽器會提醒本次通信是不安全的,雖然通信過程中是不會被破解的,但是瀏覽器認為你請求的服務器不一定是真正的服務器而已
瀏覽器會提示不安全,但你仍然收到了展示的頁面,所以說即使證書不正確,也是可以通信的。其實后台用HttpClient來請求https數據的時候,也會遇到此類困擾,此前常用的忽略證書,但這樣雖然也能得到數據,從安全的角度來說是不可接受的,一些大公司里面的代碼安全掃描工具很容易此類問題。
正確的做法是應該將你訪問服務器的證書添加在你的java請求中,具體有以下2種方式
1. 將目標服務器的證書添加至HttpClient所用jdk證書文件
keytool -import -alias ${alias} -keystore ${JAVA_HOME}/jre/lib/security/cacerts -file ${path-to-certificate-file}
如果 keytool 要求你輸入密碼,在你沒有變更過的情況下,該值默認為:changeit
參照下圖導出證書,如命名為:baidu.cer
keytool -import -alias baidu -keystore ${JAVA_HOME}/jre/lib/security/cacerts -file baidu.cer
2. java代碼動態加載目標網站的證書
方法一雖好,但對服務器入侵的太深,而且強依賴環境,甚至有些環境是不容許導入的,那么通過java代碼動態加載證書便十分的可行
keytool -import -alias baidu -keystore java.baidu.cacert -file baidu.cer
java代碼可參照如下(注意要將上面命令生成的證書文件java.baidu.cacert添加至你的項目里面):
import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; public class LoadCert { public static void main(String[] args) throws Exception { // 加載 Keytool 生成的證書文件 String p = "changeit"; char[] passphrase = p.toCharArray(); InputStream in = LoadCert.class.getResourceAsStream("/java.baidu.cacert"); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(in, passphrase); in.close(); // 構造 javax.net.ssl.TrustManager 對象 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509", "SunJSSE"); tmf.init(ks); TrustManager[] tms = tmf.getTrustManagers(); // 使用構造好的 TrustManager 訪問相應的 https 站點 SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tms, new java.security.SecureRandom()); SSLSocketFactory ssf = sslContext.getSocketFactory(); URL myURL = new URL("https://replace.to.your.site.real.url/"); HttpsURLConnection httpsConn = (HttpsURLConnection) myURL.openConnection(); httpsConn.setSSLSocketFactory(ssf); InputStreamReader insr = new InputStreamReader(httpsConn.getInputStream()); int respInt = insr.read(); while (respInt != -1) { System.out.print((char) respInt); respInt = insr.read(); } } }