IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 【无标题】Java 基于文件双向认证的 HTTPS 请求,支持 CICD -> 正文阅读

[网络协议]【无标题】Java 基于文件双向认证的 HTTPS 请求,支持 CICD

. 原理

双向验证即: 客户端与服务端在进行数据传输的时候双方都需要验证对方的身份。

在建立 Https 连接的工程中,握手的流程比单向认证多了几步。

  • 单向认证的过程

    客户端从服务器端下载服务器公钥证书进行验证,然后才建立安全通信通道

  • 双向认证的过程

    客户端出了需要从服务器端下葬服务器的公钥证书进行验证外,还需要把客户端的公钥证书上传到服务器给服务器端进行验证,等双方认证都认证通过之后,才会开始进行安全通信通道

1.1 单向认证流程

单向认证流程中,服务器端保存着公钥证书和私钥两个文件,整个握手过程如下:

  1. 客户端发青建立 HTTPS 连接请求,将 SSL 版本协议的信息发送给服务器端

  2. 服务器端将本机的公钥证书 ( server.crt ) 发送给客户端

  3. 客户端读取公钥证书 ( server.crt ) , 获取服务器端的公钥

  4. 客户端生成一个随机数(密钥R),用刚才得到的服务器公钥去加密这个随机数形成密文,发送给服务端;

  5. 服务端用自己的私钥(server.key)去解密这个密文,得到了密钥R

  6. 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。

1.2 双向认证流程

  1. 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务端;

  2. 服务器端将本机的公钥证书(server.crt)发送给客户端;

  3. 客户端读取公钥证书(server.crt),取出了服务端公钥;

  4. 客户端将客户端公钥证书(client.crt)发送给服务器端;

  5. 服务器端使用根证书(root.crt)解密客户端公钥证书,拿到客户端公钥;

  6. 客户端发送自己支持的加密方案给服务器端;

  7. 服务器端根据自己和客户端的能力,选择一个双方都能接受的加密方案,使用客户端的公钥加密后发送给客户端;

  8. 客户端使用自己的私钥解密加密方案,生成一个随机数R,使用服务器公钥加密后传给服务器端;

  9. 服务端用自己的私钥去解密这个密文,得到了密钥R

  10. 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。

2. cert 、 key 和 pem 文件区别

推荐阅读:常见证书格式和转换_justinjing的专栏-CSDN博客

我们当前获取有3个文件,分别是

  • ca-server.cert.pem

    我们请求到的服务器给颁布的公钥

  • vendor.my.cert.pem

    我们本地发送客户端的公钥 ( 需要发送到服务器端)

  • vendor.my.key.pem

    我们本地客户端的密钥

我们可以转换为我们自己想要的格式

所有的 key 密码推荐使用?changeit?默认值

先将 pem 转换为jks ,因为没有密钥,所以转换为 JKS 作为 trustStore 使用

keytool -import -noprompt -file ca-server.cert.pem -keystore ca-server.jks -storepass changeit 

而且我们仍要携带客户端的 vendor.my.cert.pem 推送给服务器,而 Java 客户端无法处理 pem文件,需要将其转换为 .der 文件或者 p12 文件。

可以通过如下指令

openssl pkcs12 -export -clcerts -in vendor.yundun.cert.pem -inkey vendor.yundun.key.pem -out vendor.yundun.p12
也可以将 pkcs12 转换为 JKS 不推荐
keytool -importkeystore -srckeystore vendor.my.p12 -srcstoretype PKCS12 -deststoretype JKS -destkeystore vendor.my.jks

Java 官方也不建议将 PKCS12 转换为 JKS

正在将密钥库 vendor.my.p12 导入到 vendor.my.jks...?输入目标密钥库口令:?输入源密钥库口令:?已成功导入别名 1 的条目。?已完成导入命令: 1 个条目成功导入, 0 个条目失败或取消

Warning:?JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore vendor.my.jks -destkeystore vendor.my.jks -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。

3 基于 JAVA 实现 HTTPS 双向绑定请求

3.1 基于内部代码实现

3.1.1 证书在 Jar 外部

