SSL——Secure Sockets Layer雙向認(rèn)證(個(gè)人理解):
客戶端認(rèn)證:
客戶端通過瀏覽器訪問某一網(wǎng)站時(shí),如果該網(wǎng)站為HTTPS網(wǎng)站,瀏覽器會(huì)自動(dòng)檢測(cè)系統(tǒng)中是否存在該網(wǎng)站的信任證書,如果沒有信任證書,瀏覽器一般會(huì)拒絕訪問,IE會(huì)有一個(gè)繼續(xù)訪問的鏈接,但地址欄是紅色,給予用戶警示作用,即客戶端驗(yàn)證服務(wù)端并不是強(qiáng)制性的,可以沒有服務(wù)端的信任證書,當(dāng)然是否繼續(xù)訪問完全取決于用戶自己。如何去除地址欄的紅色警告呢?后續(xù)會(huì)介紹導(dǎo)入服務(wù)端證書到瀏覽器的方法。
服務(wù)端認(rèn)證:
服務(wù)端需要獲取到客戶端通過瀏覽器發(fā)送過來的認(rèn)證證書,該證書在服務(wù)端的證書庫中已存在,僅僅是個(gè)匹配過程,匹配成功即通過認(rèn)證,可繼續(xù)訪問網(wǎng)站資源,反之則無法顯示網(wǎng)頁,后續(xù)有截圖。
基本邏輯:
1、生成服務(wù)端密鑰庫并導(dǎo)出證書;
2、生成客戶端密鑰庫并導(dǎo)出證書;
3、根據(jù)服務(wù)端密鑰庫生成客戶端信任的證書;
4、將客戶端證書導(dǎo)入服務(wù)端密鑰庫;
5、將服務(wù)端證書導(dǎo)入瀏覽器。
構(gòu)建演示系統(tǒng)
演示環(huán)境:
JDK:1.6.0_32
Tomcat:apache-tomcat-7.0.27
開發(fā)工具:MyEclipse 10
瀏覽器:Internet Explorer 9
一、生成密鑰庫和證書
可參考以下密鑰生成腳本,根據(jù)實(shí)際情況做必要的修改,其中需要注意的是:服務(wù)端的密鑰庫參數(shù)“CN”必須與服務(wù)端的IP地址相同,否則會(huì)報(bào)錯(cuò),客戶端的任意。
key.script
1、生成服務(wù)器證書庫

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、從客戶端證書庫中導(dǎo)出客戶端證書

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


4、從服務(wù)器證書庫中導(dǎo)出服務(wù)器證書

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


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

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


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

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"的標(biāo)簽,取消注釋,并修改成如下:
<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:指定服務(wù)器密鑰庫,可以配置成絕對(duì)路徑,如“D:/key/server.keystore”,本例中是在Tomcat目錄中創(chuàng)建了一個(gè)名稱為key的文件夾,僅供參考。
keystorePass:密鑰庫生成時(shí)的密碼
truststoreFile:受信任密鑰庫,和密鑰庫相同即可
truststorePass:受信任密鑰庫密碼
三、建立演示項(xiàng)目
項(xiàng)目結(jié)構(gòu)圖:
項(xiàng)目名稱: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("共檢測(cè)到[" + count + "]個(gè)客戶端證書
");

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

} else
{

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

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

public void doPost(HttpServletRequest request, HttpServletResponse response)

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

/** *//**
* <p>
* 校驗(yàn)證書是否過期
* </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
說明:該演示項(xiàng)目強(qiáng)制使用了SSL,即普通的HTTP請(qǐng)求也會(huì)強(qiáng)制重定向?yàn)镠TTPS請(qǐng)求,配置在最下面,可以去除,這樣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>

<!-- 強(qiáng)制SSL配置,即普通的請(qǐng)求也會(huì)重定向?yàn)镾SL請(qǐng)求 -->
<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: 要保證服務(wù)器和客戶端之間傳輸?shù)臄?shù)據(jù)不能夠被修改,且不能被第三方查看到 -->
<!-- INTEGRAL: 要保證服務(wù)器和client之間傳輸?shù)臄?shù)據(jù)不能夠被修改 -->
<!-- NONE: 指示容器必須能夠在任一的連接上提供數(shù)據(jù)。(即用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>四、演示及配置
發(fā)布演示項(xiàng)目,通過瀏覽器訪問:
http://127.0.0.1:8080/SSL或
https://127.0.0.1:8443/SSL,得到相同的結(jié)果,如圖:


得到如上結(jié)果的原始是因?yàn)榭蛻舳藳]有通過服務(wù)端的安全認(rèn)證,接下來將服務(wù)端給客戶端頒發(fā)的證書導(dǎo)入到瀏覽器中:
雙擊“client.p12”

彈出窗口,下一步

默認(rèn),下一步

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

下一步

完成

成功

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

點(diǎn)擊確定后,IE瀏覽器自動(dòng)阻止了繼續(xù)訪問,并給予警告提示,原因是瀏覽器中未導(dǎo)入該網(wǎng)站的可信證書


點(diǎn)擊“繼續(xù)瀏覽此網(wǎng)站”,彈出提示,點(diǎn)擊確定

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

點(diǎn)擊“提交證書”按鈕,返回正確結(jié)果!

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

瀏覽器Internet選項(xiàng)-內(nèi)容-證書

點(diǎn)擊“受信任的根證書頒發(fā)機(jī)構(gòu)”

點(diǎn)擊“導(dǎo)入”

下一步

手動(dòng)選擇“server.cer”,下一步

下一步

完成

點(diǎn)“是”

成功

可以看到我們剛剛導(dǎo)入的根證書

把所有瀏覽器窗口都關(guān)掉,再次訪問網(wǎng)站,發(fā)現(xiàn)鮮紅色已經(jīng)逝去

點(diǎn)擊“提交證書”按鈕,一切正常了,雙向認(rèn)證的DEMO結(jié)束了!

全文完!