http://blog.csdn.net/fw0124/article/details/41013333html
關於SSL/TLS/JSSE的介紹:
1)SSL/TLS協議運行機制
2)圖解SSL/TLS協議
3)使用wireshark觀察SSL/TLS握手過程
4)SSL/TLS的Java實現--JSSEjava
(一)使用keytool建立密鑰庫
使用雙向認證的SSL/TLS協議通訊,客戶端和服務器端都要設置用於證明本身身份的安全證書,而且還要設置信任對方的哪些安全證書。
理論上一共須要準備四個文件,兩個keystore文件和兩個truststore文件。
通訊雙方分別擁有一個keystore和一個truststore,keystore用於存放本身的密鑰和公鑰,truststore用於存放全部須要信任方的公鑰。
首先使用JDK自帶的keytool工具來生成keystore和truststore。這裏使用的Java版本是1.7。
1)建立server的keystore文件,生成server的公鑰/私鑰密鑰對。須要指定keystore的密碼(storepass)和密鑰對的密碼(keypass)。
訪問keystore須要storepass。訪問密鑰對須要keypass。node
- <span style="font-family:Verdana;">C:\Program Files\Java\jre7\bin>keytool -genkey -alias catserver -keyalg rsa -keysize 1024 -sigalg sha256withrsa -keypass catserver -keystore c:\_tmp\catserver.keystore -storepass catserverks
- What is your first and last name?
- [Unknown]: cat
- What is the name of your organizational unit?
- [Unknown]: cat
- What is the name of your organization?
- [Unknown]: cat
- What is the name of your City or Locality?
- [Unknown]: cat
- What is the name of your State or Province?
- [Unknown]: cat
- What is the two-letter country code for this unit?
- [Unknown]: ct
- Is CN=cat, OU=cat, O=cat, L=cat, ST=cat, C=ct correct?
- [no]: y</span>
2)建立client的keystore文件。一樣須要指定keystore的密碼和密鑰對的密碼。linux
- <span style="font-family:Verdana;">C:\Program Files\Java\jre7\bin>keytool -genkey -alias foxclient -keyalg dsa -keysize 512 -sigalg sha1withdsa -keypass foxclient -keystore c:\_tmp\foxclient.keystore -storepass foxclientks
- What is your first and last name?
- [Unknown]: fox
- What is the name of your organizational unit?
- [Unknown]: fox
- What is the name of your organization?
- [Unknown]: fox
- What is the name of your City or Locality?
- [Unknown]: fox
- What is the name of your State or Province?
- [Unknown]: fox
- What is the two-letter country code for this unit?
- [Unknown]: fx
- Is CN=fox, OU=fox, O=fox, L=fox, ST=fox, C=fx correct?
- [no]: y</span>
3)從server的keystore中導出server的證書(其中包括server的公鑰)。算法
- <span style="font-family:Verdana;">C:\Program Files\Java\jre7\bin>keytool -export -alias catserver -keystore c:\_tmp\catserver.keystore -storepass catserverks -file c:\_tmp\catserver.cer
- Certificate stored in file <c:\_tmp\catserver.cer>
- </span>
4)從client的keystore中導出client的證書(其中包括client的公鑰)。編程
- <span style="font-family:Verdana;">C:\Program Files\Java\jre7\bin>keytool -export -alias foxclient -keystore c:\_tmp\foxclient.keystore -storepass foxclientks -file c:\_tmp\foxclient.cer
- Certificate stored in file <c:\_tmp\foxclient.cer>
- </span>
5)建立server的truststore文件並導入client的證書(其中包括client的公鑰)。安全
- <span style="font-family:Verdana;">C:\Program Files\Java\jre7\bin>keytool -import -alias foxclient -keystore c:\_tmp\catservertrust.keystore -storepass catservertrustks -file c:\_tmp\foxclient.cer
- Owner: CN=fox, OU=fox, O=fox, L=fox, ST=fox, C=fx
- Issuer: CN=fox, OU=fox, O=fox, L=fox, ST=fox, C=fx
- Serial number: 6eaf996f
- Valid from: Wed Nov 05 16:15:41 CST 2014 until: Tue Feb 03 16:15:41 CST 2015
- Certificate fingerprints:
- MD5: B5:B6:92:66:84:92:A0:C2:F5:40:39:25:F8:66:2A:17
- SHA1: 07:42:A3:1A:49:7B:C9:34:4B:6B:FA:37:6C:20:98:D4:20:13:7C:91
- SHA256: 37:A5:00:A3:13:00:DE:99:3B:08:47:F6:1E:8A:05:F1:4A:B2:C6:22:20:
- E1:AF:0E:05:B2:CE:E0:2F:94:B6:94
- Signature algorithm name: SHA1withDSA
- Version: 3
-
- Extensions:
-
- #1: ObjectId: 2.5.29.14 Criticality=false
- SubjectKeyIdentifier [
- KeyIdentifier [
- 0000: 69 3E 6A D0 B5 B1 1F BD 48 46 E1 A4 6C 1F 71 90 i>j.....HF..l.q.
- 0010: 29 06 3B 32 ).;2
- ]
- ]
-
- Trust this certificate? [no]: y
- Certificate was added to keystore</span>
6)建立client的truststore文件並導入server的證書(其中包括server的公鑰)。bash
- <span style="font-family:Verdana;">C:\Program Files\Java\jre7\bin>keytool -import -alias catserver -keystore c:\_tmp\foxclienttrust.keystore -storepass foxclienttrustks -file c:\_tmp\catserver.cer
- Owner: CN=cat, OU=cat, O=cat, L=cat, ST=cat, C=ct
- Issuer: CN=cat, OU=cat, O=cat, L=cat, ST=cat, C=ct
- Serial number: 3e421457
- Valid from: Wed Nov 05 16:13:52 CST 2014 until: Tue Feb 03 16:13:52 CST 2015
- Certificate fingerprints:
- MD5: 20:44:7C:E5:30:E6:7A:21:C2:49:64:77:E1:3A:A0:77
- SHA1: 8B:02:D2:BE:98:2F:99:94:08:47:E2:96:EC:05:1B:5D:B1:8F:30:2F
- SHA256: A6:66:85:F4:C2:B2:06:4E:2E:40:D8:52:84:6E:85:2B:5B:BB:C3:B0:9C:
- 31:92:99:F5:91:5D:83:67:C8:4D:D8
- Signature algorithm name: SHA256withRSA
- Version: 3
-
- Extensions:
-
- #1: ObjectId: 2.5.29.14 Criticality=false
- SubjectKeyIdentifier [
- KeyIdentifier [
- 0000: F5 91 E6 14 EE EF 5F 24 4F AC 6F A6 B8 36 A6 11 ......_$O.o..6..
- 0010: 2B 5C DF 04 +\..
- ]
- ]
-
- Trust this certificate? [no]: y
- Certificate was added to keystore</span>
keysize
若是加密算法是rsa,key size範圍512->16384 bits,而且必須是64的倍數。
若是加密算法是dsa,key size範圍512->1024 bits,而且必須是64的倍數。
sigalg
若是加密算法是rsa,簽名算法能夠是md5withrsa/sha1withrsa/sha256withrsa/sha384withrsa/sha512withrsa
若是加密算法是dsa,簽名算法能夠是sha1withdsa
若是熟悉Java的socket編程,就會發現使用JSSE進行SSL/TLS編程其實和它很類似。
區別在於ServerSocket對象換成了SSLServerSocket,Socket對象換成了SSLSocket對象。服務器
(二)server端處理流程和代碼
處理流程:
1)加載server的keystore文件,須要指定keystore的密碼(storepass)。
KeyStore類型有以下三種:
jceks - The proprietary keystore implementation provided by the SunJCE provider.
jks - The proprietary keystore implementation provided by the SUN provider.
pkcs12 - The transfer syntax for personal identity information as defined in PKCS #12.
2)加載server的truststore文件,須要指定truststore的密碼(storepass)。
3) 建立KeyManagerFactory對象並用1)中加載的keystore和server密鑰對的密碼(keypass)來初始化。
4) 建立TrustManagerFactory對象並用2)中加載的truststore來初始化。truststore中存的是client的公鑰,不須要keypass也能夠訪問。
5)建立SSLContext並用3)和4)中建立的KeyManagerFactory和TrustManagerFactory對象來初始化。
http://docs.Oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
建立SSLContext是須要給出SSLContext Algorithms。上面這個連接中給出了合法的SSLContext Algorithms,有以下可用值。
SSL - Supports some version of SSL; may support other versions
SSLv2 - Supports SSL version 2 or later; may support other versions
SSLv3 - Supports SSL version 3; may support other versions
TLS - Supports some version of TLS; may support other versions
TLSv1 - Supports RFC 2246: TLS version 1.0 ; may support other versions
TLSv1.1 - Supports RFC 4346: TLS version 1.1 ; may support other versions
TLSv1.2 - Supports RFC 5246: TLS version 1.2 ; may support other versions
6)建立SSLServerSocketFactory,在指定的端口上建立SSLServerSocket並設定須要客戶端證書:setNeedClientAuth(true)
7)在SSLServerSocket對象上調用accept()方法等待客戶端的鏈接。
客戶端連上來以後這個函數會返回一個SSLSocket對象,在這個對象的輸入輸出流上進行讀寫。
在這個SSLSocket對象上能夠添加一個HandshakeCompletedListener的監聽器,SSL/TLS握手結束後這個監聽器的handshakeCompleted方法就會被調用。
客戶端有三種方法會觸發握手:
- 顯式調用startHandshake方法/calling startHandshake which explicitly begins handshakes, or
- 在socket對象上進行read或write操做/any attempt to read or write application data on this socket causes an implicit handshake, or
- 在socket對象上調用getSession方法/a call to getSession tries to set up a session if there is no currently valid session, and an implicit handshake is done.網絡
- <span style="font-family:Verdana;">package learning.net.ssl;
-
- import java.io.BufferedReader;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.PrintWriter;
- import java.net.Socket;
- import java.security.KeyStore;
- import java.security.cert.X509Certificate;
-
- import javax.net.ssl.HandshakeCompletedEvent;
- import javax.net.ssl.HandshakeCompletedListener;
- import javax.net.ssl.KeyManagerFactory;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.SSLPeerUnverifiedException;
- import javax.net.ssl.SSLServerSocket;
- import javax.net.ssl.SSLServerSocketFactory;
- import javax.net.ssl.SSLSocket;
- import javax.net.ssl.TrustManagerFactory;
-
- public class CatServer implements Runnable, HandshakeCompletedListener {
-
- public static final int SERVER_PORT = 11123;
-
- private final Socket _s;
- private String peerCerName;
-
- public CatServer(Socket s) {
- _s = s;
- }
-
- public static void main(String[] args) throws Exception {
- String serverKeyStoreFile = "c:\\_tmp\\catserver.keystore";
- String serverKeyStorePwd = "catserverks";
- String catServerKeyPwd = "catserver";
- String serverTrustKeyStoreFile = "c:\\_tmp\\catservertrust.keystore";
- String serverTrustKeyStorePwd = "catservertrustks";
-
- KeyStore serverKeyStore = KeyStore.getInstance("JKS");
- serverKeyStore.load(new FileInputStream(serverKeyStoreFile), serverKeyStorePwd.toCharArray());
-
- KeyStore serverTrustKeyStore = KeyStore.getInstance("JKS");
- serverTrustKeyStore.load(new FileInputStream(serverTrustKeyStoreFile), serverTrustKeyStorePwd.toCharArray());
-
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- kmf.init(serverKeyStore, catServerKeyPwd.toCharArray());
-
- TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- tmf.init(serverTrustKeyStore);
-
- SSLContext sslContext = SSLContext.getInstance("TLSv1");
- sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
-
- SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
- SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(SERVER_PORT);
- sslServerSocket.setNeedClientAuth(true);
-
- while (true) {
- SSLSocket s = (SSLSocket)sslServerSocket.accept();
- CatServer cs = new CatServer(s);
- s.addHandshakeCompletedListener(cs);
- new Thread(cs).start();
- }
- }
-
- @Override
- public void run() {
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(_s.getInputStream()));
- PrintWriter writer = new PrintWriter(_s.getOutputStream(), true);
-
- writer.println("Welcome~, enter exit to leave.");
- String s;
- while ((s = reader.readLine()) != null && !s.trim().equalsIgnoreCase("exit")) {
- writer.println("Echo: " + s);
- }
- writer.println("Bye~, " + peerCerName);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- _s.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- @Override
- public void handshakeCompleted(HandshakeCompletedEvent event) {
- try {
- X509Certificate cert = (X509Certificate) event.getPeerCertificates()[0];
- peerCerName = cert.getSubjectX500Principal().getName();
- } catch (SSLPeerUnverifiedException ex) {
- ex.printStackTrace();
- }
- }
-
- }</span>
(三)client端處理流程和代碼
處理流程:(1~5和server相同)
1)加載client的keystore文件。
2)加載client的truststore文件。
3) 建立KeyManagerFactory對象並初始化。
4) 建立TrustManagerFactory對象並初始化。truststore中存的是server的公鑰,不須要keypass也能夠訪問。
5)建立SSLContext並用3)和4)中建立的KeyManagerFactory和TrustManagerFactory對象來初始化。
6)建立SSLSocketFactory,在指定的網絡地址和端口上建立SSLSocket。
7)在這個SSLSocket對象的輸入輸出流上進行讀寫。
- <span style="font-family:Verdana;">package learning.net.ssl;
-
- import java.io.BufferedReader;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.PrintWriter;
- import java.net.Socket;
- import java.security.KeyStore;
-
- import javax.net.ssl.KeyManagerFactory;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.SSLSocketFactory;
- import javax.net.ssl.TrustManagerFactory;
-
- public class FoxClient {
- public static void main(String[] args) throws Exception {
- String clientKeyStoreFile = "c:\\_tmp\\foxclient.keystore";
- String clientKeyStorePwd = "foxclientks";
- String foxclientKeyPwd = "foxclient";
- String clientTrustKeyStoreFile = "c:\\_tmp\\foxclienttrust.keystore";
- String clientTrustKeyStorePwd = "foxclienttrustks";
-
- KeyStore clientKeyStore = KeyStore.getInstance("JKS");
- clientKeyStore.load(new FileInputStream(clientKeyStoreFile), clientKeyStorePwd.toCharArray());
-
- KeyStore clientTrustKeyStore = KeyStore.getInstance("JKS");
- clientTrustKeyStore.load(new FileInputStream(clientTrustKeyStoreFile), clientTrustKeyStorePwd.toCharArray());
-
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- kmf.init(clientKeyStore, foxclientKeyPwd.toCharArray());
-
- TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- tmf.init(clientTrustKeyStore);
-
- SSLContext sslContext = SSLContext.getInstance("TLSv1");
- sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
-
- SSLSocketFactory socketFactory = sslContext.getSocketFactory();
- Socket socket = socketFactory.createSocket("localhost", CatServer.SERVER_PORT);
-
- PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
- BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
-
- send("hello", out);
- send("exit", out);
- receive(in);
- socket.close();
- }
-
- public static void send(String s, PrintWriter out) throws IOException {
- System.out.println("Sending: " + s);
- out.println(s);
- }
-
- public static void receive(BufferedReader in) throws IOException {
- String s;
- while ((s = in.readLine()) != null) {
- System.out.println("Reveived: " + s);
- }
- }
- }</span>
(四)使用Openssl進行證書籤名
若是須要對證書進行簽名,可使用keytool生成一個csr(certificate sign request), 而後提交到一個CA進行簽名。這裏使用openssl提供的CA功能。
1)[openssl]生成CA的RSA私鑰。
bash-3.00$ openssl genrsa -out cakey.pem 1024
2)[openssl]使用CA.sh腳本初始化CA。腳本會要求輸入剛纔生成的私鑰文件。
bash-3.00$ CA.sh -newca
CA certificate filename (or enter to create)
cakey.pem
3)[openssl]利用CA私鑰生成自簽名的CA根證書。
指定x509參數表示要生成自簽名的證書。(咱們知道證書裏面只包含公鑰,爲何從私鑰能夠生成證書呢?這是由於私鑰裏面包含RSA算法所需的參數,其實能夠算出公鑰,後面會詳細解釋)
bash-3.00$ openssl req -new -x509 -key cakey.pem -out ca.cer -config openssl.cnf
(Linux上openssl.cnf的路徑/etc/pki/tls/openssl.cnf,能夠拷貝一份到當前目錄)
4)[keytool]生成server的keystore文件。
C:\Program Files\Java\jre7>keytool -genkey -alias catserver -keyalg rsa -keysize 1024 -sigalg sha256withrsa -keypass catserver -keystore c:\_tmp\catserver.keystore -storepass catserverks
5)[keytool]生成server的csr.
C:\Program Files\Java\jre7>keytool -certreq -alias catserver -file c:\_tmp\catserver.csr -keypass catserver -keystore c:\_tmp\catserver.keystore -storepass catserverks
6) [openssl]對server的csr進行簽名獲得server證書
bash-3.00$ openssl ca -in catserver.csr -out catserver.cer -cert ca.cer -keyfile cakey.pem -config openssl.cnf
(若是是由於權限問題,不能訪問CA的newcerts目錄,能夠先進行以下操做:
bash-3.00$ mkdir newcerts
bash-3.00$ touch index.txt
bash-3.00$ echo 01 > serial)
7)[keytool]生成client的keystore文件。
C:\Program Files\Java\jre7>keytool -genkey -alias foxclient -keyalg dsa -keysize 512 -sigalg sha1withdsa -keypass foxclient -keystore c:\_tmp\foxclient.keystore -storepass foxclientks
8)[keytool]生成client的csr.
C:\Program Files\Java\jre7>keytool -certreq -alias foxclient -file c:\_tmp\foxclient.csr -keypass foxclient -keystore c:\_tmp\foxclient.keystore -storepass foxclientks
9) [openssl]對client的csr進行簽名獲得client證書
bash-3.00$ openssl ca -in foxclient.csr -out foxclient.cer -cert ca.cer -keyfile cakey.pem -config openssl.cnf
如今咱們有以下文件:
catserver.keystore -- server的keystore文件
catserver.cer -- server證書文件,已經由CA簽名。
foxclient.keystore -- client的keystore文件
foxclient.cer -- client證書文件,已經由CA簽名。
ca.cer -- CA的證書文件。做爲根證書。
接下來咱們還須要
10)[keytool]導入ca.cer和catserver.cer到catserver.keystore
keytool -import -trustcacerts -alias ca -keystore c:\_tmp\catserver.keystore -storepass catserverks -file c:\_tmp\ca.cer
keytool -import -alias catserver -keypass catserver -keystore c:\_tmp\catserver.keystore -storepass catserverks -file c:\_tmp\catserver.cer
11)[keytool]導入ca.cer和foxclient.cer到foxclient.keystore
keytool -import -trustcacerts -alias ca -keystore c:\_tmp\foxclient.keystore -storepass foxclientks -file c:\_tmp\ca.cer
keytool -import -alias foxclient -keypass foxclient -keystore c:\_tmp\foxclient.keystore -storepass foxclientks -file c:\_tmp\foxclient.cer
12)[keytool]建立server的truststore文件並導入ca.cer和foxclient.cer.
keytool -import -trustcacerts -alias ca -keystore c:\_tmp\catservertrust.keystore -storepass catservertrustks -file c:\_tmp\ca.cer
keytool -import -alias foxclient -keystore c:\_tmp\catservertrust.keystore -storepass catservertrustks -file c:\_tmp\foxclient.cer
13)[keytool]建立client的truststore文件並導入ca.cer和catserver.cer.
keytool -import -trustcacerts -alias ca -keystore c:\_tmp\foxclienttrust.keystore -storepass foxclienttrustks -file c:\_tmp\ca.cer
keytool -import -alias catserver -keystore c:\_tmp\foxclienttrust.keystore -storepass foxclienttrustks -file c:\_tmp\catserver.cer
*前面提到能夠從CA的私鑰文件生成CA的根證書。證書裏面其實包含的是公鑰。
其實openssl也提供命令能夠直接從私鑰cakey.pem生成公鑰。
openssl rsa -in cakey.pem -pubout -out caputkey.pem
爲何可以從私鑰推導出公鑰呢?
這須要瞭解RSA算法-〉http://blog.csdn.NET/fw0124/article/details/41118525
緣由很簡單,由於私鑰中也包含了計算公鑰所需的參數n和e。
*可使用openssl來生成server的證書,步驟以下
openssl genrsa -out catserverkey.pem 1024
openssl req -new -key catserverkey.pem -out catserver.csr -config openssl.cnf
openssl ca -in catserver.csr -out catserver.cer -cert ca.cer -keyfile cakey.pem -config openssl.cnf
=====================================
若是openssl ca ...進行簽名的時候碰到
The stateOrProvinceName field needed to be the same in the ...的錯誤,能夠修改openssl.cnf文件:
[ policy_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
若是遇到
failed to update database
TXT_DB error number 2 的錯誤,
刪除index.txt,並再touch下
若是keytool -import命令導入簽名後的證書的時候遇到如下錯誤
keytool 錯誤: java.lang.Exception: 沒法從回覆中創建鏈
從新導入下ca.cer便可。
keytool -delete -alias ca -keystore c:\_tmp\catserver.keystore -storepass catserverks
keytool -import -trustcacerts -alias ca ...
======================================
CER文件到PEM文件的轉換較簡單。這二者都是X509證書,編碼不一樣,使用openssl工具便可:
openssl x509 -inform der -in catserver.cer -out catserver.pem
(五)如何從.keystore文件中導出私鑰
Keytool不支持直接從.keystore中導出私鑰。可是能夠把JKS文件轉換成openssl支持的PKCS#12格式的文件。
因此能夠經過下面2步來從.keystore中導出私鑰。
➜ ~ keytool -importkeystore -srckeystore catserver.keystore -destkeystore catserver.p12 -deststoretype PKCS12 -srcalias catserver -srcstorepass catserverks -srckeypass catserver -deststorepass catserverks -destkeypass catserver
Warning: Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -destkeypass value.
➜ ~ openssl pkcs12 -in catserver.p12 -nodes -nocerts -out catserverkey.pem
Enter Import Password:
MAC verified OK
➜ ~
*-nodes選項意思是No DES,即不加密導出的私鑰。