SSL——Secure Sockets Layer
雙向認證(我的理解):
客戶端認證:
客戶端經過瀏覽器訪問某一網站時,若是該網站爲HTTPS網站,瀏覽器會自動檢測系統中是否存在該網站的信任證書,若是沒有信任證書,瀏覽器通常會拒絕訪問,IE會有一個繼續訪問的連接,但地址欄是紅色,給予用戶警示做用,即客戶端驗證服務端並非強制性的,能夠沒有服務端的信任證書,固然是否繼續訪問徹底取決於用戶本身。如何去除地址欄的紅色警告呢?後續會介紹導入服務端證書到瀏覽器的方法。
服務端認證:
服務端須要獲取到客戶端經過瀏覽器發送過來的認證證書,該證書在服務端的證書庫中已存在,僅僅是個匹配過程,匹配成功即經過認證,可繼續訪問網站資源,反之則沒法顯示網頁,後續有截圖。
基本邏輯:
一、生成服務端密鑰庫並導出證書;
二、生成客戶端密鑰庫並導出證書;
三、根據服務端密鑰庫生成客戶端信任的證書;
四、將客戶端證書導入服務端密鑰庫;
五、將服務端證書導入瀏覽器。
構建演示系統
演示環境:
JDK:1.6.0_32
Tomcat:apache-tomcat-7.0.27
開發工具:MyEclipse 10
瀏覽器:Internet Explorer 9
1、生成密鑰庫和證書
可參考如下密鑰生成腳本,根據實際狀況作必要的修改,其中須要注意的是:服務端的密鑰庫參數「CN」必須與服務端的IP地址相同,不然會報錯,客戶端的任意。
key.script
html
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 123456java
2、Tomat配置
使用文本編輯器編輯${catalina.base}/conf/server.xml
找到Connector port="8443"的標籤,取消註釋,並修改爲以下:
web
<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"/>apache
備註:
keystoreFile:指定服務器密鑰庫,能夠配置成絕對路徑,如「D:/key/server.keystore」,本例中是在Tomcat目錄中建立了一個名稱爲key的文件夾,僅供參考。
keystorePass:密鑰庫生成時的密碼
truststoreFile:受信任密鑰庫,和密鑰庫相同便可
truststorePass:受信任密鑰庫密碼
3、創建演示項目
項目結構圖:
項目名稱: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;
}
}
tomcat
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>app
index.jsp
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>編輯器