首页 > 解决方案 > 关于使用 keystore 和 truststore 编写 Java SSL 客户端和服务器的疑惑

问题描述

keytool我使用如下方式创建了密钥库、信任库、私钥和证书:

  1. 创建密钥库、私钥和证书

    keytool -genkey -alias ssl_key -keyalg RSA -keypass passwd123 -keystore keystore.jks -storepass passwd123
    
  2. 将证书从密钥库导出到信任库

    keytool -import -v -trustcacerts -alias ssl_key -keypass passwd123 -file ssl_key.cer -keystore truststore.jks -storepass passwd123
    

现在我想写java SSL客户端服务器。我在网上参考了一些文章(1、2 和代码,并编写了简单的Java SSL服务器和客户端,如下所示:

服务器

public class Server {
    static KeyStore ks;
    static KeyManagerFactory kmf;
    static TrustManagerFactory tmf;
    static SSLContext sc;
    static TrustManager[] trustManagers;

    static {
        try {
            ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("D:\\javasslstores\\keystore.jks"), "passwd123".toCharArray());

            kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, "passwd123".toCharArray());

            tmf = TrustManagerFactory.getInstance("SunX509"); 
            tmf.init(ks);

            sc = SSLContext.getInstance("TLS"); 
            sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 
        } catch (Exception e) {
            System.out.println(e.getMessage());
            System.out.println(e.getStackTrace());
        }
    }

    public static void main(String[] args) throws IOException {
        System.out.println("SSL Server");
        SSLServerSocketFactory ssf = sc.getServerSocketFactory(); 
        SSLServerSocket s = (SSLServerSocket) ssf.createServerSocket(8089);
        System.out.println("Listening on port 8089");
        SSLSocket socket = (SSLSocket) s.accept();


        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            String line;
            System.out.println("Data from client:");
            while((line = bufferedReader.readLine()) != null){
                System.out.println(line);
                out.println(line);
            }
        }
        System.out.println("Closed");
    }
}

客户

public class Client {
    static KeyStore ks;
    static KeyManagerFactory kmf;
    static TrustManagerFactory tmf;
    static SSLContext sc;
    static TrustManager[] trustManagers;

    static 
    {
        try 
        {
            ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("D:\\javasslstores\\keystore.jks"), "passwd123".toCharArray());

            kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, "passwd123".toCharArray());

            tmf = TrustManagerFactory.getInstance("SunX509"); 
            tmf.init(ks);

            sc = SSLContext.getInstance("TLS"); 
            sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            System.out.println(e.getStackTrace());
        }
    }

    public static void main(String[] args) throws IOException {
        SSLSocketFactory ssf = sc.getSocketFactory();
        SSLSocket socket = (SSLSocket) ssf.createSocket("localhost", 8089);
        socket.startHandshake();

        PrintWriter out = new PrintWriter
                                (new BufferedWriter
                                (new OutputStreamWriter
                                (socket.getOutputStream())));

        System.out.println("SSL Client");

        out.println("GET / HTTP/1.0");
        out.println("From java ssl client");
        out.println("written by me");
        out.flush();

        if (out.checkError())
            System.out.println("SSLSocketClient:  java.io.PrintWriter error");

        BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                socket.getInputStream()));

        String inputLine;

        while ((inputLine = in.readLine()) != null)
            System.out.println(inputLine);

        in.close();
        out.close();
        socket.close();
    }
}

上面的代码有效。

怀疑

但我有以下疑问:

  1. 怎么办?:将密钥库或信任库传递给客户端和服务器或两者都传递?
    查看链接 1中的示例,我在客户端和服务器中都指定了密钥库和信任库。那就是我在两者中都有以下行:

    sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);  //point 0
    

    如果我理解正确,服务器需要密钥库,客户端需要信任库。因此,在服务器中有以下内容:

    sc.init(kmf.getKeyManagers(), null, null); //point 1
    

    并在客户端:

    sc.init(null, tmf.getTrustManagers(), null); //point 2
    

    也有效。但是有这个:

    sc.init(null, tmf.getTrustManagers(), null);
    

    在服务器和/或这个:

    sc.init(kmf.getKeyManagers(), null, null);
    

    在客户端失败。那么我对上面的第 1 点和第 2 点是否正确?当我需要在第 0 点中同时指定信任库和密钥库时?

  2. 如果有多个密钥和证书用于通过 SSL 进行通信,则使用哪个密钥和证书?
    密钥库和信任库仅包含单个密钥和证书。但是在代码中,我没有指定使用哪个密钥和证书。我什至不知道我是否必须明确指定它们。如果我在商店中有多个密钥和证书怎么办?我是否必须明确指定要使用哪一个。如果是,我该怎么做?(似乎缺少一些基本的东西:\)

  3. oracle 网站上是否有任何官方示例解释使用密钥库和信任库编写 java SSL 服务器和客户端?

标签: javassl

解决方案


推荐阅读