以管理员身份打开Windows PowerShel,通过cd(与linux系统类似)命令进入到JDK的bin目录:如C:\Program Files\Java\jdk1.8.0_221\jre\bin,找到目录下有keytool.exe就是正确进入目录了

生成测试用证书:

参数说明:

genkey 表示要创建一个新的密钥

alias 表示 keystore 的别名、

keyalg 表示使用的加密算法是 RSA ,一种非对称加密算法

keysize 表示密钥的长度

keystore 表示生成的密钥存放位置

validity 表示密钥的有效时间,单位为天

import 表示导入证书,如要删除导入的证书用delete即可,注意信任库主要根据别名去判断证书是否导入,别名不能重复。删除同理也通别名

1.生成TrustStore(信任库) ---证书也可以用pfx代替

keytool -genkey -alias trustkeys -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore trustKeys.p12 -validity 36500

2.生成server-one客户端密钥 ---证书也可以用pfx代替

keytool -genkey -alias server-one -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore server-one.p12 -validity 36500

3.生成server-two客户端密钥 ---证书也可以用pfx代替

keytool -genkey -alias server-two -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore server-two.p12 -validity 36500

4.导出双方公钥

导出server-one的公钥

keytool -keystore server-one.p12 -export -alias server-one -file server-one-publicKey.cer

导出server-two的公钥

keytool -keystore server-two.p12 -export -alias server-two -file server-two-publicKey.cer

5.添加对方公钥到信任库,注意这里共用的一个信任库,所以是把两个公钥导入到一个信任库了,如果正常情况下是有两个trustKeys.p12文件,互相导入对方的公钥信息即可,如果是用CFCA的证书的话需要导入证书的二级根证书即可(公钥:一般是把二级根证书和以及根证书一起导入)

keytool -import -alias server-one -v -file server-one-publicKey.cer -keystore trustKeys.p12

keytool -import -alias server-two -v -file server-two-publicKey.cer -keystore trustKeys.p12

6.如要删除导入的信任证书

keytool -delete -alias 删除证书的别名 -keystore trustKeys.p12

搭建测试用服务端SpringBoot项目:server-one

  1. 把server-one服务的证书和信任库放到对应目录下



resttemplate ssl resttemplatessl双向认证_ssl


  1. 修改配置文件,开启应用SSL认证--加入下面这段配置注意配置文件格式是yml还是properties,不同文件的格式是不同的,下文是yml格式
# 开启ssl
server.ssl.enabled: true
#双向校验need
server.ssl.client-auth: need
#协议
#server.ssl.protocol: TLS
#服务通信证书
server.ssl.key-store: classpath:certFile/server-one.p12
#证书密码
server.ssl.key-store-password: 12345678
#证书格式
server.ssl.key-store-type: PKCS12
#证书别名
server.ssl.keyAlias: server-one
#信任库文件
server.ssl.trust-store: file:E:/Test_Dir/pfx/SDKtrustKeys.p12
#信任库密码
server.ssl.trust-store-password: 12345678
#信任库类型
server.ssl.trust-store-type: PKCS12
  1. 修改pom文件--在build里加入这段,在打包是把证书带入jar
<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
            <include>certFile/server-one.p12</include>
            <include>certFile/trustKeys.p12</include>
        </includes>
    </resource>
    <resource>
        <directory>src/main/resources</directory>
    </resource>
</resources>
  1. 启动项目,服务端开启认证完成,通过地址去访问会发现报错--不开启认证可以访问的情况下
  2. 编写RestTemplateUtil工具类--注意提前导入Httpclient的jar包
package com.csii.gateway.util;

import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;

/**
 * @Description //HTTPS通信双向认证工具类
 * @Date 2023-02-23 11:19
 **/

//@Configuration
public class RestTemplateUtil {
    /**
     * resources 目录下证书路径
     * cerPath = "classpath:/keystore/client_trust.keystore";
     *
     *      * 根据给定的资源路径,返回响应的Resource资源对象
     *      * 支持如下三种形式:
     *      *"file:C:/test.dat".
     *      *"classpath:test.dat".
     *      *"WEB-INF/test.dat".
     *
     */
    //SDK端证书路径
    private static String clientCerPath;
   //SDK段证书密码
    private static String clientCerPwd;
    //客户端证书类型--默认PKCS12
    public static String clientCertType = "PKCS12";

    //客户端密钥库密码---就是通信证书密码
    public static String clientPass;

    //信任库路径
    public static String trustCerPath;
    //信任库密码
    public static String trustCerPass;
    //信任库证书类型--默认PKCS12
    public static String trustCertType = "PKCS12";


    public static String getClientCerPath() {
        return clientCerPath;
    }

    public static void setClientCerPath(String clientCerPath) {
        RestTemplateUtil.clientCerPath = clientCerPath;
    }

    public static String getClientCerPwd() {
        return clientCerPwd;
    }

