亚洲成精品动漫久久精久,九九在线精品视频播放,黄色成人免费观看,三级成人影院,久碰久,四虎成人欧美精品在永久在线

掃一掃
關(guān)注微信公眾號

用JSSE定制SSL連接
2005-12-01   

在深入介紹JSSE之前,讓我們來一個(gè)簡單的客戶機(jī)/服務(wù)器程序,程序中包含了兩個(gè)文件:SimpleSSLServer和SimpleSSLClient。在運(yùn)行程序之前,你需要配置下面這些KeyStore和TrestStore文件:
· 一個(gè)客戶端的KeyStore文件,該文件中包含了對Alice和Bob的授權(quán)。
· 一個(gè)服務(wù)器端的KeyStore文件,該文件中包含了對server的授權(quán)。
· 一個(gè)名為clientTrust的客戶端TrustStore文件,該文件中包含了對server的授權(quán)。
· 一個(gè)名為serverTrust的服務(wù)器端TrustStore文件,該文件中包含了對Alice和Bob的授權(quán)。
使用keytool可以幫助你創(chuàng)建這些文件(該工具在Java的bin目錄下):
· 一個(gè)客戶端的KeyStore文件,該文件中包含了對Alice和Bob的授權(quán)。
在命令窗口中輸入下面的命令:
keytool -genkey -alias alice -keystore clientKeys
窗口中會出現(xiàn)下面的提示,根據(jù)提示輸入相應(yīng)的信息:

輸入keystore密碼: password
您的名字與姓氏是什么?
[Unknown]: Alice
您的組織單位名稱是什么?
[Unknown]: Development
您的組織名稱是什么?
[Unknown]: DCQ
您所在的城市或區(qū)域名稱是什么?
[Unknown]: ChongQing
您所在的州或省份名稱是什么?
[Unknown]: ChongQing
該單位的兩字母國家代碼是什么
[Unknown]: CH
CN=Alice, OU=Development, O=DCQ, L=ChongQing, ST=ChongQing, C=CH 正確嗎?
[否]: 是
輸入的主密碼


(如果和 keystore 密碼相同,按回車):
通過相同的方式可以建立對Bob的授權(quán)。
keytool -genkey -alias bob -keystore clientKeys
注意在名字與姓氏一欄中填寫B(tài)ob。在完成后可以鍵入下面的命令來檢測是否已經(jīng)正確完成了授權(quán)。
keytool -list -v -keystore clientKeys????
· 一個(gè)服務(wù)器端的KeyStore文件,該文件中包含了對server的授權(quán)。
在命令窗口中鍵入下面的命令:
keytool -genkey -alias server -keystore serverKeys
注意將密碼設(shè)為password,名字與姓氏設(shè)定為Server。完成授權(quán)后同樣可以通過上面提到的命令來檢測。
· 一個(gè)名為clientTrust的客戶端TrustStore文件,該文件中包含了對server的授權(quán)。以及一個(gè)名為serverTrust的服務(wù)器端TrustStore文件,該文件中包含了對Alice和Bob的授權(quán)。
keytool -export -alias server -keystore clientKeys -file server.cer
輸入keystore密碼: password
保存在文件中的認(rèn)證


keytool -export -alias alice -keystore clientKeys -file alice.cer
輸入keystore密碼: password
保存在文件中的認(rèn)證


keytool -export -alias bob -keystore clientKeys -file bob.cer
輸入keystore密碼: password
保存在文件中的認(rèn)證


這樣keytool就在當(dāng)前目錄下創(chuàng)建了三個(gè)授權(quán)文件。然后我們將server.cer文件導(dǎo)入到clientTrust文件中;將alice.cer和bob.cer導(dǎo)入到serverTruest文件中:


