一文详解如何通过Java实现SSL交互功能

 更新时间:2023年04月26日 14:25:20   作者:上官冰  
这篇文章主要为大家详细介绍了如何通过Java实现SSL交互功能,文中的示例代码讲解详细,对我们的学习或工作有一定的帮助,需要的可以参考一下

创建证书

因为要产生 key 信任库,要求的密码至少的 6位数,所以密码设为 123456

  • 首先 生成 根密钥和根证书
  • 然后 生成 客户端密钥 和 客户端证书,使用根证书对应客户端证书签名,将根证书和签名客户端证书添加到密钥库中
  • 最后 同上生成 服务端签名证书,并将根证书和服务端证书添加到 密钥库中。

对应的脚本如下所示:

#!/bin/bash
# 加个前缀
PFX='file'
# Password 这里只能使用单引号
PASS='123456'
echo "---- 产生根相关文件 ----"
echo "创建自签名的根密钥"
openssl genrsa -out ${PFX}.rootkey.pem 2048
echo "生成根证书"
openssl req -x509 -new -key ${PFX}.rootkey.pem -out ${PFX}.root.crt -subj "/C=CN/ST=GD/L=GZ/O=RootCA/OU=RootCA/CN=RootCA"
echo "----- 产生客户端相关文件 -----"
echo "生成客户端密钥"
openssl genrsa -out ${PFX}.clientkey.pem 2048
echo "生成客户端证书请求文件,使用根证书进行签发"
openssl req -new -key ${PFX}.clientkey.pem -out ${PFX}.client.csr -subj "/C=CN/ST=GD/L=GZ/O=BMW/OU=Vehicle/CN=Vehicle1"
echo "用根证书来签发客户端请求文件,生成客户端证书client.crt"
openssl x509 -req -in ${PFX}.client.csr -CA ${PFX}.root.crt -CAkey ${PFX}.rootkey.pem -CAcreateserial -days 3650 -out ${PFX}.client.crt
echo "打包客户端资料为pkcs12格式(client.pkcs12)"
openssl pkcs12 -export -in ${PFX}.client.crt -inkey clientkey.pem -out ${PFX}.client.pkcs12 -passin "pass:$PASS" -passout "pass:$PASS"
echo "生成信任客户端的keystore,把根证书以及需要信任的客户端的证书添加到这个keystore"
keytool -importcert -alias ca -file ${PFX}.root.crt -keystore ${PFX}.clienttrust.jks -storepass "$PASS" <<EOF
是
EOF
keytool -importcert -alias clientcert -file ${PFX}.client.crt -keystore ${PFX}.clienttrust.jks -storepass "$PASS"
echo "--------产生服务端相关文件----"
echo "生成服务器端的密匙"
openssl genrsa -out ${PFX}.serverkey.pem 2048
echo "生成服务器端证书的请求文件。请求根证书来签发"
openssl req -new -key ${PFX}.serverkey.pem -out ${PFX}.server.csr -subj "/C=CN/ST=GD/L=GZ/O=BMW/OU=IT/CN=Broker"
echo "用根证书来签发服务器端请求文件,生成服务器端证书server.crt"
openssl x509 -req -in ${PFX}.server.csr -CA ${PFX}.root.crt -CAkey ${PFX}.rootkey.pem -CAcreateserial -days 3650 -out ${PFX}.server.crt
echo "打包服务器端资料为pkcs12格式(server.pkcs12 )"
openssl pkcs12 -export -in ${PFX}.server.crt -inkey ${PFX}.serverkey.pem -out ${PFX}.server.pkcs12 -passin "pass:$PASS" -passout "pass:$PASS"
echo "生成信任服务器端的keystore,把根证书以及需要信任的服务端的证书添加到这个keystore"
keytool -importcert -alias ca -file ${PFX}.root.crt -keystore ${PFX}.servertrust.jks -storepass "$PASS" <<EOF
是
EOF
keytool -importcert -alias servercert -file ${PFX}.server.crt -keystore ${PFX}.servertrust.jks -storepass "$PASS"

