Https双向认证,实现系统间通信双向认证

1.背景java

如今公司系统较多,多系统间须要进行通信,已经实现了https单项认证,如今为了保证通信的安全,须要实现https双向认证,让系统间通信更安全。web

2.问题spring

当时拿到这个问题,第一时间去网上百度https双向认证的案例,试了好多种,有单项认证的实现,还有双向认证基于浏览器端的访问,对于两个系统之间的https双向认证不多,本身也浪费了好多时间没有成功,因而,下定决心,先把https双向认证的原理搞清楚,懂了原理后,解决起来问题就方便了,下面附上https单项认证和双向认证的原理图apache

              https双向认证原理图 

            https单项认证原理图                                                                                       

经过图片可知,https单项认证是客户端对服务端的认证,双向认证是在此基础上加了服务端对客户端的认证!浏览器

3.前提准备安全

 1. 多个springboot项目,以两个springboot项目为例,这里暂且叫Client项目,Server项目。springboot

 2. Client项目所在的服务器,Client.p12(客户端证书库) ,Client.cer(客户端公钥);服务器

     Server项目所在的服务器,Server.p12(服务端证书库),Server.cer(服务端公钥);app

4.实现步骤dom

 1. 建立springboot项目,Client ,Server 此处,请自行百度建立如何建立springboot项目。

 2. 使用jdk自带的keytool工具,生成Client和Server端相应的证书,步骤以下:

      a. Client端证书生成步骤:

           1.生成客户端 Client.p12文件    

         keytool -genkey -v -alias Client -keyalg RSA -storetype PKCS12 -keystore C:\D\jdk1.8.0_161\Client.p12      

            设置密码: lq123456
   注意事项:生成证书,您的名字与姓氏一项,应该填写服务器的ip(此处应该是域名,可是没有域名,故此处填写服务器ip)
           2 . 导出客户端公钥Client.cer 文件
         keytool -keystore C:\D\jdk1.8.0_161\Client.p12  -export -alias Client -file C:\D\jdk1.8.0_161\Client.cer

      b. Server端证书生成步骤

           1.  生成服务端Server.p12文件

         keytool -genkey -v -alias Server  -keyalg RSA -storetype PKCS12 -keystore C:\D\jdk1.8.0_161\Server.p12

           设置密码: lq123456
   注意事项:生成证书,您的名字与姓氏一项,应该填写服务器的ip(此处应该是域名,可是没有域名,故此处填写服务器ip)
           2.    导出服务端公钥Server.cer 文件

       keytool -keystore C:\D\jdk1.8.0_161\Server.p12 -export -alias Server -file C:\D\jdk1.8.0_161\Server.cer

      c. 将Client端和Server端的公钥文件(.cer文件)导入双方系统的jre运行环境的cacerts证书库

           1.    将客户端公钥导入的服务端jdk信任库
          keytool -import -file Client.cer -keystore  C:\D\jdk1.8.0_161\jre\lib\security\cacerts –v

           2.    将服务端公钥导入到客户端的jdk信任库
           keytool -import -file Server.cer -keystore  C:\D\jdk1.8.0_161\jre\lib\security\cacerts –v

           3.  将客户端公钥导入到服务端Server.p12证书库

           keytool -import -v -file C:\D\jdk1.8.0_161\Client.cer  -keystore C:\D\jdk1.8.0_161\server.p12

      注意事项:此处导入的密码为changeit,默认密码

至此,证书生成完成,证书库导入完成!

       d. 代码实现

            1.Server端

            第一步:在application.properties中添加以下配置:包括本地证书库和受信任证书配置

server.port=8090
server.address=10.119.165.171
server.ssl.key-store=classpath:server.p12
server.ssl.key-store-password=lq123456
server.ssl.key-alias=server
server.ssl.keyStoreType=JKS


server.ssl.trust-store=classpath:server.p12
server.ssl.trust-store-password=lq123456
server.ssl.client-auth=need
server.ssl.trust-store-type=JKS
server.ssl.trust-store-provider=SUN

             第二步:服务端接口开放

package com.example.server1.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author lucasliang
 * @date 01/03/2019 3:21 下午
 * @Description
 */
@RestController
@RequestMapping("/server")
public class ServerController {

  @RequestMapping("/hello")
  public String getUrlInfo() {
    return "************request https success************";
  }

}

 

     2. Client端

             第一步:在application.properties中添加以下配置:

server.port=8091
server.address=10.119.165.171
server.ssl.key-store=classpath:client.p12
server.ssl.key-store-password=lq123456
server.ssl.key-alias=client
server.ssl.keyStoreType=JKS

              第二步:单元测试:

package com.example.client;

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author lucasliang
 * @date 04/03/2019 2:13 下午
 * @Description
 */
@SpringBootTest(classes = {Client1ApplicationTests.class})
@RunWith(SpringRunner.class)
public class P12CertTest {

  private final static String TEST_URL = "https://10.119.165.171:8090/server/hello";

  @Test
  public void getHKVesselTrip() throws Exception {
    KeyStore clientStore = KeyStore.getInstance("PKCS12");
    clientStore
        .load(new FileInputStream("C:\\D\\jdk1.8.0_161\\shuangxiang\\client_original.p12"),
            "lq123456".toCharArray());

    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(clientStore, "lq123456".toCharArray());
    KeyManager[] kms = kmf.getKeyManagers();
    TrustManagerFactory tmf = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());

    KeyStore trustStore = KeyStore.getInstance("JKS");
    trustStore.load(new FileInputStream("C:\\D\\jdk1.8.0_161\\jre\\lib\\security\\cacerts"),
        "changeit".toCharArray());
    tmf.init(trustStore);
    TrustManager[] tms = tmf.getTrustManagers();
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(kms, tms, new SecureRandom());
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
    CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
    try {
      HttpGet httpget = new HttpGet(TEST_URL);
      System.out.println("executing request" + httpget.getRequestLine());
      CloseableHttpResponse response = httpclient.execute(httpget);
      try {
        HttpEntity entity = response.getEntity();
        if (entity != null) {
          System.out.println(EntityUtils.toString(entity));
        }
      } finally {
        response.close();
      }
    } finally {

      httpclient.close();
    }

  }

}

  此时,Client端发送请求到Server端,请求成功,https双向认证完成!

5. 心得

  凡事搞懂原理,事半功倍!