Posted on 2012-06-04 17:36
IceWee 閱讀(32923)
評論(22) 編輯 收藏 所屬分類:
Java 、
Tomcat
SSL——Secure Sockets Layer雙向認證(個人理解):
客戶端認證:
客戶端通過瀏覽器訪問某一網站時,如果該網站為HTTPS網站,瀏覽器會自動檢測系統中是否存在該網站的信任證書,如果沒有信任證書,瀏覽器一般會拒絕訪問,IE會有一個繼續訪問的鏈接,但地址欄是紅色,給予用戶警示作用,即客戶端驗證服務端并不是強制性的,可以沒有服務端的信任證書,當然是否繼續訪問完全取決于用戶自己。如何去除地址欄的紅色警告呢?后續會介紹導入服務端證書到瀏覽器的方法。
服務端認證:
服務端需要獲取到客戶端通過瀏覽器發送過來的認證證書,該證書在服務端的證書庫中已存在,僅僅是個匹配過程,匹配成功即通過認證,可繼續訪問網站資源,反之則無法顯示網頁,后續有截圖。
基本邏輯:
1、生成服務端密鑰庫并導出證書;
2、生成客戶端密鑰庫并導出證書;
3、根據服務端密鑰庫生成客戶端信任的證書;
4、將客戶端證書導入服務端密鑰庫;
5、將服務端證書導入瀏覽器。
構建演示系統
演示環境:
JDK:1.6.0_32
Tomcat:apache-tomcat-7.0.27
開發工具:MyEclipse 10
瀏覽器:Internet Explorer 9
一、生成密鑰庫和證書
可參考以下密鑰生成腳本,根據實際情況做必要的修改,其中需要注意的是:服務端的密鑰庫參數“CN”必須與服務端的IP地址相同,否則會報錯,客戶端的任意。
key.script
1、生成服務器證書庫

keytool -validity 365 -genkey -v -alias server -keyalg RSA -keystore E:\ssl\server.keystore -dname "CN=127.0.0.1,OU=icesoft,O=icesoft,L=Haidian,ST=Beijing,c=cn" -storepass 123456 -keypass 123456


2、生成客戶端證書庫

keytool -validity 365 -genkeypair -v -alias client -keyalg RSA -storetype PKCS12 -keystore E:\ssl\client.p12 -dname "CN=client,OU=icesoft,O=icesoft,L=Haidian,ST=Beijing,c=cn" -storepass 123456 -keypass 123456


3、從客戶端證書庫中導出客戶端證書

keytool -export -v -alias client -keystore E:\ssl\client.p12 -storetype PKCS12 -storepass 123456 -rfc -file E:\ssl\client.cer


4、從服務器證書庫中導出服務器證書

keytool -export -v -alias server -keystore E:\ssl\server.keystore -storepass 123456 -rfc -file E:\ssl\server.cer


5、生成客戶端信任證書庫(由服務端證書生成的證書庫)

keytool -import -v -alias server -file E:\ssl\server.cer -keystore E:\ssl\client.truststore -storepass 123456


6、將客戶端證書導入到服務器證書庫(使得服務器信任客戶端證書)

keytool -import -v -alias client -file E:\ssl\client.cer -keystore E:\ssl\server.keystore -storepass 123456


7、查看證書庫中的全部證書

keytool -list -keystore E:\ssl\server.keystore -storepass 123456二、Tomat配置
使用文本編輯器編輯${catalina.base}/conf/server.xml
找到Connector port="8443"的標簽,取消注釋,并修改成如下:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="${catalina.base}/key/server.keystore" keystorePass="123456"
truststoreFile="${catalina.base}/key/server.keystore" truststorePass="123456"/>備注:
keystoreFile:指定服務器密鑰庫,可以配置成絕對路徑,如“D:/key/server.keystore”,本例中是在Tomcat目錄中創建了一個名稱為key的文件夾,僅供參考。
keystorePass:密鑰庫生成時的密碼
truststoreFile:受信任密鑰庫,和密鑰庫相同即可
truststorePass:受信任密鑰庫密碼
三、建立演示項目
項目結構圖:
項目名稱:SSL(隨意)

SSLServlet.java
package com.icesoft.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.security.cert.X509Certificate;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/** *//**
* <p>
* SSL Servlet
* </p>
*
* @author IceWee
* @date 2012-6-4
* @version 1.0
*/

