Keytool工具生成SSL证书以及在Java中实现SSL

1.Keytool工具生成SSL证书

    keytool即JDK中自带的证书生成工具,常见的还有openssl工具。

     1.生成一个自签名的CA证书,为了给Client和Server的证书签名。

      命令:keytool -genkeypair (-keyalg RSA) -alias TEST_ROOT -keystore test_root.jks

      解释:生成一对密钥,存储在test_root.jks中,条目别名为TEST_ROOT。输入该命令后会提示输入个人信息。当命令完成后,会在test_root.jks中生成一个自签名的证书。当然,私钥也保存在该文件中。这里密钥库的密码和密钥密码都设置成123456

如果不加keyalg 的,默认是DSA算法生成

2.为server生成一对密钥(也就是一个自签名的证书)

 命令:keytool -genkeypair -alias TEST_SERVER -keystore test_server.jks

解释:生成一对密钥,存储在test_server.jks中,条目别名TEST_SERVER。

3.生成一个签名请求文件(即请求CA给server自签名的证书签名)

命令:keytool -certreq -file test_server.csr -alias TEST_SERVER -keystore test_server.jks

解释:为存储在test_server.jks中的别名TEST_SERVER生成一个证书请求文件test_server.csr。

4.ca为server的证书签名

命令:keytool -gencert -infile test_server.csr -outfile test_server.cer -alias TEST_ROOT -keystore TEST_ROOT.jks

解释:利用TEST_ROOT.jks中条目别名为TEST_ROOT的私钥为test_server.csr证书请求对应的证书签名,并将签名后的证书保存在test_server.cer中。

5.导出ca证书

命令:keytool -exportcert -alias TEST_ROOT -file test_root.cer -keystore test_root.jks

解释:将test_root.jks中条目别名为TEST_ROOT的证书导出到test_root.cer中。

6.将ca证书导入到test_server.jks中。

命令:keytool -importcert -alias TEST_ROOT -file test_root.cer -keystore TEST_SERVER.jks

解释:将test_root.cer证书文件导入TEST_SERVER.jks中,条目别名为TEST_ROOT。这里需要注意,在TEST_SERVER.jks中之前应不存在条目别名为TEST_ROOT,这样才会以信任证书的方式导入。

7.更新server证书

命令:keytool -importcert -alias TEST_SERVER -file test_server.cer -keystore TEST_SERVER.jks

解释:将test_server.cer导入到TEST_SERVER.jks中。因为在生成server密钥对的时候,在test_server.jks中已经存在自签的证书,条目别名为TEST_SERVER。而该命令保存的别名还是TEST_SERVER。意思上就是更新证书。将之前自签名的证书替换为ca签名过的证书。

8.查看test_server.jks中server的证书和ca证书。

命令:keytool -list -v -keystore test_server.jks

从条目test_server中可以看出,该证书被ca签名后,ca的证书也保存在该条目中,形成证书链。

对于Client的证书生成参考2-7步。在该过程中生成的文件如下:

2.在Java中实现SSL通信

  Java中主要通过JSSE(Java Secure Socket Extension)来完成安全套接字的编写。其中主要涉及了一些类,例如Keystore、KeyManagerFactory、TrustManagerFactory、SSLContext等。它们的关系如下图(图的来源于文章结尾的链接中)

编程的步骤如下:

1.利用keystore类完成客户端和服务器的证书库和信任库的加载

2.利用加载的证书库和信任库,完成KeyManagerFactory和TrustManagerFactory的初始化。

3.生成SSLContext对象,并用keyManagerFactory和TrustManagerFactory生成的keyManager和TrustManager完成初始化。

4.利用SSLContext对象,生成对应的SSLSocket和SSLServerSocket。

需要的文件

1.test_root.jks(存储了ca证书,因为client和server的证书都是被ca签名的,所以ca证书被信任,则client和server的证书都会被信任)。

2.test_client.jks(存储了client的证书)。

3.test_server.jks(存储了server的证书)。

客户端代码

public class Client {

    public static void main(String[] args) throws Exception {

        String clientKeyStoreFile = "d:\\keystore\\test_client.jks";

        String clientKeyStorePwd = "123456";

        String clientKeyPwd = "123456";

        String clientTrustKeyStoreFile = "d:\\keystore\\test_root.jks";

        String clientTrustKeyStorePwd = "123456";

        //生成client的keystore对象

        KeyStore clientKeyStore = KeyStore.getInstance("JKS");

        clientKeyStore.load(new FileInputStream(clientKeyStoreFile), clientKeyStorePwd.toCharArray());

        //生成信任证书的keystore对象

        KeyStore clientTrustKeyStore = KeyStore.getInstance("JKS");

        clientTrustKeyStore.load(new FileInputStream(clientTrustKeyStoreFile), clientTrustKeyStorePwd.toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

        kmf.init(clientKeyStore, clientKeyPwd.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", Server.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);

        }

    }

}

服务器代码

public class Server implements Runnable,HandshakeCompletedListener {

    public static final int SERVER_PORT = 11123;

    private final Socket s;

    private String peerCerName;

    public Server(Socket s) {

      this.s = s;

    }

    public static void main(String[] args) throws Exception {

        String serverKeyStoreFile = "d:\\keystore\\test_server.jks";

        String serverKeyStorePwd = "123456";

        String ServerKeyPwd = "123456";

        String serverTrustKeyStoreFile = "d:\\keystore\\test_root.jks";

        String serverTrustKeyStorePwd = "123456";

        /*

        * 加载server.keystore

        *

        */

        KeyStore serverKeyStore = KeyStore.getInstance("JKS");

        serverKeyStore.load(new FileInputStream(serverKeyStoreFile), serverKeyStorePwd.toCharArray());

        /*

        * 加载servertrust.keystore

        *

        */

        KeyStore serverTrustKeyStore = KeyStore.getInstance("JKS");

        serverTrustKeyStore.load(new FileInputStream(serverTrustKeyStoreFile), serverTrustKeyStorePwd.toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

        kmf.init(serverKeyStore, ServerKeyPwd.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();

            Server cs = new Server(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 message;

            while ((message = reader.readLine()) != null && !message.trim().equalsIgnoreCase("exit")) {

                writer.println("Echo: " + message);

            }

            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();

        }

    }

}

运行结果:

说明SSL握手成功建立。

参考的文章:https://www.ibm.com/developerworks/cn/java/j-lo-socketkeytool/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容