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

概述:
客戶端,浏覽器或者使用(yòng)http協議(yì)和(hé)服務器通(tōng)信的(de)程序。
如:
客戶端通(tōng)過浏覽器訪問某一網站時(shí),如果該網站爲HTTPS網站,浏覽器會自動檢測系統中是否存在該網站的(de)信任證書(shū),
如果沒有信任證書(shū),浏覽器一般會拒絕訪問,IE會有一個(gè)繼續訪問的(de)鏈接,但地址欄是紅色,給予用(yòng)戶警示作用(yòng),
即客戶端驗證服務端并不是強制性的(de),可(kě)以沒有服務端的(de)信任證書(shū),當然是否繼續訪問完全取決于用(yòng)戶自己。
如果要去除地址欄的(de)紅色警告,需要導入服務端提供的(de)證書(shū)到浏覽器中。

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

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

源碼下(xià)載地址:http://pan.baidu.com/s/1eQ5r9OA


生成密鑰庫和(hé)證書(shū):
因使用(yòng)java環境,下(xià)面使用(yòng)jdk下(xià)面的(de)keytool工具來(lái)生成相應的(de)密鑰庫和(hé)證書(shū)
下(xià)面的(de)命令是在windows 7 下(xià)面測試通(tōng)過的(de),可(kě)以直接複制使用(yòng)
1、創建目錄,如d:/sslDemo

2、使用(yòng)資源管理(lǐ)進入d:/sslDemo,按住shift+右鍵,彈出菜單,選擇"在此處打開命令行".

3、服務器端相關操作
3.1、生成服務器證書(shū)庫
keytool -validity 36500 -genkey -v -alias server -keyalg RSA -keystore server.keystore -dname "CN=www.itjoyee.com,OU=itjoyee.com,O=itjoyee.com,L=Wuhan,ST=HuBei,c=cn" -storepass 123456 -keypass 123456
注: 服務器證書(shū)庫參數“CN”必須與服務端的(de)IP地址相同,否則會報錯,客戶端的(de)任意。

3.2、從服務器證書(shū)庫中導出服務器證書(shū)
keytool -export -v -alias server -keystore server.keystore -storepass 123456 -rfc -file server.cer

3.3、生成客戶端信任證書(shū)庫(由服務端證書(shū)生成的(de)證書(shū)庫,客戶端使用(yòng)此證書(shū)驗證服務端來(lái)源可(kě)靠)
keytool -import -v -alias server -file server.cer -keystore client.truststore -storepass 123456 -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider

注:-storetype BKS 是生成android上面可(kě)以識别的(de)格式,如果不指定jdk默認生成的(de)格式是JKS.
-provider org.bouncycastle.jce.provider.BouncyCastleProvider,需要下(xià)載jar包bcprov-jdk16-1.46.jar放到jdk1.7.0_65\jre\lib\ext\目錄下(xià).
注意需要jdk16,其他(tā)的(de)版本android下(xià)面有版本不匹配的(de)問題.


4、客戶端相關操作
4.1、生成客戶端證書(shū)庫
keytool -validity 36500 -genkeypair -v -alias client -keyalg RSA -storetype PKCS12 -keystore client.p12 -dname "CN=clients.itjoyee.com,OU=jiajianfa,O=jiajianfa,L=Wuhan,ST=HuBei,c=cn" -storepass 123456 -keypass 123456

4.2、從客戶端證書(shū)庫中導出客戶端證書(shū)
keytool -export -v -alias client -keystore client.p12 -storetype PKCS12 -storepass 123456 -rfc -file client.cer

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

4.3、将客戶端證書(shū)導入到服務器證書(shū)庫(使得(de)服務器信任客戶端證書(shū),服務器端用(yòng)此驗證客戶端的(de)合法性)
keytool -import -v -alias client -file client.cer -keystore server.keystore -storepass 123456

4.4、查看服務端證書(shū)中信任的(de)客戶端證書(shū)
keytool -list -keystore server.keystore -storepass 123456

5、服務器端配置
由于使用(yòng)tomcat,下(xià)面使用(yòng)tomcat做(zuò)爲實例配置.
5.1、在tomcat安裝目錄下(xià)新建key目錄,将上面生成的(de)server.keystore複制過去.
5.2、編輯tomcat安裝目錄下(xià)的(de)conf目錄下(xià)的(de)server.xml,如:d:\sslDemo\apache-tomcat-7.0.55\conf\server.xml
找到Connector,修改如下(xià):