    public static void setClientCerPwd(String clientCerPwd) {
        RestTemplateUtil.clientCerPwd = clientCerPwd;
    }

    public static String getClientPass() {
        return clientPass;
    }

    public static void setClientPass(String clientPass) {
        RestTemplateUtil.clientPass = clientPass;
    }

    public static String getTrustCerPath() {
        return trustCerPath;
    }

    public static void setTrustCerPath(String trustCerPath) {
        RestTemplateUtil.trustCerPath = trustCerPath;
    }

    public static String getTrustCerPass() {
        return trustCerPass;
    }

    public static void setTrustCerPass(String trustCerPass) {
        RestTemplateUtil.trustCerPass = trustCerPass;
    }

    public static String getClientCertType() {
        return clientCertType;
    }

    public static void setClientCertType(String clientCertType) {
        RestTemplateUtil.clientCertType = clientCertType;
    }

    public static String getTrustCertType() {
        return trustCertType;
    }

    public static void setTrustCertType(String trustCertType) {
        RestTemplateUtil.trustCertType = trustCertType;
    }


    //双向认证
    public static RestTemplate rsaSslRestTemplate() throws Exception{

        RestTemplate  restTemplate = new RestTemplate(rsaHttpComponentsClientHttpRequestFactory());
        restTemplate.setErrorHandler(new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse clientHttpResponse) {
                return false;
            }

            @Override
            public void handleError(ClientHttpResponse clientHttpResponse) {
                    //默认处理非200的返回,会抛异常
            }
            });

        return restTemplate;
    }

    public static CloseableHttpClient httpClient = null;

    public static HttpComponentsClientHttpRequestFactory rsaHttpComponentsClientHttpRequestFactory() throws Exception{
        //获取httpClient对象,防止重复创建--读取证书信息,对象生成后不做其他操作所以未加锁
        if(httpClient == null){
            httpClient = createCloseableHttpClientByRsa();
        }
        HttpComponentsClientHttpRequestFactory httpsFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        httpsFactory.setReadTimeout(5000);
        httpsFactory.setConnectTimeout(5000);
        return httpsFactory;
    }

    /**
     * https协议证书认证---国际双向认证
     *
     * @return
     * @throws Exception
     */
    private static CloseableHttpClient createCloseableHttpClientByRsa() throws  Exception{

        //加载客户端证书
        KeyStore clientStore  = KeyStore.getInstance(clientCertType);
        clientStore .load(resourceLoader(clientCerPath), clientCerPwd.toCharArray());
        // 创建密钥管理工厂实例
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        // 初始化客户端密钥库
        keyManagerFactory.init(clientStore , clientPass.toCharArray());
        KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();


        // 创建信任库管理工厂实例
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        KeyStore trustStore = KeyStore.getInstance(trustCertType);
        //加载信任证书
        trustStore.load(resourceLoader(trustCerPath), trustCerPass.toCharArray());
        // 初始化信任库
        trustManagerFactory.init(trustStore);

        //双向校验---校验服务端证书是否在信任库
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        // 建立TLS连接
        SSLContext sslContext = SSLContext.getInstance("TLS");
        // 初始化SSLContext
        sslContext.init(keyManagers, trustManagers, new SecureRandom());

        //单项认证--不校验服务端证书是否在信任库
//        TrustStrategy anyTrustStrategy = (x509Certificates, s) -> true;
//        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, anyTrustStrategy).build();

        // INSTANCE 忽略域名检查
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
        //创建httpClient对象
        CloseableHttpClient closeableHttpClient = HttpClients
                .custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .setSSLHostnameVerifier(new NoopHostnameVerifier())
                .build();
        return closeableHttpClient;
    }

    /**
     * 读取文件信息
     *
     * @param fileFullPath
     * @return
     * @throws IOException
     */
    public static InputStream resourceLoader(String fileFullPath) throws IOException {
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        return resourceLoader.getResource(fileFullPath).getInputStream();
    }
}
  1. 编辑测试方法
//客户端通信证书路径
RestTemplateUtil.setClientCerPath("file:E:/Test_Dir/pfx/server-two.p12");;
//客户端通信证书密码
RestTemplateUtil.setClientCerPwd("12345678");
//客户端密钥库密码
RestTemplateUtil.setClientPass("12345678");
//客户端信任库路径
RestTemplateUtil.setTrustCerPath("file:E:/Test_Dir/pfx/trustKeys.p12");
//客户端信任库密码
RestTemplateUtil.setTrustCerPass("12345678");
try {
//生成认证RestTemplate类,后续按正常的RestTemplate的方法去调用即可
   RestTemplate  template = RestTemplateUtil.rsaSslRestTemplate();
} catch (Exception e) {
    e.printStackTrace();
}
  1. 按上一步骤的RestTemplate去调用server-one发现可以正常访问,即双向认证完成

此文仅做记录学习参考用,如有错误还望指正