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

转自: https://blog.csdn.net/u012977486/article/details/88815621

一、Http

HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准。HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全。

使用TCP端口为:80

二、Https

Hyper Text Transfer Protocol over Secure Socket Layer,安全的超文本传输协议,网景公式设计了SSL(Secure Sockets Layer)协议用于对Http协议传输的数据进行加密,保证会话过程中的安全性。

使用TCP端口默认为443

三、SSL协议加密方式

SSL协议即用到了对称加密也用到了非对称加密(公钥加密),在建立传输链路时,SSL首先对对称加密的密钥使用公钥进行非对称加密,链路建立好之后,SSL对传输内容使用对称加密。

对称加密

速度高,可加密内容较大,用来加密会话过程中的消息

公钥加密

加密速度较慢,但能提供更好的身份认证技术,用来加密对称加密的密钥

四、单向认证

Https在建立Socket连接之前,需要进行握手,具体过程如下:

1、客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。

2、服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书

3、客户端使用服务端返回的信息验证服务器的合法性,包括:

证书是否过期

发型服务器证书的CA是否可靠

返回的公钥是否能正确解开返回证书中的数字签名

服务器证书上的域名是否和服务器的实际域名相匹配

验证通过后,将继续进行通信,否则,终止通信

4、客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择

5、服务器端在客户端提供的加密方案中选择加密程度最高的加密方式。

6、服务器将选择好的加密方案通过明文方式返回给客户端

7、客户端接收到服务端返回的加密方式后,使用该加密方式生成产生随机码,用作通信过程中对称加密的密钥,使用服务端返回的公钥进行加密,将加密后的随机码发送至服务器

8、服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获取对称加密密钥。 在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。

五、双向认证

双向认证和单向认证原理基本差不多,只是除了客户端需要认证服务端以外,增加了服务端对客户端的认证,具体过程如下:

1、客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。

2、服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书

3、客户端使用服务端返回的信息验证服务器的合法性,包括:

证书是否过期

发型服务器证书的CA是否可靠

返回的公钥是否能正确解开返回证书中的数字签名

服务器证书上的域名是否和服务器的实际域名相匹配

验证通过后,将继续进行通信,否则,终止通信

4、服务端要求客户端发送客户端的证书,客户端会将自己的证书发送至服务端

5、验证客户端的证书,通过验证后,会获得客户端的公钥

6、客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择

7、服务器端在客户端提供的加密方案中选择加密程度最高的加密方式

8、将加密方案通过使用之前获取到的公钥进行加密,返回给客户端

9、客户端收到服务端返回的加密方案密文后,使用自己的私钥进行解密,获取具体加密方式,而后,产生该加密方式的随机码,用作加密过程中的密钥,使用之前从服务端证书中获取到的公钥进行加密后,发送给服务端

10、服务端收到客户端发送的消息后,使用自己的私钥进行解密,获取对称加密的密钥,在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。

六. 代码实现

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

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

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

4.实现步骤

 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双向认证完成!

发表评论

电子邮件地址不会被公开。 必填项已用*标注