keytool -import -alias server -keystore clientTrust -file server.cer
keytool -import -alias alice -keystore serverTrust -file alice.cer
keytool -import -alias bob -keystore serverTrust-file bob.cer
到目前為止,在當(dāng)前目錄下包含clientKeys,serverKeys,clientTrust,serverTrust四個(gè)文件。完成了KeyStore和TrustStore的設(shè)置后就可以運(yùn)行例子程序了。首先需要運(yùn)行服務(wù)器程序:
java -Djavax.net.ssl.keyStore=serverKeys
-Djavax.net.ssl.keyStorePassword=password
-Djavax.net.ssl.trustStore#NAME?
-Djavax.net.ssl.trustStorePassword=password SimpleSSLServer
在命令行中我們指定了keyStore屬性為serverKeys。由于服務(wù)器程序需要獲得客戶端的授權(quán)信息,我們指定trustStore為serverTrust。這樣SSLSimpleServer就可以驗(yàn)證由SSLSimpleClient提供的授權(quán)信息。當(dāng)服務(wù)器程序成功運(yùn)行后,你會看到下面的提示:
SimpleSSLServer running on port 49152????
這時(shí)候服務(wù)器會等待客戶端發(fā)出建立連接的申請。如果你希望在另一個(gè)端口上運(yùn)行服務(wù)器程序,可以在命令中指定-port xxx參數(shù),其中xxx是端口號。


然后在另一個(gè)命令窗口中運(yùn)行客戶端程序:
java -Djavax.net.ssl.keyStore=clientKeys
-Djavax.net.ssl.keyStorePassword=password
-Djavax.net.ssl.trustStore=clientTrust
-Djavax.net.ssl.trustStorePassword=password SimpleSSLClient
客戶端程序會試圖向本機(jī)的49152端口建立SSL連接。同樣你可以通過-port參數(shù)指定端口號,也可以通過-host參數(shù)指定主機(jī)名稱。當(dāng)連接成功后,會出現(xiàn)下面的提示信息:


Connected
同時(shí)在服務(wù)器端會提示用戶客戶端已經(jīng)連接成功。


SimpleSSLServer


讓我們先來看一下SimpleSSLServer。在main()方法中,程序獲得了缺省的SSLServerSocketFactory對象;然后利用SSLServerSocketFactory創(chuàng)建一個(gè)SimpleSSLServer對象,最后調(diào)用start()方法啟動SimpleSSLServer對象。


