以管理员身份打开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
把server-one服务的证书和信任库放到对应目录下
修改配置文件,开启应用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
修改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>
启动项目,服务端开启认证完成,通过地址去访问会发现报错--不开启认证可以访问的情况下
编写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();
}
}
编辑测试方法
//客户端通信证书路径
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();
}
按上一步骤的RestTemplate去调用server-one发现可以正常访问,即双向认证完成
此文仅做记录学习参考用,如有错误还望指正
参考文章:
1.SpringBoot服务间使用自签名证书实现https双向认证 - 膜拜狂神cdy - 博客园 (cnblogs.com)文章来源:https://www.toymoban.com/news/detail-616845.html
2.(2条消息) spring boot 使用RestTemplate通过证书认证访问https实现SSL请求_踩到最基点的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-616845.html
到了这里,关于JAVA使用RestTemplate类实现SSL双向/单向认证(国际)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!