相互问题
HTTPS協(xié)議是用于確保我們的連接安全的公認(rèn)標(biāo)準(zhǔn)。 理解此協(xié)議的工作原理不是問(wèn)題,并且從2000年起可以使用相應(yīng)的RFC文檔 。
盡管HTTPS的使用如此廣泛,但您仍然可以找到一種無(wú)需不必要的復(fù)雜性就無(wú)法處理此協(xié)議的軟件。 不幸的是,在使用該語(yǔ)言進(jìn)行相互身份驗(yàn)證的過(guò)程中遇到了問(wèn)題,這一點(diǎn)都不會(huì)讓我感到驚訝。 它是Java 。
HTTPS如何工作?
在描述實(shí)現(xiàn)過(guò)程中遇到的問(wèn)題之前,我將描述相互認(rèn)證的工作原理。 HTTPS協(xié)議使用TLS / SSL協(xié)議來(lái)保護(hù)連接。 TLS / SSL協(xié)議定義了身份驗(yàn)證握手,該握手允許以安全方式將任何客戶端與服務(wù)器連接。 在握手過(guò)程中,執(zhí)行以下步驟:
- 客戶端發(fā)送消息以啟動(dòng)連接。
- 服務(wù)器將其證書(shū)發(fā)送給客戶端。
- 客戶端使用由可信機(jī)構(gòu)頒發(fā)的證書(shū)來(lái)驗(yàn)證證書(shū)。
- 服務(wù)器發(fā)送對(duì)客戶端證書(shū)的請(qǐng)求。
- 客戶端將其證書(shū)發(fā)送到服務(wù)器。
- 服務(wù)器驗(yàn)證客戶端的證書(shū)。
- 服務(wù)器和客戶端交換在數(shù)據(jù)加密期間使用的主密鑰。
- 建立連接。
我們和隊(duì)友一起嘗試用Java實(shí)現(xiàn)HTTPS客戶端。 結(jié)合我們對(duì)TLS / SSL握手的了解以及對(duì)curl進(jìn)行手動(dòng)測(cè)試的經(jīng)驗(yàn),我們假設(shè)實(shí)現(xiàn)客戶端只需要三個(gè)文件: 一個(gè)客戶端的證書(shū) , 一個(gè)客戶端的私鑰和一個(gè)用于驗(yàn)證服務(wù)器證書(shū)的受信任證書(shū) 。
哦,我們這么認(rèn)為是多么錯(cuò)誤。
Java –問(wèn)題,解決方案以及為什么如此困難
由于每天使用相互身份驗(yàn)證是很不常見(jiàn)的,因此我們要求世界上最好的消息來(lái)源提供少量幫助。 初看Google叔叔提供的結(jié)果并沒(méi)有揭示實(shí)現(xiàn)背后的復(fù)雜性,但是每次點(diǎn)擊結(jié)果都使我們找到了越來(lái)越混亂的解決方案(其中一些來(lái)自90年代)。 更糟的是,我們不得不使用Apache HttpComponents來(lái)實(shí)現(xiàn)我們的連接,但是大多數(shù)提議的解決方案都基于純Java庫(kù)。
來(lái)自互聯(lián)網(wǎng)的知識(shí)使我們能夠確定:
- Java不能直接使用任何證書(shū)或私鑰(例如curl )
- Java需要單獨(dú)的文件( Java Keystores ),其中可以包含原始證書(shū)和密鑰。
- 我們需要一個(gè)受信任的密鑰庫(kù),其中包含每個(gè)HTTPS連接的服務(wù)器證書(shū)驗(yàn)證所需的證書(shū)。
- 我們需要一個(gè)密鑰密鑰庫(kù),其中包含客戶端的證書(shū)和客戶端的私鑰以進(jìn)行相互認(rèn)證。
首先,我們必須創(chuàng)建受信任的密鑰庫(kù)。 我們使用keytool命令使用證書(shū)創(chuàng)建了密鑰庫(kù):
$ keytool -import -alias trusted_certificate -keystore trusted.jks -file trusted.crt我們將證書(shū)trusted.crt存儲(chǔ)在密鑰庫(kù)文件trusted.jks中,別名為trusted_certificate 。 在執(zhí)行此命令期間,要求我們輸入此密鑰庫(kù)的密碼。 我們稍后使用此密碼來(lái)訪問(wèn)密鑰庫(kù)文件。
要?jiǎng)?chuàng)建密鑰庫(kù),需要執(zhí)行一些其他步驟。 在大多數(shù)情況下,您可能會(huì)從公司獲得兩個(gè)文件,這些文件會(huì)頒發(fā)客戶的證書(shū)。 第一個(gè)文件將是pem格式的客戶證書(shū)。 該證書(shū)將被發(fā)送到服務(wù)器。 第二個(gè)文件是客戶端的私鑰(也是pem格式),在握手期間使用該私鑰來(lái)確認(rèn)您是客戶端證書(shū)的所有者。
不幸的是, Java僅支持PKCS12格式。 因此,我們必須將我們的證書(shū)和私鑰轉(zhuǎn)換為PKCS12格式。 我們可以使用OpenSSL做到這一點(diǎn)。
$ openssl pkcs12 -export \-in client.crt \-inkey client.key \-out key.p12 \-name client我們從文件client.crt和client.key生成了文件key.p12 。 再次需要輸入密碼。 該密碼用于保護(hù)私鑰。
通過(guò)將PKCS12導(dǎo)入新的密鑰庫(kù),我們可以從PKCS12格式的文件中生成另一個(gè)密鑰庫(kù):
$ keytool -importkeystore \-destkeystore key.jks \-deststorepass <<keystore_password>> \-destkeypass <<key_password_in_keystore>> \-alias client \-srckeystore key.p12 \-srcstoretype PKCS12 \-srcstorepass <<original_password_of_PKCS12_file>>這個(gè)命令看起來(lái)有點(diǎn)復(fù)雜,但是解密起來(lái)卻很容易。 在命令的開(kāi)頭,我們聲明名為key.jks的新密鑰庫(kù)的參數(shù)。 我們定義了密鑰庫(kù)的密碼和此密鑰庫(kù)將使用的私鑰的密碼。 我們還將私鑰分配給密鑰庫(kù)中的某些別名(在本例中為client )。 接下來(lái),我們指定源文件( key.p12 ),該文件的格式和原始密碼。
使用trusted.jks和key.jks我們已經(jīng)準(zhǔn)備好進(jìn)行編碼。 在第一步中,我們必須描述我們?nèi)绾问褂妹荑€庫(kù)。
File trustedKeystoreFile = new File("trusted.jks"); File keystoreFile = new File("key.jks");SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustedKeystoreFile, "<<trusted_keystore_password>>".toCharArray()).loadKeyMaterial(keystoreFile, "<<keystore_password>>".toCharArray(), "<<original_password_of_PKCS12_file>>".toCharArray()).build();SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslcontext,new String[]{"TLSv1.2"},null,SSLConnectionSocketFactory.getDefaultHostnameVerifier());我們獲取了密鑰庫(kù)文件,并構(gòu)建了SSL上下文。 接下來(lái),我們創(chuàng)建了套接字工廠,該套接字工廠為我們的請(qǐng)求提供了正確的HTTPS連接。
最后,我們可以從Java調(diào)用端點(diǎn):
try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) {HttpGet httpGet = new HttpGet("https://ourserver.com/our/endpoint");try (CloseableHttpResponse response = httpclient.execute(httGet)) {HttpEntity entity = response.getEntity();System.out.println(response.getStatusLine());EntityUtils.consume(entity);} }做完了 在創(chuàng)建了兩個(gè)額外的文件(密鑰庫(kù))之后,這些文件等效于我們的原始證書(shū)和私鑰,我們使用Java實(shí)現(xiàn)了相互身份驗(yàn)證 。 用Java實(shí)現(xiàn)HTTPS連接也許有一定道理,但是現(xiàn)在這只是一個(gè)頭痛的事情。
翻譯自: https://www.javacodegeeks.com/2016/03/mutual-problems-2.html
總結(jié)
- 上一篇: apache thrift_使用Java
- 下一篇: jetty eclipse_3个步骤实现