public class SSLServlet extends HttpServlet
{

private static final long serialVersionUID = 1601507150278487538L;
private static final String ATTR_CER = "javax.servlet.request.X509Certificate";
private static final String CONTENT_TYPE = "text/plain;charset=UTF-8";
private static final String DEFAULT_ENCODING = "UTF-8";
private static final String SCHEME_HTTPS = "https";

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException
{
response.setContentType(CONTENT_TYPE);
response.setCharacterEncoding(DEFAULT_ENCODING);
PrintWriter out = response.getWriter();
X509Certificate[] certs = (X509Certificate[]) request.getAttribute(ATTR_CER);

if (certs != null)
{
int count = certs.length;
out.println("共檢測到[" + count + "]個客戶端證書
");

for (int i = 0; i < count; i++)
{
out.println("客戶端證書 [" + (++i) + "]: ");
out.println("校驗結果:" + verifyCertificate(certs[--i]));
out.println("證書詳細:\r" + certs[i].toString());
}

} else
{

if (SCHEME_HTTPS.equalsIgnoreCase(request.getScheme()))
{
out.println("這是一個HTTPS請求,但是沒有可用的客戶端證書
");

} else
{
out.println("這不是一個HTTPS請求,因此無法獲得客戶端證書列表
");
}
}
out.close();
}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException
{
doGet(request, response);
}

/** *//**
* <p>
* 校驗證書是否過期
* </p>
*
* @param certificate
* @return
*/

private boolean verifyCertificate(X509Certificate certificate)
{
boolean valid = true;

try
{
certificate.checkValidity();

} catch (Exception e)
{
e.printStackTrace();
valid = false;
}
return valid;
}

}

web.xml
說明:該演示項目強制使用了SSL,即普通的HTTP請求也會強制重定向為HTTPS請求,配置在最下面,可以去除,這樣HTTP和HTTPS都可以訪問。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>Secure Sockets Layer</display-name>
<servlet>
<servlet-name>SSLServlet</servlet-name>
<servlet-class>com.icesoft.servlet.SSLServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SSLServlet</servlet-name>
<url-pattern>/sslServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<!-- 強制SSL配置,即普通的請求也會重定向為SSL請求 -->
<security-constraint>
<web-resource-collection>
<web-resource-name>SSL</web-resource-name>
<url-pattern>/*</url-pattern><!-- 全站使用SSL -->
</web-resource-collection>
<user-data-constraint>
<description>SSL required</description>
<!-- CONFIDENTIAL: 要保證服務器和客戶端之間傳輸的數據不能夠被修改,且不能被第三方查看到 -->
<!-- INTEGRAL: 要保證服務器和client之間傳輸的數據不能夠被修改 -->
<!-- NONE: 指示容器必須能夠在任一的連接上提供數據。(即用HTTP或HTTPS,由客戶端來決定)-->
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
</web-app>index.jsp

<%
@ page language="java" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>客戶端證書上傳</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<form action="${pageContext.request.contextPath}/sslServlet" method="post">
<input type="submit" value="提交證書"/>
</form>
</body>
</html>四、演示及配置
發布演示項目,通過瀏覽器訪問:
http://127.0.0.1:8080/SSL或
https://127.0.0.1:8443/SSL,得到相同的結果,如圖:


得到如上結果的原始是因為客戶端沒有通過服務端的安全認證,接下來將服務端給客戶端頒發的證書導入到瀏覽器中:
雙擊“client.p12”

彈出窗口,下一步

默認,下一步

輸入生成密鑰時的密碼“123456”,下一步

下一步

完成

成功

再次訪問
http://127.0.0.1:8080/SSL或
https://127.0.0.1:8443/SSL,彈出提示框:

點擊確定后,IE瀏覽器自動阻止了繼續訪問,并給予警告提示,原因是瀏覽器中未導入該網站的可信證書


點擊“繼續瀏覽此網站”,彈出提示,點擊確定

哇!鮮紅的地址欄,夠醒目吧!你訪問的網站不安全那,親!

點擊“提交證書”按鈕,返回正確結果!

可以看出,客戶端并沒有服務端那么嚴格,只要未通過驗證就甭想訪問,下面將服務端生成的信任證書導入到瀏覽器的根證書中,這樣紅色的地址欄就會消失了!
開始導入服務端信任證書,不能雙擊“server.cer”,需要手動導入到受信任的根證書機構中去。

瀏覽器Internet選項-內容-證書

點擊“受信任的根證書頒發機構”

點擊“導入”

下一步

手動選擇“server.cer”,下一步

下一步

完成

點“是”

成功

可以看到我們剛剛導入的根證書

把所有瀏覽器窗口都關掉,再次訪問網站,發現鮮紅色已經逝去

點擊“提交證書”按鈕,一切正常了,雙向認證的DEMO結束了!

全文完!