SSLServerSocketFactory ssf=
(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
SimpleSSLServer server=new SimpleSSLServer(ssf,port);
server.start();
由于服務(wù)器是在一個(gè)單獨(dú)的線程中運(yùn)行的,main()方法啟動了服務(wù)器之后就退出了。start()方法啟動了一個(gè)新的線程,該線程執(zhí)行run()方法中的代碼。在run()方法中創(chuàng)建了一個(gè)SSLServerSocket對象,然后設(shè)定服務(wù)器需要進(jìn)行客戶端驗(yàn)證:
SSLServerSocket serverSocket= (SSLServerSocket)serverSocketFactory.createServerSocket(port);
serverSocket.setNeedClientAuth(true);
調(diào)用run()方法后,程序進(jìn)入了一個(gè)死循環(huán),等待客戶端的連接申請。循環(huán)中的每個(gè)Socket對應(yīng)一個(gè)HandshakeCompletedListener對象(該對象是用來顯示客戶驗(yàn)證信息中的標(biāo)識名稱[distinguished name]的)。Socket的InputStream對象被包裝在一個(gè)InputDisplayer對象中,這個(gè)InputDisplayer對象運(yùn)行在另外一個(gè)線程中,用來將Socket接收到的數(shù)據(jù)發(fā)送到System.out。下面的代碼是SimpleSSLServer中的主循環(huán)體:
while (true) {
String ident=String.valueOf(id++);
//監(jiān)聽連接請求.
SSLSocket socket=(SSLSocket)serverSocket.accept();
//通過使用HandshakeCompletedListener對象,程序進(jìn)行授權(quán)驗(yàn)證.
HandshakeCompletedListener hcl=new SimpleHandshakeListener(ident);
socket.addHandshakeCompletedListener(hcl);
InputStream in=socket.getInputStream();
new InputDisplayer(ident, in);
}
程序中的SimpleHandshakeListener類實(shí)現(xiàn)了HandshakeCompletedListerner接口。在SimpleHandshakeListener類中實(shí)現(xiàn)了handshakeCompleted()方法,該方法在SSL握手階段完成后將被JSSE調(diào)用。它將顯示出客戶端的標(biāo)識名稱:
class SimpleHandshakeListener implements HandshakeCompletedListener
{
String ident;
/**
* 構(gòu)造函數(shù).
*/
public SimpleHandshakeListener(String ident)
{
this.ident=ident;
}
/**當(dāng)SSL握手過程完成后該方法被激活. */
public void handshakeCompleted(HandshakeCompletedEvent event)
{
//顯示授權(quán)信息.
try {
X509Certificate
cert=(X509Certificate)event.getPeerCertificates()[0];
String peer=cert.getSubjectDN().getName();
System.out.println(ident+": Request from "+peer);
}
catch (SSLPeerUnverifiedException pue) {
System.out.println(ident+": Peer unverified");
}
}
}
用紅色字體表示的兩行代碼是這段代碼的核心:getPeerCertificates()方法返回一個(gè)X509Certificated對象的數(shù)組。這些X509Certificated對象創(chuàng)建了客戶端的身份標(biāo)識。在數(shù)組中的第一個(gè)元素是客戶端的驗(yàn)證信息,而最后一個(gè)通常是CA驗(yàn)證。當(dāng)我們有了客戶端的驗(yàn)證信息后。我們可以得到其中的標(biāo)識名稱,并將它傳送到System.out。


SimpleSSLClient


SimpleSSLClient類比較簡單,但是在后面的一些比較復(fù)雜的例子中的類會繼承該類。在getSLLSocketFactory()方法中,程序返回缺省的工廠類:


protected SSLSocketFactory getSSLSocketFactory()
throws IOException, GeneralSecurityException
{
return (SSLSocketFactory)SSLSocketFactory.getDefault();
}
在runClient()方法中,程序處理了輸入?yún)?shù)后,獲得SSLSockFactory對象,調(diào)用connect()方法連接到服務(wù)器程序。在connect()方法中,程序首先創(chuàng)建一個(gè)SSLSocket對象,然后調(diào)用SSLSocket對象的startHandshang()方法啟動和服務(wù)器端的握手過程。當(dāng)握手過程完成后,會觸發(fā)一個(gè)HandshakeCompletedEvent事件。在服務(wù)器端的HandshakeCompletedListener對象會處理這個(gè)事件。事實(shí)上,JSSE可以自動啟動握手過程,但是必須是在第一次有數(shù)據(jù)通過Socket傳輸?shù)那闆r下。由于在例子程序中,直到用戶在鍵盤上輸入信息后才會有數(shù)據(jù)通過Socket傳輸,而我們希望服務(wù)器端及時(shí)報(bào)告連接情況,因此我們用startShake()方法來手工激活握手過程。

boolean done=false;
while (!done) {
String line=reader.readLine();
if (line!=null) {
writer.write(line);
writer.write('\n');
writer.flush();
}
else done=true;
}
定制KeyStore和TrustStore?
還記得我們是如何運(yùn)行客戶端的嗎?我們需要在命令行中指定keyStore,keyStorePasword, trustStore和trustStorePassword參數(shù),以至于整個(gè)命令顯得過于冗長。事實(shí)上你可以在程序中指定KeyStore和TrustStore,后面的例子中將告訴你如何實(shí)現(xiàn)這一點(diǎn)。同時(shí)在例子中還會演示如何配置多個(gè)SSLSocketFactory對象,其中每個(gè)SSLSocketFactory對象對應(yīng)不同的KeyStore和TrustStore設(shè)置。如果沒有這種技術(shù),在同一個(gè)虛擬機(jī)上的所有安全連接都只能使用同一個(gè)KeyStore和TrustStore。對于比較小的應(yīng)用程序,這也許不會產(chǎn)生問題;但是對于那些比較大的應(yīng)用程序來說,這絕對是一個(gè)嚴(yán)重的缺陷。