1
2
3
4
5
6
7
<Connector port="8444" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150"
SSLEnabled="true" scheme="https" secure="true"
keystoreFile="${catalina.base}/key/server.keystore" keystorePass="123456"
clientAuth="true" sslProtocol="TLS"
truststoreFile="${catalina.base}/key/server.keystore" truststorePass="123456"/>

注:
port配置https訪問的(de)端口
SSLEnabled="true" 開啓https服務
scheme="https"
secure="true" 開啓服務端安全通(tōng)信,客戶端獲取服務器端證書(shū)
keystoreFile="${catalina.base}/key/server.keystore" keystorePass="123456" 服務器證書(shū)庫

clientAuth="true" 開啓驗證客戶端
sslProtocol="TLS" 使用(yòng)的(de)協議(yì)
truststoreFile="${catalina.base}/key/server.keystore" truststorePass="123456" 服務器證書(shū)庫(已導入客戶端證書(shū))

6、測試
由于生成證書(shū)CN配置的(de)是www.itjoyee.com,故需要修改C:\Windows\System32\drivers\etc\hosts
添加

192.168.0.50 www.itjoyee.com
注:
192.168.0.50 爲服務器的(de)ip

啓動tomcat
打開浏覽器
地址欄輸入 http://www.itjoyee.com:8080/
可(kě)以訪問

地址欄輸入https://www.itjoyee.com:8444/
訪問結果,爲無法顯示,因爲,沒有使服務器端生成的(de)信任的(de)客戶端證書(shū)

雙擊client.p12,輸入密碼,在此訪問https://www.itjoyee.com:8444/
此時(shí)會有證書(shū)相關的(de)提示,點擊"确認",接著(zhe)會提示網站安全證書(shū)有問題,點擊繼續訪問,即可(kě)進入正常訪問頁面

7、tomcat下(xià)的(de)服務強制使用(yòng)ssl配置
已ROOT服務爲例,修改D:\sslDemo\apache-tomcat-7.0.55\webapps\ROOT\WEB-INF\web.xml
添加

1
2
3
4
5
6
7
8
9
<security-constraint>
<web-resource-collection>
<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>

打開浏覽器
地址欄輸入 http://www.itjoyee.com:8080/會有證書(shū)相關提示


爲了(le)方便測試android下(xià)雙向認證可(kě)以用(yòng),生成證書(shū)的(de)時(shí)候把域名換成服務器的(de)ip地址,驗證才可(kě)以通(tōng)過

1、android app 代碼實現

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... params) {
try {
HttpClient httpsClient = AppSslApplication.getHttpsClient(MainActivity.this.getBaseContext());
HttpGet httpget = new HttpGet(HTTPS_URL);
HttpResponse response = httpsClient.execute(httpget);
HttpEntity entity = response.getEntity();
Log.e("Response status", response.getStatusLine().toString());
if (entity != null) {
Log.e("Response", "Response content length: " + entity.getContentLength());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String text;
while ((text = bufferedReader.readLine()) != null) {
Log.e("Response status", text);
}
bufferedReader.close();
}
httpsClient.getConnectionManager().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.truststore";
private static final String KEY_STORE_PASSWORD = "123456";
private static final String KEY_STORE_TRUST_PASSWORD = "123456";
private static KeyStore keyStore;
private static KeyStore trustStore;
public static HttpClient getSslHttpClient(Context pContext) {
HttpClient httpsClient = new DefaultHttpClient();
try {
// 服務器端需要驗證的(de)客戶端證書(shū)
keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
// 客戶端信任的(de)服務器端證書(shū)
trustStore = KeyStore.getInstance(KEY_STORE_TYPE_BKS);
InputStream ksIn = pContext.getResources().getAssets().open(KEY_STORE_CLIENT_PATH);
InputStream tsIn = pContext.getResources().getAssets().open(KEY_STORE_TRUST_PATH);
try {
keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());
trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ksIn.close();
} catch (Exception ignore) {
}
try {
tsIn.close();
} catch (Exception ignore) {
}
}
SSLSocketFactory socketFactory = 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 (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return httpsClient;
}
}

2、android浏覽器實現
1.先把你的(de)CA證書(shū)拷貝到你的(de)SD卡裏面
2.進入手機的(de)"設置"->"位置和(hé)安全",最下(xià)面有個(gè)"從SD卡安裝",就是安裝證書(shū)的(de)。
---------暫時(shí)沒有實現


http://my.oschina.net/jjface/blog/339144

Copyright©2012-2024 All Rights Reserved. 技術支持:23456175@QQ.com