生成的文件目录,如下图所示:

#编码

编写 SSL Server

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import java.net.Socket;

public class SSLServer {
    private SSLServerSocket sslServerSocket;

    public static void main(String[] args) throws Exception {
        SSLServer server = new SSLServer();
        server.initTestServer();
        server.process();
    }

    private SSLServerSocket initTestServer() throws Exception {
    // ssl-cert 放在 测试 resources 目录下
        String certDic = SSLServer.class.getClassLoader().getResource("ssl-cert").getPath();
        sslServerSocket = initSocket(certDic + "/file.server.pkcs12",
                certDic + "/file.clienttrust.jks",
                "123456");
    // 如果为 false 表示单向认证,否则为双向认证
        sslServerSocket.setNeedClientAuth(false);
        System.out.println("Server test initialted!");
        return sslServerSocket;
    }

    private SSLServerSocket initSocket(String keyPath, String trustPath, String password) throws Exception {
        SSLContext context = SSLContext.getInstance("TLSv1.2");
        char[] keystorePass = password.toCharArray();
        context.init(SSLUtils.creatKey(keyPath, keystorePass).getKeyManagers(),
                SSLUtils.creatTrustJks(trustPath, keystorePass).getTrustManagers(),
                null);
        return (SSLServerSocket) context.getServerSocketFactory().createServerSocket(9999);
    }

//服务端回复客户端
    private void process() throws Exception {
        String bye = "Hello, I am Server!";
        while (true) {
            Socket socket = sslServerSocket.accept();
            System.out.println("Received: " + SSLUtils.read(socket));
            SSLUtils.write(bye, socket);
        }
    }
}

编写 SSL Client

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

public class SSLClient {
    private SSLSocket sslSocket;

    public static void main(String[] args) throws Exception {
        SSLClient client = new SSLClient();
        client.init();
        client.process();
    }

    private void init() throws Exception {
     // ssl-cert 放在 测试 resources 目录下
        String certDic = this.getClass().getClassLoader().getResource("ssl-cert").getPath();
        initSocket(certDic + "/file.server.pkcs12", certDic + "/file.servertrust.jks", "123456");
        System.out.println("Client initiated.");
    }

    private void initSocket(String keystorePath, String trustStorePath, String password) throws Exception {
        char[] keystorePass = password.toCharArray();

        KeyManager[] keyManagers = null;
        KeyManagerFactory keyManagerFactory = SSLUtils.creatKey(keystorePath, keystorePass);
        if (keyManagerFactory != null) {
            keyManagers = keyManagerFactory.getKeyManagers();
        }

        TrustManager[] trustManagers = null;
        TrustManagerFactory trustManagerFactory = SSLUtils.creatTrustJks(trustStorePath, keystorePass);
        if (trustManagerFactory != null) {
            trustManagers = trustManagerFactory.getTrustManagers();
        }

        SSLContext context = SSLContext.getInstance("TLSv1.2");
        context.init(keyManagers, trustManagers, null);

        String host = "127.0.0.1";
        sslSocket = (SSLSocket) context.getSocketFactory().createSocket(host, 9999);
    }

    public void process() throws Exception {
        String hello = "Client Hello";
        SSLUtils.write(hello, sslSocket);
        System.out.println(SSLUtils.read(sslSocket));
    }

}

通用编码

import org.apache.commons.lang3.StringUtils;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;

public class SSLUtils {

    public static void write(String message, Socket sslSocket) throws Exception {
        OutputStream out = sslSocket.getOutputStream();
        byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
        out.write(messageBytes, 0, messageBytes.length);
        out.flush();
    }

    public static String read(Socket sslSocket) throws Exception {
        InputStream in = sslSocket.getInputStream();
        byte[] buffer = new byte[50];
        in.read(buffer);
        return new String(buffer);
    }

    public static KeyManagerFactory creatKey(String keystorePath, char[] keystorePass) throws Exception {
        if (StringUtils.isEmpty(keystorePath)) {
            return null;
        }
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
        kmf.init(loadKeyStore(keystorePath, keystorePass), keystorePass);
        return kmf;
    }

