java SSLContext創(chuàng)建方式
概述
HTTPS相對于HTTP多了SSL(security sock layer),應(yīng)用層將數(shù)據(jù)丟給TCP時,需要經(jīng)過SSL層的加密處理;TCP層的數(shù)據(jù)丟給應(yīng)用層時,需要經(jīng)過SSL層的解密處理。
因此網(wǎng)絡(luò)中傳輸?shù)亩际羌用芎蟮臄?shù)據(jù),提高的通信的安全性。

HTTPS除了擁有傳輸加密的功能,還提供身份認(rèn)證,防止對端偽造身份。這個是通過HTTPS證書實(shí)現(xiàn)。
注下圖中對HTTPS握手過程進(jìn)行簡化,握手過程的關(guān)鍵步驟就是證書校驗(yàn)及對稱加密秘鑰的協(xié)商,另外客戶端證書的校驗(yàn)是可選的。

問題
標(biāo)準(zhǔn)證書都是第三方機(jī)構(gòu)頒發(fā)的,需要付費(fèi)找第三方機(jī)構(gòu)申請。有些出于節(jié)省成本或者省事的目的,一般會使用工具生成自簽名的證書。
瀏覽器訪問這種自簽名證書的網(wǎng)址時就會彈出不安全的提示,如果是java代碼訪問這種網(wǎng)址時就會拋出異常。
解決這種問題有2個途徑,一種是忽略證書認(rèn)證,另一種就是將自簽名的證書添加到受信證書庫中。
SSLContext關(guān)鍵參數(shù)說明
SSLContext都是通過SSLContext.getInstance(String protocol)方法獲取,獲取后需要調(diào)用init方法初始化。
init方法簽名如下:
public final void init(KeyManager[] km, TrustManager[] tm,SecureRandom random)
KeyManager
- 用于HTTPS雙向認(rèn)證時,客戶端向服務(wù)端發(fā)送的認(rèn)證信息(證書);與不同的服務(wù)端交互時,客戶端可能用不同的身份(證書),所以需要KeyManager進(jìn)行管理。
- 如果只是單向認(rèn)證,KeyManager[]可以為空。
TrustManager
- 用于客戶端檢測服務(wù)端發(fā)送過來的證書。
KeyStore:只是單純的存儲證書、秘鑰數(shù)據(jù),不包含任何邏輯。
TrustManager,KeyManager可以使用KeyStore得到證書列表進(jìn)行相應(yīng)的業(yè)務(wù)處理。
忽略證書認(rèn)證
有些語言比如Python是可以修改全局配置去忽略證書檢查,但是Java好像沒有提供這種功能,所以需要覆寫證書認(rèn)證的類X509TrustManager 。
忽略證書認(rèn)證后,雙方通信的時候仍然是加密的,只是無法對服務(wù)端的身份進(jìn)行認(rèn)證,有服務(wù)端偽造的風(fēng)險。如果都是在內(nèi)網(wǎng),可以采用這種簡潔的手法。
public SSLContext buildSSLContext() throws Exception {
X509TrustManager x509TrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init((KeyManager[]) null, new X509TrustManager[] {x509TrustManager}, (SecureRandom) null);
return sslContext;
}
將自簽名證書放到受信證書庫
將證書直接導(dǎo)入到JDK的證書庫
待續(xù)
通過編碼的方式將證書放到受信證書列表中
@Override
public SSLContext buildSSLContext() throws Exception {
KeyStore keyStore = buildKeyStore();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init((KeyManager[]) null, tmf.getTrustManagers(), (SecureRandom) null);
return sslContext;
}
private KeyStore buildKeyStore() throws Exception {
// 獲取自簽名的證書
List<X509Certificate> certs = loadHttpsCerts();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
// 獲得jdk證書庫中的證書
Arrays.stream(trustManagerFactory.getTrustManagers())
.filter(manager -> manager instanceof X509TrustManager)
.findFirst()
.ifPresent(manager -> {
List<X509Certificate> jdkCerts = Arrays.asList(((X509TrustManager) manager).getAcceptedIssuers());
certs.addAll(jdkCerts);
});
certs.stream().forEach(cert -> {
keyStore.setCertificateEntry(cert.getSubjectDN().getName(), cert);
});
return keyStore;
}
private List<X509Certificate> loadHttpsCerts() throws IOException, CertificateException {
List<X509Certificate> certs = new ArrayList<>(32);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(HTTPS_CERTS_PATH);
for (Resource resource : resources) {
InputStream inStream = resource.getInputStream();
CertificateFactory factory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) factory.generateCertificate(inStream);
certs.add(cert);
}
return certs;
}
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java對象以Hash結(jié)構(gòu)存入Redis詳解
這篇文章主要介紹了Java對象以Hash結(jié)構(gòu)存入Redis詳解,和Java中的對象非常相似,卻不能按照J(rèn)ava對象的結(jié)構(gòu)直接存儲進(jìn)Redis的hash中,因?yàn)镴ava對象中的field是可以嵌套的,而Redis的Hash結(jié)構(gòu)不支持嵌套結(jié)構(gòu),需要的朋友可以參考下2023-08-08
JAVA多線程處理for循環(huán)數(shù)據(jù)詳細(xì)講解
這篇文章主要給大家介紹了關(guān)于JAVA多線程處理for循環(huán)數(shù)據(jù)的相關(guān)資料,我們在代碼中經(jīng)常需要使用for循環(huán)這個操作來達(dá)到目的,而當(dāng)for循環(huán)的次數(shù)過多時我們會發(fā)現(xiàn)執(zhí)行效率會變的很低,整體耗時非常多,需要的朋友可以參考下2023-07-07
Android開發(fā)在輪播圖片上加入點(diǎn)擊事件的方法
這篇文章主要介紹了Android開發(fā)在輪播圖片上加入點(diǎn)擊事件的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-11-11