在下面的例子中,我們將使用CustomTrustStoreClient來動態(tài)定義KeyStore和TrustStore。首先讓我們先運(yùn)行一下CustomTrustStoreClient:


java CustomTrustStoreClient
為什么運(yùn)行CustomTrustStoreClient時(shí)不需要指定KeyStore和TrustStore參數(shù)呢?這是應(yīng)為在CustomTrustStoreClient的代碼中指定了KeyStore(ClientKeys)和TrustStore(ClientTruts)以及它們的密鑰(password)。如果你想使用其他的KeyStore、 TrustStore或密鑰,可以使用-ks、-kspass、-ts和-tspass參數(shù)來指定。下面讓我們來看一下CustomTrustStoreClient的getSSLSocketFactory()方法。該方法通過調(diào)用getTrustManager()方法獲得一個(gè)TurstManager對象數(shù)組,通過調(diào)用getKeyManagers()方法獲得一個(gè)KeyManager對象數(shù)組。然后利用得到的TurstManager和KeyManager對象數(shù)組構(gòu)造一個(gè)SSLContext對象,最后通過SSLContext對象的getSocketFactory()方法來配置JSSE。需要注意的是在調(diào)用SSLContext類的init()方法時(shí)使用的參數(shù)。第一個(gè)參數(shù)是KeyManager對象數(shù)組。第二個(gè)參數(shù)和第一個(gè)參數(shù)類似,是TrustManager數(shù)組。如果前兩個(gè)參數(shù)被設(shè)定為null,程序?qū)⑹褂萌笔〉腒eyManager和TrustStore(缺省的KeyStore來源于系統(tǒng)屬性中的javax.net.ssl.keyStore和javax.net.ssl.keyStorePassword屬性;缺省的TrustStore來源于系統(tǒng)屬性中的javax.net.ssl.trustStore和javax.net.ssl.trustStorePassword屬性)。通過設(shè)定第三個(gè)參數(shù)可以指定JSSE中的隨機(jī)數(shù)產(chǎn)生器(Random Number Generate, RNG)。由于在SSL中隨機(jī)數(shù)的產(chǎn)生是一個(gè)很敏感的問題,錯(cuò)誤使用這個(gè)參數(shù)會導(dǎo)致安全連接變得不安全,因此我在例子中使用了null。這樣程序?qū)⑹褂萌笔〉牟⑶沂前踩腟ecureRandom對象。
protected SSLSocketFactory getSSLSocketFactory()
throws IOException, GeneralSecurityException
{
// 調(diào)用getTrustManagers方法獲得trust managers
TrustManager[] tms=getTrustManagers();
// 調(diào)用getKeyManagers方法獲得key manager
KeyManager[] kms=getKeyManagers();
//利用KeyManagers創(chuàng)建一個(gè)SSLContext對象.用獲得的KeyStore和
// TrustStore初始化該SSLContext對象.我們使用缺省的SecureRandom.
SSLContext context=SSLContext.getInstance("SSL");
context.init(kms, tms, null);
//最后獲得了SocketFactory對象.
SSLSocketFactory ssf=context.getSocketFactory();
return ssf;
}
下面讓我們看一看CustomKeyStoreClient類中的getKeyMangers()方法是如何初始化KeyManagers對象數(shù)組的:


protected KeyManager[] getKeyManagers()
throws IOException, GeneralSecurityException
{
// 獲得KeyManagerFactory對象.
String alg=KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmFact=KeyManagerFactory.getInstance(alg);
// 配置KeyManagerFactory對象使用的KeyStoree.我們通過一個(gè)文件加載
// KeyStore.
FileInputStream fis=new FileInputStream(keyStore);
KeyStore ks=KeyStore.getInstance("jks");
ks.load(fis, keyStorePassword.toCharArray());
fis.close();
// 使用獲得的KeyStore初始化KeyManagerFactory對象
kmFact.init(ks, keyStorePassword.toCharArray());
// 獲得KeyManagers對象
KeyManager[] kms=kmFact.getKeyManagers();
return kms;
}
首先的任務(wù)是獲得一個(gè)KeyManagerFactory對象,但是你必須知道應(yīng)該使用哪種算法。JSSE中提供了一個(gè)缺省的KeyManagerFactory算法(程序員也可以通過指定ssl.KeyManagerFacotory.algorithm屬性指定缺省算法)。獲得KeyManagerFactory對象后就可以加載KeyStore文件了,程序中通過一個(gè)InputStream對象將信息從文件送入KeyStore對象中。在這個(gè)過程之前,KeyStore對象需要知道輸入流的格式(例子中我使用的是jks)和密鑰。當(dāng)我們完成了KeyStore的加載后,我們就可以用它來初始化KeyManagerFactory對象了。通常在JSSE中,在KeyStore中的所有證書使用和KeyStore相同的密碼,但是通過創(chuàng)建KeyManagerFactory對象你可以突破這個(gè)限制。在初始化了KeyManagerFactory對象后,通常使用getKeyManager()方法來獲得KeyManager對象數(shù)組。程序員通過使用和getKeyMangers()方法類似的流程來初始化TrustManager數(shù)組,這里我就不再重復(fù)了。
實(shí)現(xiàn)一個(gè)KeyManager類
到目前為止,我們已經(jīng)知道如何在程序中動態(tài)生成KeyStore和TrustStore了。最后一個(gè)例子將告訴你如何實(shí)現(xiàn)一個(gè)KeyManager類。


當(dāng)運(yùn)行前幾個(gè)例子的時(shí)候,不知道大家是否注意到服務(wù)器端顯示的授權(quán)的標(biāo)識名稱。在前面我們授權(quán)給了兩個(gè)人:Alice和Bob,在運(yùn)行程序時(shí)JSSE會從中任選一個(gè)。在我的計(jì)算機(jī)上JSSE選擇的總是Bob,或許在你的計(jì)算機(jī)上情況會有所不同。下面讓我們來看一看最后一個(gè)例子程序:SelectAliasClient。這個(gè)例子使你能夠在運(yùn)行客戶端時(shí)使用指定的授權(quán)。例如你需要指定使用Alice的授權(quán),由于Alice的別名是alice,你需要在命令窗口中鍵入下面的命令:


java SelectAliasClient -alias alice
當(dāng)客戶端和服務(wù)器端成功連接后,客戶器端會出現(xiàn)下面的信息:
1: New connection request
1: Request from CN=Alice, OU= Development, O=DCQ, L=ChongQing,
ST=ChongQing, C=CH
為了使程序使用指定的授權(quán),我們需要實(shí)現(xiàn)X509KeyManager接口(X509KeyManager是JSSE中最常用的KeyManager)。X509KeyManager接口在SSL握手階段使用了幾個(gè)方法來獲得授權(quán)。下面是X509KeyManager接口獲得授權(quán)的過程:


1.JSSE調(diào)用chooseClientAlias()方法獲得指定的授權(quán)。


2.chooseClientAlias()方法調(diào)用X509KeyManager接口的getClientAlaises()方法獲得SSLSocket對象使用的所有授權(quán)的別名,然后檢查指定的授權(quán)別名是否有效。
3.JSSE將別名作為參數(shù)調(diào)用X509KeyManager接口的getCertificateChain()和getPrivateKey()方法,這樣就獲得了指定授權(quán)的相關(guān)信息。
在例子程序中,X509KeyManager接口的實(shí)現(xiàn)類是AliasForcingKeyManager。在該類中最重要的方法就是就是chooseClientAlias()方法。下面是該方法的源代碼:


public String chooseClientAlias(String[] keyType, Principal[] issuers,
Socket socket)
{
//對于每一種類型的授權(quán),都需要調(diào)用一次getClientAliases()方法來驗(yàn)
// 證別名是否有效.
boolean aliasFound=false;
for (int i=0; i< i++) !aliasFound; &&>


String[] validAliases=baseKM.getClientAliases(keyType, issuers);
if (validAliases!=null) {
for (int j=0; j< !aliasFound; && j++)>


if (validAliases[j].equals(alias)) aliasFound=true;
}
}
}
if (aliasFound) return alias;
else return null;
}
我們可以看到在程序中,chooserClientAlias()方法實(shí)際上多次調(diào)用了getClientAliases()方法,每次都針對不同的授權(quán)類型。AliasForingKeyManager還實(shí)現(xiàn)了X509KeyManager接口的其他五個(gè)方法,在這里就不再一一贅述了。


然后我們就可以在程序中用AliasForingKeyManager對象來替代KeyManager對象了。在getSSLSocketFactory()方法中,我們只需要將通過調(diào)用getKeyManagers()方法獲得KeyManager對象數(shù)組,然后將其強(qiáng)制轉(zhuǎn)化為AliasForcingKeyManager對象就可以了。下面是新的getSSLSocketFactory()方法的代碼:


protected SSLSocketFactory getSSLSocketFactory()
throws IOException, GeneralSecurityException
{
// 調(diào)用父類中的方法獲得TrustManager和KeyManager
KeyManager[] kms=getKeyManagers();
TrustManager[]tms=getTrustManagers();
// 如果指定了別名,將KeyManagers包裝在AliasForcingKeyManager對象中.
if (alias!=null) {
for (int i=0; i< i++)>


// 這里只處理了X509KeyManager接口
if (kms instanceofX509KeyManager)
kms=new AliasForcingKeyManager((X509KeyManager)kms,alias);
}
}
// 利用TrustManagers和已經(jīng)被包裝的KeyManagers創(chuàng)建一個(gè)SSLContext對象.
SSLContext context=SSLContext.getInstance("SSL");
context.init(kms, tms, null);
// 獲得SocketFactory對象.
SSLSocketFactory ssf=context.getSocketFactory();
return ssf;
}
我們可以使用同樣的方法來替換TrustManager對象,這樣我們就可以控制JSSE驗(yàn)證授權(quán)的機(jī)制。具體的實(shí)現(xiàn)就留給讀者朋友去解決了。
小結(jié)
在這篇文章中,我們講述了使用JSSE的一些小技巧。讀完這篇文章后,我相信大家因該知道如何通過編程實(shí)現(xiàn)下面的任務(wù):


· 使用HandshagCompletedListerner對象來獲得關(guān)于連接的信息。


· 從SSLContext對象中獲得一個(gè)SLLSocketFactory對象。


· 使用動態(tài)的TrustStroe或KeyStore。
· 突破在JSSE中KeySotre的密鑰的每個(gè)授權(quán)的密鑰必須相同的限制。


· 通過實(shí)現(xiàn)自己的KeyManager類來指定JSSE使用的授權(quán)。


如果大家有興趣的話,還可以進(jìn)一步將這些技術(shù)進(jìn)行擴(kuò)展。例如你可以在JSSE的其他類中使用X509KeyManager接口,也可以在TrustStore和KeyStore的實(shí)現(xiàn)類中從數(shù)據(jù)庫中讀取授權(quán)信息。但是在使用自己編寫的TrustStore,KeyStore,TrustManager和KeyManager的時(shí)候,需要非常小心,因?yàn)槿魏我粋€(gè)細(xì)微的錯(cuò)誤都可能導(dǎo)致SSL連接不再是安全的了。
作者簡介:馮睿畢業(yè)于美國北伊利諾大學(xué)計(jì)算機(jī)和電氣工程系,獲工程碩士學(xué)位。曾就職于NewMonics公司,進(jìn)行Java虛擬機(jī)部分包的設(shè)計(jì)和開發(fā)和Java底層的性能優(yōu)化工作。目前負(fù)責(zé)一些政府和企業(yè)級GIS系統(tǒng)的設(shè)計(jì)和實(shí)現(xiàn)。


熱詞搜索:

上一篇:安全連接方式SSL
下一篇:SSL代理客戶端使用

分享到:           收藏