基于java的(de)https雙向認證,android上(s''‌®hàng)亦可(kě)用(yòng)發布日(rì)期:2014-11-09   來(lái)源:網絡
  

概述:
客戶端,浏覽器(qì)或者使用(yòng)http協議εγ(yì)和(hé)服務器(qì)通(tōng)信的(de∏σ)程序。
如(rú):
客戶端通(tōng)過浏覽器(qì)訪問(wèn)某一(yλ♠₩±ī)網站(zhàn)時(shí),如(rú)果該網站δ♦α(zhàn)為(wèi)HTTPS網站(zhàn),浏覽器(qì)會(hu&☆®©ì)自(zì)動檢測系統中是(shì)否存在該網站(zhàn)≈→β的(de)信任證書(shū),
如(rú)果沒有(yǒu)信任證書(shū),浏覽器∞δ☆‍(qì)一(yī)般會(huì)拒絕訪問(↑₽ wèn),IE會(huì)有(yǒu)一(yī)個(gè)繼ε♠↔↓續訪問(wèn)的(de)鏈接,但(dàn)地(dì)址欄是(shì)紅(hó∏♥φng)色,給予用(yòng)戶警示作(zuò)用(yòng)δπ,
即客戶端驗證服務端并不(bù)是(shì)強± 制(zhì)性的(de),可(kě)以沒有(yǒu)服務端的(de)信任證書‌÷™φ(shū),當然是(shì)否繼續訪問(wèn)完全取決于用(yò‌↔↑ng)戶自(zì)己。
如(rú)果要(yào)去(qù)除地(dì)址欄的(de÷♠€§)紅(hóng)色警告,需要(yào)導入服務端提供的(de)證書(shū≥©§→)到(dào)浏覽器(qì)中。

服務器(qì)端,使用(yòng)http協議(y♦®♥ì)提供服務的(de)程序。
服務端需要(yào)獲取到(dào)客戶端通(tōn•‌g)過浏覽器(qì)發送過來(lái)的(de)認證證書(shū),
如(rú):
該證書(shū)在服務端的(de)證書(shū)庫中σ♦∏ 已存在,僅僅是(shì)個(gè)匹配過程,匹配成功即通(tōng)過認證,ε"可(kě)繼續訪問(wèn)網站(zhàn)資源,反之則無Ω♥法顯示網頁。

基本邏輯:
1、生(shēng)成服務端密鑰庫并導出證書(shū).
2、生(shēng)成客戶端密鑰庫并導出證書(shū).
3、根據服務端密鑰庫生(shēng)成客戶端信任的(de)證書(shū).↕♦σ
4、将客戶端證書(shū)導入服務端密鑰庫.
5、将服務端證書(shū)導入浏覽器(qì).

源碼下(xià)載地(dì)址:http://pan.baidu.com/s/1eQ$Ω‍₹5r9OA


生(shēng)成密鑰庫和(hé)證書(shū):$"
因使用(yòng)java環境,下(xià)面使用(yòng)jdk下Ω ‌β(xià)面的(de)keytool工(gōngα ♦<)具來(lái)生(shēng)成相(xiàn≠×®g)應的(de)密鑰庫和(hé)證書(s&¥hū)
下(xià)面的(de)命令是(shì)在windo &★εws 7 下(xià)面測試通(tōng)過←♣↑的(de),可(kě)以直接複制(zhì)ε♦₽₹使用(yòng)
1、創建目錄,如(rú)d:/sslDemo

2、使用(yòng)資源管理(lǐ)進入d:/sslDemo,按<€↔§住shift+右鍵,彈出菜單,選擇"在此處♠∏​α打開(kāi)命令行(xíng)".