使用 Java 代码实现 Https 双向绑定请求

    private final static String CLIENT_PFX_PATH = "/{your_path}/vendor.my.p12";    //客户端证书路径
   
    private final static String CLIENT_PFX_PWD = "password";    //客户端证书密码

    private final static String SERVER_PFX_PATH = "/{your_path}/ca-server.jks";    //服务器端证书路径
    
    private final static String SERVER_PFX_PWD = "password";    // 服务器端证书密码

        public static void sslRequestGet(String url) throws Exception {
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(getSSLFactory()).build();
        try {
            HttpGet httpget = new HttpGet(url);
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");//返回结果
                EntityUtils.consume(entity);
                System.out.println(jsonStr);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }



        public static SSLConnectionSocketFactory getSSLFactory() throws Exception{
      // 此时需要查看对应的文件格式,如果公钥密钥都存在,建议使用 PKCS12 标准格式
      // 如果只存在公钥,建议使用 JAVA 支持的 JKS 格式 ( PKCS12 必须要求公密钥)
      // 转换 command 在 #2
        KeyStore serverKeyStore = KeyStore.getInstance("jks");
        KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
        try (InputStream clientStream = new FileInputStream(CLIENT_PFX_PATH);
             InputStream serverStream = new FileInputStream(SERVER_PFX_PATH)){
            clientKeyStore.load(clientStream, CLIENT_PFX_PWD.toCharArray());
            serverKeyStore.load(serverStream, SERVER_PFX_PWD.toCharArray());
        }
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(clientKeyStore, CLIENT_PFX_PWD.toCharArray());
        KeyManager[] keyManagers = kmf.getKeyManagers();

        TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(serverKeyStore);
        TrustManager[] trustManagers = tmf.getTrustManagers();

        SSLContext sslcontext = SSLContext.getInstance("TLS");
        sslcontext.init(keyManagers, trustManagers, new java.security.SecureRandom());

        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext
                , new String[]{"TLSv1"}    // supportedProtocols ,这里可以按需要设置
                , null    // supportedCipherSuites
                , SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        return sslsf;
    }

基于纯内部代码实现, 其中各个公密钥的 Path 推荐放置在打包好的 Jar 文件外部

3.1.2 证书在 Jar 内部

这样可以将证书打包在 Jar 内部,更好在 K8S 或者 CICD 中部署继承。

java 代码

   private final static String CLIENT_PFX_PATH = "/{your_path}/vendor.my.p12";    //客户端证书路径
   
    private final static String CLIENT_PFX_PWD = "password";    //客户端证书密码

    private final static String SERVER_PFX_PATH = "/{your_path}/ca-server.jks";    //服务器端证书路径
    
    private final static String SERVER_PFX_PWD = "password";    // 服务器端证书密码

        public static void sslRequestGet(String url) throws Exception {
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(getSSLFactory()).build();
        try {
            HttpGet httpget = new HttpGet(url);
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");//返回结果
                EntityUtils.consume(entity);
                System.out.println(jsonStr);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }


        public static SSLConnectionSocketFactory getSSLFactory() throws Exception{
      // 此时需要查看对应的文件格式,如果公钥密钥都存在,建议使用 PKCS12 标准格式
      // 如果只存在公钥,建议使用 JAVA 支持的 JKS 格式 ( PKCS12 必须要求公密钥)
      // 转换 command 在 #2
        KeyStore serverKeyStore = KeyStore.getInstance(SERVER_PFX_TYPE);
        KeyStore clientKeyStore = KeyStore.getInstance(CLIENT_PFX_TYPE);

        try (InputStream clientStream = getClass().getClassLoader().getResourceAsStream(CLIENT_PFX_PATH);
             InputStream serverStream = getClass().getClassLoader().getResourceAsStream(SERVER_PFX_PATH)) {
            clientKeyStore.load(clientStream, CLIENT_PFX_PWD.toCharArray());
            serverKeyStore.load(serverStream, SERVER_PFX_PWD.toCharArray());
        }
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(clientKeyStore, CLIENT_PFX_PWD.toCharArray());
        KeyManager[] keyManagers = kmf.getKeyManagers();

        TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(serverKeyStore);
        TrustManager[] trustManagers = tmf.getTrustManagers();

        SSLContext sslcontext = SSLContext.getInstance("TLS");
        sslcontext.init(keyManagers, trustManagers, new java.security.SecureRandom());
·
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext
                , new String[]{"TLSv1"}    // supportedProtocols ,这里可以按需要设置
                , null    // supportedCipherSuites
                , SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        return sslsf;
    }

需要在?resources/httpsKeys?文件夹下存放相对应的文件

3.2 使用命令行指定文件

Java代码为:

    public static void request(String url) {

        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(SSLConnectionSocketFactory.getSystemSocketFactory()).build();

        HttpGet get = new HttpGet(url);
        System.out.println("trying to execute request");
        try (CloseableHttpResponse response = httpClient.execute(get)) {
            HttpEntity entity = response.getEntity();
            System.out.println(response.getStatusLine().getStatusCode());
            String s = EntityUtils.toString(entity);
            System.out.println(s);
            System.out.println("finished request");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 
java -Djavax.net.ssl.keyStore=./vendor.my.p12 -Djavax.net.ssl.keyStorePassword=changeit -Djavax.net.ssl.keyStoreType=PKCS12 -Djavax.net.ssl.trustStore=./ca-server.jks -Djavax.net.ssl.trustStorePassword=changeit  -jar https_test.jar 

其中只需要指定好各个文件的位置、密码以及类型,即可通过?SSLConnectionSocketFactory.getSystemSocketFactory()?实现

3.3 在 JVM LIB 中添加密钥

我们可以直接在 Java 中导入 ca-chain.cert.pem ,服务器的公钥,通过如下命令

不推荐

sudo keytool 
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-01-25 10:56:42  更:2022-01-25 10:57:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:46:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码