* 基于springboot 根据ca.crt、client.crt和client.key文件实现与服务端https通讯
* 服务端提供了三个文件需要进行TLS方式通讯,三个文件分别为ca.crt、client.crt、client.key:
* ca.crt 为服务端证书
* client.crt 为服务端分配给客户端的证书
* client.key 为服务端分配给客户端证书私钥
引入pom文件:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
实现:
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import sun.misc.BASE64Decoder;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
@Component
public class CertificateHttpsTools {
private static Logger logger = LogManager.getLogger(CertificateHttpsTools.class);
public static final String CA_PATH = "C:\\Users\\kff\\Documents\\WeChat Files\\wxid_dxcp7wdj1nfg22\\FileStorage\\File\\2024-01\\certify_1228(1)\\certify_1228\\root\\ca.crt";
public static final String CRT_PATH = "C:\\Users\\kff\\Documents\\WeChat Files\\wxid_dxcp7wdj1nfg22\\FileStorage\\File\\2024-01\\certify_1228(1)\\certify_1228\\client\\client.crt";
public static final String KEY_PATH = "C:\\Users\\kff\\Documents\\WeChat Files\\wxid_dxcp7wdj1nfg22\\FileStorage\\File\\2024-01\\certify_1228(1)\\certify_1228\\client\\client.key";
public static final String PASSWORD = "changeit";
/**
* 在这段代码中,首先加载了CA(证书颁发机构)的证书,用于验证服务器的身份。然后,加载了客户端的证书和私钥,用于让服务器验证客户端的身份。
*
* 然后,这段代码创建了一个SSL上下文,并使用先前加载的密钥和信任管理器初始化它。最后,使用这个SSL上下文创建了一个SSL连接套接字工厂。
*
* 注意,这个代码片段没有展示如何获取客户端私钥的具体实现,这可能是在getPrivateKey方法中完成的。此外,这个代码片段也没有处理可能的异常和错误,例如文件不存在、证书无效等。
* @return
* @throws Exception
*/
public static SSLConnectionSocketFactory getSSLSocktetBidirectional() throws Exception {
SSLConnectionSocketFactory sslsf = null;
try {
//CA证书用于对服务器进行身份验证 CA certificate is used to authenticate server
//加载CA证书,用于验证服务器的身份,X.509证书包含公钥、身份信息和签名信息,当用户与服务器交互时,将生成加密密钥对,并将其散列,再将其与数字证书请求一起发送到证书颁发机构(CA)。CA是受信任的第三方实体,它将自己的公钥绑定到数字证书上。根据X.509标准,数字证书包含特定的数据,例如用户设备的位置、证书的序列号、CA的名称、使用的特定加密算法等等。
CertificateFactory cAf = CertificateFactory.getInstance("X.509");
FileInputStream caIn = new FileInputStream(CA_PATH);
X509Certificate ca = (X509Certificate) cAf.generateCertificate(caIn);
KeyStore caKs = KeyStore.getInstance("JKS");
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(caKs);
//加载客户端密钥和证书 并发送到服务器,以便它可以对我们进行身份验证 client key and certificates are sent to server so it can authenticate us
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream crtIn = new FileInputStream(CRT_PATH);
X509Certificate caCert = (X509Certificate) cf.generateCertificate(crtIn);
crtIn.close();
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("certificate", caCert);
ks.setKeyEntry("private-key", getPrivateKey(KEY_PATH), PASSWORD.toCharArray(), new java.security.cert.Certificate[]{caCert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ks, PASSWORD.toCharArray());
//最后,创建SSL sockets的工厂类finally, create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
sslsf = new SSLConnectionSocketFactory(context, null, null,
NoopHostnameVerifier.INSTANCE);
} catch (Exception e) {
logger.error("证书加载失败");
e.printStackTrace();
}
return sslsf;
}
private static PrivateKey getPrivateKey(String path) throws Exception {
BASE64Decoder decoder = new BASE64Decoder();
byte[] buffer = decoder.decodeBuffer(getPem(path));
// 创建PKCS8EncodedKeySpec对象,用于封装解码后的密钥数据
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
// 创建KeyFactory对象,指定密钥算法为RSA(如果是EC算法,则指定为"EC")
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 生成私钥对象
return keyFactory.generatePrivate(keySpec);
}
private static String getPem(String path) throws Exception {
FileInputStream fin = new FileInputStream(path);
BufferedReader br = new BufferedReader(new InputStreamReader(fin));
String readLine = null;
StringBuilder sb = new StringBuilder();
while ((readLine = br.readLine()) != null) {
if (readLine.charAt(0) == '-') {
continue;
} else {
sb.append(readLine);
sb.append('\r');
}
}
fin.close();
return sb.toString();
}
public static String httpsPost(String url, String jsonParam) throws IOException {
CloseableHttpClient httpClient = null;
String result = null;
logger.info("请求数据:" + jsonParam);
try {
//加载证书
SSLConnectionSocketFactory sslsf = getSSLSocktetBidirectional();
//设置认证信息到httpclient
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
} catch (Exception e) {
logger.error("证书读取失败");
e.printStackTrace();
}
// 根据默认超时限制初始化requestConfig
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(25000).setConnectTimeout(25000).build();
//post通讯初始化
HttpPost httpPost = new HttpPost(url);
// 设置报文头 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
//添加请求报文体
StringEntity reqentity = new StringEntity(jsonParam, "UTF-8");
httpPost.setEntity(reqentity);
// 设置请求器的配置
httpPost.setConfig(requestConfig);
try {
CloseableHttpResponse response = null;
InputStream reqin = null;
try {
//打印请求报文体
reqin = httpPost.getEntity().getContent();
String reqBbody = StreamUtils.copyToString(reqin, StandardCharsets.UTF_8);
logger.info("请求数据:" + reqBbody);
//与服务端通讯
response = httpClient.execute(httpPost);
//打印通讯状态
logger.info(JSONObject.toJSONString(response.getStatusLine()));
HttpEntity entity = response.getEntity();
InputStream in = null;
try {
in = entity.getContent();
String rspBbody = StreamUtils.copyToString(in, StandardCharsets.UTF_8);
logger.info("应答为:" + rspBbody);
in.close();
result = rspBbody;
// result = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
logger.error("服务端应答信息读取失败");
e.printStackTrace();
} finally {
if (in != null) {
in.close();
}
}
} catch (IOException e) {
logger.error("与服务端通讯失败");
e.printStackTrace();
} finally {
try {
//释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
if (reqin != null) {
reqin.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} finally {
httpPost.abort();
}
return result;
}
public static String httpsGet(String url, String param) throws IOException {
CloseableHttpClient httpClient = null;
String result = null;
logger.info("请求数据:" + param);
try {
//加载证书
SSLConnectionSocketFactory sslsf = getSSLSocktetBidirectional();
//设置认证信息到httpclient
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
} catch (Exception e) {
logger.error("证书读取失败");
e.printStackTrace();
}
// 根据默认超时限制初始化requestConfig
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(25000).setConnectTimeout(25000).build();
//get通讯初始化
//HttpGet httpGet = new HttpGet(url + "?" + param);
HttpGet httpGet = new HttpGet(url);
// 设置报文头 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
httpGet.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
// 设置请求器的配置
httpGet.setConfig(requestConfig);
try {
CloseableHttpResponse response = null;
InputStream reqin = null;
try {
//与服务端通讯
response = httpClient.execute(httpGet);
//打印通讯状态
logger.info(JSONObject.toJSONString(response.getStatusLine()));
HttpEntity entity = response.getEntity();
InputStream in = null;
try {
in = entity.getContent();
String rspBbody = StreamUtils.copyToString(in, StandardCharsets.UTF_8);
logger.info("应答为:" + rspBbody);
in.close();
result = rspBbody;
// result = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
logger.error("服务端应答信息读取失败");
e.printStackTrace();
} finally {
if (in != null) {
in.close();
}
}
} catch (IOException e) {
logger.error("与服务端通讯失败");
e.printStackTrace();
} finally {
try {
//释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
if (reqin != null) {
reqin.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} finally {
httpGet.abort();
}
return result;
}
public static void main(String[] args) {
try {
httpsPost("https://10.44.111.113:8443/hi_", "go go go");
// httpsGet("https://10.44.111.113:8443/hi", "");
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
参考文章:https://www.cnblogs.com/telwanggs/p/16561913.html