3、服務器(qì)端相(xiàng)關操作(zuò)
3.1、生(shēng)成服務器(qì)證書(shū)庫♣♦‍
keytool -validity 36500 -genkey -v -ε©↓alias server -keyalg'←β RSA -keystore server.keystor→>₽γe -dname "CN=www.itjo∑∑yee.com,OU=itjoyee.com,O=itjoyee.com,♥¶<L=Wuhan,ST=HuBei,c=cn"$₩; -storepass 123456 -keypass >₽123456
注: 服務器(qì)證書(shū)庫參數(shù)“CN”φ✔必須與服務端的(de)IP地(dì)址相(xiàng)‍"同,否則會(huì)報(bào)錯(cuò),客戶端的(de)任意。≠✔

3.2、從(cóng)服務器(qì)證書(shū)庫中導出服務器(★♣©qì)證書(shū)
keytool -export -v -alias s₽ ≠erver -keystore server.keystore $ε-storepass 123456 -rf®∑₽‍c -file server.cer

3.3、生(shēng)成客戶端信任證書(shū)庫(由服務端✘÷證書(shū)生(shēng)成的(de)證書(shū)庫,客戶端使用(yò‍πng)此證書(shū)驗證服務端來(lái)源可(kě)靠)
keytool -import -v -alias server -fil§↕←₽e server.cer -keystore clienσ↔♦t.truststore -storepass 123456 -s↑ toretype BKS -provider org.bouncycas≤ tle.jce.provider.BouncyCast≤'←leProvider

