PKI學習之路(四)-----------------------SSL雙向認證
專案地址:https://github.com/gongxianshengjiadexiaohuihui/PKI/tree/master/ssl
上一篇 我們講了如何用java自帶的keytool工具生成數字證書,我們需要準備兩個證書,一個是server的一個是client的
keytool -import -file client_pub_cer.cer -keystore server.keystore -storepass 123456
把 client的證書匯入到server的證書倉庫中,並且信任
同樣把server的證書匯入到client的證書倉庫中,並且信任
然後就開始程式碼編寫
服務端程式碼
package com.ggp.server; import javax.net.ServerSocketFactory; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.security.KeyStore; /** * @ClassName SSLServer * @Description TODO * @Author Mr.G * @Date 2018/11/29 16:44 * @Version 1.0 */ public class SSLServer extends Thread { private Socket socket; private ServerSocket serverSocket; public SSLServer(ServerSocket serverSocket)throws Exception{ this.serverSocket = serverSocket; } @Override public void run() { while (this.isAlive()) { try { socket = serverSocket.accept(); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer = new PrintWriter(socket.getOutputStream()); String data = reader.readLine(); writer.println(data); writer.close(); socket.close(); } catch (IOException e) { } } } public static void main(String[] args) throws Exception{ /** * KeyStore的位置路徑 */ String serverKeyStore = "C:\\Users\\14747\\server.keystore"; /** * KeyStore的密碼 */ String serverKeyStorePassword = "123456"; /** * 指定trustStore */ System.setProperty("javax.net.ssl.trustStore",serverKeyStore); /** * 定義並初始化一個keyStore */ KeyStore server_KeyStore = KeyStore.getInstance("JKS"); server_KeyStore.load(new FileInputStream(serverKeyStore),null); /** * 選用一個安全套接字協議 */ SSLContext context = SSLContext.getInstance("TLSv1"); /** * 初始化一個keyManager工廠 */ KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(server_KeyStore,serverKeyStorePassword.toCharArray()); /** * 初始化SSLcontext */ context.init(keyManagerFactory.getKeyManagers(),null,null); /** * 得到ServerSocket工廠 */ ServerSocketFactory factory = context.getServerSocketFactory(); ServerSocket serverSocket = factory.createServerSocket(28888); /** * 決定服務端是否驗證客戶端的身份 即選擇單向認證還是雙向認證 */ ((SSLServerSocket)serverSocket).setNeedClientAuth(true); new SSLServer(serverSocket).start(); } }
客戶端程式碼
package com.ggp.client; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.security.KeyStore; /** * @ClassName SSLClient * @Description TODO * @Author Mr.G * @Date 2018/11/30 9:17 * @Version 1.0 */ public class SSLClient { public static void main(String[] args)throws Exception{ String clientKeyStore = "D:\\client.keystore"; String clientKeyStorePassword = "123456"; System.setProperty("javax.net.ssl.trustStore",clientKeyStore); System.setProperty("javax.net.debug","ssl,handshake"); KeyStore client_keyStore = KeyStore.getInstance("JKS"); client_keyStore.load(new FileInputStream(clientKeyStore),null); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(client_keyStore,clientKeyStorePassword.toCharArray()); SSLContext context = SSLContext.getInstance("TLSv1"); context.init(keyManagerFactory.getKeyManagers(),null,null); SSLSocketFactory factory = context.getSocketFactory(); Socket socket =factory.createSocket("localhost",28888); PrintWriter writer = new PrintWriter(socket.getOutputStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer.println("hello"); writer.flush(); System.out.println(reader.readLine()); socket.close(); } }
服務端的程式碼,註釋很詳細,客戶端的程式碼基本配置和服務端大致相同
然後我們討論裡面出現的幾個名詞
System.setProperty("javax.net.ssl.trustStore",serverKeyStore);
如果是要發起SSL請求,這個時候通常是需要指定trustStore的
一種方式是通過啟動引數指定
java -Djavax.net.ssl.trustStore=yourTruststore -Djavax.net.ssl.trustStorePassword=yourpassword yourApp
還有一種方式,就是通過程式中指定Properties引數進行載入,不過一定要在請求發出前進行載入。就是我們所用的方式
SSLContext context = SSLContext.getInstance("TLSv1");
首先給出一張圖
(1)SSLContext: 此類的例項表示安全套接字協議的實現, 它是SSLSocketFactory、SSLServerSocketFactory和SSLEngine的工廠。
(2)SSLSocket: 擴充套件自Socket
(3)SSLServerSocket: 擴充套件自ServerSocket
(4)SSLSocketFactory: 抽象類,擴充套件自SocketFactory, SSLSocket的工廠
(5)SSLServerSocketFactory: 抽象類,擴充套件自ServerSocketFactory, SSLServerSocket的工廠
(6)KeyStore: 表示金鑰和證書的儲存設施
(7)KeyManager: 介面,JSSE金鑰管理器
(8)TrustManager: 介面,信任管理器
(9)X590TrustedManager: TrustManager的子介面,管理X509證書,驗證遠端安全套接字
幾種SSLContext Algorithms
SL - 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