1. 程式人生 > >PKI學習之路(四)-----------------------SSL雙向認證

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