SSL(Secure Sockets Layer 安全套接層)就是一種協議(規范),用於保障客戶端和服務器端通信的安全,以免通信時傳輸的信息被竊取或者修改。
客戶端和服務器端在進行握手(客戶端和服務器建立連接和交換參數的過程稱之為握手)時會產生一個“對話密鑰”(session key),用來加密接下來的數據傳輸,解密時也是用的這個“對話密鑰”,而這個“對話密鑰”只有客戶端和服務器端知道。也就是說只要這個“對話密鑰”不被破解,就能保證安全。
2. 客戶端證書和服務器端證書
客戶端證書和服務器端證書用於證明自己的身份,就好比每個人都有一張身份證,這種身份證是唯一的。一般來說,只要有服務器端的證書就可以了,但是有時需要客戶端提供自己的證書,已證明其身份。
一般證書可以使用權威機構頒發的證書,如:veri sign,百度使用的就是veri sign頒發的證書,這樣的權威證書機構是受信任的,但是這些機構頒發的證書往往是需要收費的,這樣的證書也難得到。對於小型企業來說為了節約成本,常常使用自簽名的證書。
接下來使用JDK keytool工具來簽發證書,如果未安裝JDK,請先安裝JDK(本文使用的是JDK7)。本文所有的證書文件都放到F:\ca,您可以選擇一個目錄來存放。
keytool -genkeypair -v -alias server -keyalg RSA -validity 3650 -keystore ./server.keystore -storepass 123456 -keypass 123456 -dname "CN=127.0.0.1,OU=rm,O=rm,L=gz,ST=gd,C=cn"
注意:-dname參數中的CN應該為服務器所在的ip或者域名
2. 導出服務器端證書
keytool -exportcert -alias server -keystore ./server.keystore -file ./server.cer -storepass 123456
3. 將服務器端證書導入信任證書
keytool -importcert -alias serverca -keystore ./server_trust.keystore -file ./server.cer -storepass 123456
keytool -genkeypair -v -alias client -dname "CN=rorymo" -keyalg RSA -validity 3650 -keypass 123456 -keystore ./client.p12 -storepass 123456 -storetype PKCS12
2. 導出客戶端證書
keytool -exportcert -alias client -file ./client.cer -keystore ./client.p12 -storepass 123456 -storetype PKCS12
3. 導入客戶端證書到服務器端信任證書庫
keytool -importcert -alias clientca -keystore ./server_trust.keystore -file ./client.cer -storepass 123456
4. 查看服務器端信任證書庫的信任證書信息
keytool -list -keystore ./server_trust.keystore -storepass 123456
可以看到信任證書庫中已經導入了一張服務器端證書和一張客戶端證書
5. 至此我們已經生成了如下的文件
將上圖的server.keystore和server_trust.keystore放到tomcat的根目錄下,例如我的tomcat目錄為:F:\ca\apache-tomcat-7.0.64
2. 配置tomcat
編輯conf/server.xml文件加入如下的配置:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" keystoreFile="${catalina.base}/server.keystore" keystorePass="123456" truststoreFile ="${catalina.base}/server_trust.keystore" truststorePass="123456"/>
說明:
3. 編寫用來獲取客戶端證書的servlet
1 package com.rorymo.demo.ssl; 2 3 import java.io.IOException; 4 import java.io.PrintWriter; 5 import java.security.cert.X509Certificate; 6 7 import javax.servlet.ServletException; 8 import javax.servlet.annotation.WebServlet; 9 import javax.servlet.http.HttpServlet; 10 import javax.servlet.http.HttpServletRequest; 11 import javax.servlet.http.HttpServletResponse; 12 13 /** 14 * 15 * SSLServlet 16 * 17 * @author rorymo 18 * @version 1.0 19 */ 20 @WebServlet("/SSLServlet") 21 public class SSLServlet extends HttpServlet { 22 23 private static final long serialVersionUID = 1601507150278487538L; 24 private static final String REQUEST_ATTR_CERT = "javax.servlet.request.X509Certificate"; 25 private static final String CONTENT_TYPE = "text/plain;charset=UTF-8"; 26 private static final String DEFAULT_ENCODING = "UTF-8"; 27 private static final String SCHEME_HTTPS = "https"; 28 29 public void doGet(HttpServletRequest request, HttpServletResponse response) 30 throws ServletException, IOException { 31 response.setContentType(CONTENT_TYPE); 32 response.setCharacterEncoding(DEFAULT_ENCODING); 33 PrintWriter out = response.getWriter(); 34 X509Certificate[] certs = (X509Certificate[]) request.getAttribute(REQUEST_ATTR_CERT); 35 if (certs != null) { 36 int count = certs.length; 37 out.println("共檢測到[" + count + "]個客戶端證書"); 38 for (int i = 0; i < count; i++) { 39 X509Certificate cert = certs[i]; 40 out.println("客戶端證書 [" + cert.getSubjectDN() + "]: "); 41 out.println("證書是否有效:" + (verifyCertificate(cert) ? "是" : "否")); 42 out.println("證書詳細信息:\r" + cert.toString()); 43 } 44 } else { 45 if (SCHEME_HTTPS.equalsIgnoreCase(request.getScheme())) { 46 out.println("這是一個HTTPS請求,但是沒有可用的客戶端證書"); 47 } else { 48 out.println("這不是一個HTTPS請求,因此無法獲得客戶端證書列表 "); 49 } 50 } 51 out.close(); 52 } 53 54 public void doPost(HttpServletRequest request, HttpServletResponse response) 55 throws ServletException, IOException { 56 doGet(request, response); 57 } 58 59 /** 60 * 61 * 校驗證書是否過期 62 * 63 * 64 * @param certificate 65 * @return 66 */ 67 private boolean verifyCertificate(X509Certificate certificate) { 68 boolean valid = true; 69 try { 70 certificate.checkValidity(); 71 } catch (Exception e) { 72 e.printStackTrace(); 73 valid = false; 74 } 75 return valid; 76 } 77 78 }
4. 在web應用的web.xml中加入如下配置
<security-constraint> <web-resource-collection> <web-resource-name>SSL</web-resource-name> <url-pattern>/SSLServlet</url-pattern> </web-resource-collection> <user-data-constraint> <description>SSL required</description> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint>
說明:
由於我們這裡是雙向認證,所以也需要客戶端的證書,我們接下來導入客戶端證書:
2. 重新訪問上面的地址:http://127.0.0.1:8080/SSL/SSLServlet
浏覽器提示選擇客戶端證書進行認證,我們點擊【確定】,打開了一個警告頁面,提示我們服務端證書不受信任
而且地址欄的圖標有個小紅叉:
當然我們直接點擊是能打開的
為了不出現這樣的警告信息,我們可以導入服務器端證書到客戶端
a. 導入服務器端證書到客戶端
b. 關閉浏覽器,我們再訪問
2. 本文源碼下載
【完】
感謝您的閱讀