    public static TrustManagerFactory creatTrustJks(String trustClientKeystorePath, char[] keystorePass) throws Exception {
        if (StringUtils.isEmpty(trustClientKeystorePath)) {
            return null;
        }
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
        tmf.init(loadKeyStore(trustClientKeystorePath, keystorePass));
        return tmf;
    }

    private static KeyStore loadKeyStore(String keystorePath, char[] keystorePass) throws Exception {
        KeyStore serverKeyStore = KeyStore.getInstance(StringUtils.substringAfterLast(keystorePath, "."));
        serverKeyStore.load(new FileInputStream(keystorePath), keystorePass);
        return serverKeyStore;
    }
}

测试

启动 SSLServer 和 SSLClient 可以得到的结果如下所示:

使用 wireshark 抓包如下所示:

这就是对应的 SSL 连接

到此这篇关于一文详解如何通过Java实现SSL交互功能的文章就介绍到这了,更多相关Java实现SSL交互内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 在Web项目中手机短信验证码实现的全过程记录

    在Web项目中手机短信验证码实现的全过程记录

    这篇文章主要给大家介绍了关于在Web项目中实现短信验证码的全过程记录,文中通过示例代码介绍的非常详细,在文末跟大家提供了源码下载,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-12-12
  • JPA 通过Specification如何实现复杂查询

    JPA 通过Specification如何实现复杂查询

    这篇文章主要介绍了JPA 通过Specification如何实现复杂查询,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • springboot 日志彩色消失的2种解决方案

    springboot 日志彩色消失的2种解决方案

    这篇文章主要介绍了springboot 日志彩色消失的2种解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • springboot中如何通过cors协议解决跨域问题

    springboot中如何通过cors协议解决跨域问题

    这篇文章主要介绍了springboot中通过cors协议解决跨域问题,cors是一个w3c标准,它允许浏览器(目前ie8以下还不能被支持)像我们不同源的服务器发出xmlHttpRequest请求,我们可以继续使用ajax进行请求访问。具体内容详情大家跟随脚本之家小编一起学习吧
    2018-05-05
  • 一文搞懂Mybatis-plus的分页查询操作

    一文搞懂Mybatis-plus的分页查询操作

    说起分页机制,相信我们程序员都不陌生,今天,我就给大家分享一下Mybatis-plus的分页机制,供大家学习和Copy,感兴趣的可以了解一下
    2022-06-06
  • 一文详解Java属性为什么不能是is开头的boolean

    一文详解Java属性为什么不能是is开头的boolean

    在Java实体类定义中,boolean类型的属性命名常引发争议,阿里巴巴Java开发手册建议避免使用is作为布尔类型属性的前缀,原因在于当实体类被序列化或反序列化时,基于JavaBean规范的框架可能会移除或忽略is,导致不一致的字段名,文中介绍的非常详细,需要的朋友可以参考下
    2024-10-10
  • Mybatis批量修改时出现报错问题解决方案

    Mybatis批量修改时出现报错问题解决方案

    这篇文章主要介绍了Mybatis批量修改时出现报错问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • SpringBoot详细讲解日志文件

    SpringBoot详细讲解日志文件

    Spring Boot默认使用SLF4J+Logback 记录日志,并提供了默认配置,即使我们不进行任何额外配,也可以使用SLF4J+Logback进行日志输出
    2022-06-06
  • SpringCloud创建多模块项目的实现示例

    SpringCloud创建多模块项目的实现示例

    ,Spring Cloud作为一个强大的微服务框架,提供了丰富的功能和组件,本文主要介绍了SpringCloud创建多模块项目的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • 聊一聊jdk1.8中的ArrayList 底层数组是如何扩容的

    聊一聊jdk1.8中的ArrayList 底层数组是如何扩容的

    这篇文章主要介绍了聊一聊jdk1.8中的ArrayList 底层数组是如何扩容的,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08

最新评论