基于springboot 根据ca.crt、client.crt和client.key文件实现与服务端https双向认证通讯

* 基于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

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
首先,需要在服务端和客户端生成自己的证书和私钥,并将证书和私钥放到对应的服务器上。 接下来是配置过程: 1. 配置服务端 a. 在 nginx 配置中增加以下内容: ``` ssl_certificate /path/to/server.crt; ssl_certificate_key /path/to/server.key; ssl_client_certificate /path/to/ca.crt; ssl_verify_client on; ``` 其中,`ssl_certificate` 和 `ssl_certificate_key` 分别指定服务端证书和私钥的路径,`ssl_client_certificate` 指定 CA 证书的路径,`ssl_verify_client on` 开启双向认证。 b. 在 server 配置中增加以下内容: ``` listen 443 ssl; ``` 2. 配置客户端 a. 在 nginx 配置中增加以下内容: ``` ssl_certificate /path/to/client.crt; ssl_certificate_key /path/to/client.key; ssl_client_certificate /path/to/ca.crt; ssl_verify_client on; ``` 其中,`ssl_certificate` 和 `ssl_certificate_key` 分别指定客户端证书和私钥的路径,`ssl_client_certificate` 指定 CA 证书的路径,`ssl_verify_client on` 开启双向认证。 b. 在 server 配置中增加以下内容: ``` location / { proxy_pass https://server-ip:443; } ``` 其中,`server-ip` 指定服务端的 IP 地址。 3. 配置证书链校验 在上述的配置中,配置了 `ssl_client_certificate`,这个证书是 CA 证书,如果需要使用证书链校验,需要将 CA 的证书链加入到 `ca.crt` 文件中。 将 CA 证书链加入到 `ca.crt` 文件中的方法是:将所有的 CA 证书用文本编辑器打开,依次将其内容拷贝到 `ca.crt` 文件中,即可完成证书链的配置。 注意:证书链中的证书顺序很重要,必须按照从下往上的顺序排列。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值