注:-storetype BKS 是(sh"↑₹ì)生(shēng)成android上(sh→₽àng)面可(kě)以識别的(de)格式,如(rú)果不(bù☆↑•↑)指定jdk默認生(shēng)成的(de)格式是δδ‌σ(shì)JKS.
-provider org.bouncycastle.jce.pr↓✘δovider.BouncyCastlePr ₹←ovider,需要(yào)下(xià)載jar包bcpro≤σ£v-jdk16-1.46.jar放(fàng)到(dào)jdk1.7∑φ'.0_65\jre\lib\ext\目錄下(xià).
注意需要(yào)jdk16,其他(tā)✘₽的(de)版本android下(xià)面有(yǒu™★ε)版本不(bù)匹配的(de)問(wèn)題.


4、客戶端相(xiàng)關操作(zuò)
4.1、生(shēng)成客戶端證書(shū)庫
keytool -validity 36500 -genkeypai₽♦≈¥r -v -alias client -k  eyalg RSA -storetype PKCS1©↔σ2 -keystore client.p12 -dname "C$•×δN=clients.itjoyee.com,OU=jiajia€δ≠nfa,O=jiajianfa,L=Wuhan,ST=H‍$→uBei,c=cn" -storepas πs 123456 -keypass 123456

4.2、從(cóng)客戶端證書(shū)庫中導出客戶端證書(shū)
keytool -export -v -alias client -k γ✔←eystore client.p12 -storetype PKCS12≤$ -storepass 123456 -rfc -file cli ☆×§ent.cer

注:客戶端證書(shū)可(kě)以産生(shēng)多(du£'ō)個(gè).

4.3、将客戶端證書(shū)導入到(d←★★←ào)服務器(qì)證書(shū)庫(使✘✔得(de)服務器(qì)信任客戶端證書(shū),服務器(qì)端用(yòng ₹♥β)此驗證客戶端的(de)合法性)
keytool -import -v -alias client -file $ •δclient.cer -keystore server.keysto₹¶re -storepass 123456

4.4、查看(kàn)服務端證書(shū)中信任的(d↓≈✔e)客戶端證書(shū)
keytool -list -keystore server.keystor§♣e -storepass 123456

5、服務器(qì)端配置
由于使用(yòng)tomcat,下(xià)面使用(yòng)tomcat做₽λπ(zuò)為(wèi)實例配置.
5.1、在tomcat安裝目錄下(xià)新建key目錄,将上(sh•§àng)面生(shēng)成的(de)server.keystore→δ複制(zhì)過去(qù).
5.2、編輯tomcat安裝目錄下(xià)的(de)conf目錄下(x✔✔ià)的(de)server.xml,如(rú):d:\ss✘π​lDemo\apache-tomcat-7.0."↕÷55\conf\server.xml
找到(dào)Connector,修改如(rú)下(xià): ≈€✘

1
2
3
4
5
6
7
<Connector port="8444" protocol="org.apache.coyote.h•πttp11.Http11NioProtoc§∑¥ol"
maxThreads="150"
SSLEnabled="true" scheme="https" secure="true"
keystoreFile="${catalina.base}/key/∏¥λ∞server.keystore" keystorePass="123456"
clientAuth="true" sslProtocol="TLS"
truststoreFile="${catalina.base}/key/serv→≠÷er.keystore" truststorePass="123456"/>

注:
port配置https訪問(wèn)的(de)端口
SSLEnabled="true" 開(kāi)啓ht♣$tps服務
scheme="https"λλπ$;
secure="true" ‍♠✔± 開(kāi)啓服務端安全通(tōng)信≠σ£↑,客戶端獲取服務器(qì)端證書(shū)
keystoreFile="${catalina.base}/k♣ ‍ey/server.keystore" keystorePaδ₽ss="123456" 服務器(qì)證書(s✘ε "hū)庫

clientAuth="true" 開(kāi)啓驗ε•δ證客戶端
sslProtocol="TLS" 使用 →¥(yòng)的(de)協議(yì)
truststoreFile="${catalina.b'♣€ase}/key/server.keystγ&∏ore" truststorePass="'™;123456" 服務器(qì)證書(shū)庫(已導入客戶端‌"證書(shū))

6、測試
由于生(shēng)成證書(shū)CN配置↔ ←的(de)是(shì)www.itjoyee.com,故需←✘∏要(yào)修改C:\Windows\S±γ©ystem32\drivers\etc\hosts
添加

192.168.0.50 www.β↑€itjoyee.com
注:
192.168.0.50 為(wèi)服務®↑÷‌器(qì)的(de)ip

啓動tomcat
打開(kāi)浏覽器(qì)
地(dì)址欄輸入 http://www.i‍σφtjoyee.com:8080/
可(kě)以訪問(wèn)

地(dì)址欄輸入https://www.itjoyee.coφ±∏φm:8444/
訪問(wèn)結果,為(wèi)無法顯示,因為(wè✘↓i),沒有(yǒu)使服務器(qì)端生(shēn₩∑πg)成的(de)信任的(de)客戶端證書™Ω£(shū)

雙擊client.p12,輸入密碼,在此訪問≥Ω§(wèn)https://www.itjoyee.π©δcom:8444/
此時(shí)會(huì)有(yǒu)證書(shū)相(xiàng↓≠)關的(de)提示,點擊"确認",接著(zhe)會(hu §εì)提示網站(zhàn)安全證書(shū)有(yǒu)問(wèn↓ε↓)題,點擊繼續訪問(wèn),即可(kě)進∞‍₹入正常訪問(wèn)頁面

7、tomcat下(xià)的(de)服務強制(zhì)使用αΩ (yòng)ssl配置
已ROOT服務為(wèi)例,修改D:\sslDemo\apache€♣ π-tomcat-7.0.55\webapps\ROOT\WEB-INF\wπ ★eb.xml
添加

1
2
3
4
5
6
7
8
9
<security-constraint>
<web-resource-collect ÷ion>
<web-resource-name >SSL</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>

打開(kāi)浏覽器(qì)
地(dì)址欄輸入 http://www.itjoyee.com:8080←λ®/會(huì)有(yǒu)證書(shū)相✘€(xiàng)關提示


為(wèi)了(le)方便測試android下(xià)雙向認證可(kε ↓€ě)以用(yòng),生(shēng)成證書∏$(shū)的(de)時(shí)候把域名換成€‍服務器(qì)的(de)ip地(dì)址♥★₹,驗證才可(kě)以通(tōng)過

1、android app 代碼實現(xiàn↕↕✘)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
AsyncTask testTask = new AsyncTask() {
@Override
protected Object doInBackground(Object... p®®arams) {
try {
HttpClient httpsClien÷♠"¶t = AppSslApplication.getβ"¥HttpsClient(MainActivity.this.getBaseContext());
HttpGet httpget = new HttpGet(HTTPS_URL);
HttpResponse response = httpsα↓Client.execute(httpget);
HttpEntity entity = response.getEntiφ​€ty();
Log.e("Response status©<", response.getStatusLine↑'π().toString());
if (entity != null) {
Log.e("Response", "Response content len•®→₽gth: " + entity.getContentLength()♠σ);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getCont​✘φ↔ent()));
String text;
while ((text = bufferedReader.readLine(☆≈≤ )) != null) {
Log.e("Response status", text);
}
bufferedReader.close();
}
httpsClient.getConnectionManage©±r().shutdown();
} catch (ClientProtocolException e)®λδ✔ {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
testTask.execute();
public class HttpClientSslHelper {
private static final String KEY_STORE_TYPE_BKS = "bks";
private static final String KEY_STORE_TYPE_β÷↑P12 = "PKCS12";
private static final String SCHEME_HTTPS = "https";
private static final int HTTPS_PORT = 8444;
private static final String KEY_STORE_CLIENT_PATH ™ = "client.p12";
private static final String KEY_STORE_TRUST_PATH✘​↕ = "client.truststor$©Ωe";
private static final String KEY_STORE_PASSWORD =∑λ "123456";
private static final String KEY_STORE_TRUST_PASΩ ♣÷SWORD = "123456";
private static KeyStore keyStore;
private static KeyStore trustStore;
public static HttpClient getSslHttpClient(Context pCo‍εntext) {
HttpClient httpsClient = new DefaultHttpClient();&↕×★
try {
// 服務器(qì)端需要(yào)驗證的(de)客¥♥戶端證書(shū)
keyStore = KeyStore.getI→$‍nstance(KEY_STORE_TYPE_£≠☆P12);
// 客戶端信任的(de)服務器(qì)端證書(shū)
trustStore = KeyStor✘↔$​e.getInstance(KEY_STORE_TYPE_♥ '÷BKS);
InputStream ksIn = pContext.getResourc™₩es().getAssets().open(KEY±↕_STORE_CLIENT_PATH);
InputStream tsIn = pContext.getResou↕♥≠rces().getAssets().open(KEY_STORE≤≥♠_TRUST_PATH);
try {
keyStore.load(ksIn, KEY_STORE_PASSWORDπ♦♥.toCharArray());
trustStore.load(tsIn, KE♦‍Y_STORE_TRUST_PASSWORD.€Ω×toCharArray());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ksIn.close();
} catch (Exception ignore) {
}
try {
tsIn.close();
} catch (Exception ignore) {
}
}
SSLSocketFactory socketF÷​φactory = new SSLSocketFactory(keyStore, KEYφ♠_STORE_PASSWORD, trustStore);
Scheme sch = new Scheme(SCHEME_HTTPS, socketFactory,♣λ£ HTTPS_PORT);
httpsClient.getConnectionManager(™Ω).getSchemeRegistry().register(sch);§¥
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) δ ‌{
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();∑←©
} catch (NoSuchAlgorithmExce→×∑ption e) {
e.printStackTrace();
} catch (ClientProtocolException e) ₩↔₽ {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return httpsClient;
}
}

2、android浏覽器(qì)實現(xiànΩ​)
1.先把你(nǐ)的(de)CA證書(shū)拷貝到(dào)你(nǐ)的‌≈★(de)SD卡裡(lǐ)面
2.進入手機(jī)的(de)"設置"->&δ α‌quot;位置和(hé)安全",最下(xià)面有(α♦​yǒu)個(gè)"從(cóng)SD卡安裝"φ™↕₩;,就(jiù)是(shì)安裝證書(shū)的(de)。
---------暫時(shí)沒有(yǒu)實現(xià¶≥n)


http://my.oschina.net/jjface/blog/33914‍'≤'4