JAVA 使用SSL连接EMQX,openssl自签名证书(已验证)

一:使用openssl生成ca证书,服务端证书,客户端证书

1.openssl生成证书脚本

::使用说明
::1.环境配置:环境变量中包含了 OpenSSL 的路径
::2.配置文件:确保 openssl.cnf 配置文件在同一目录下或更新脚本中的 OPENSSL_CONF 变量以指向正确的路径
::3.权限配置:管理员权限(写入到受保护的目录时)
::4.结果目录:当该bat脚本执行完成后,生成文件目录如下
::├── ca.key
::├── ca.crt
::├── server.key
::├── server.csr
::└── server.crt
::├── client.key
::├── client.csr
::└── client.crt
::DAY_LENGTH 证书有效时长 单位天

@echo off
setlocal

set OPENSSL_CONF=openssl.cnf
set DAY_LENGTH=3650

echo Generating Root CA Key...
openssl genrsa -out ca.key 2048

echo Generating Root CA Certificate...
openssl req -x509 -new -nodes -key ca.key -sha256 -days %DAY_LENGTH% -out ca.crt -extensions v3_ca

echo Generating Server Key...
openssl genrsa -out server.key 2048

echo Generating Server CSR...
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=ShangHai/L=ShangHai/O=test/CN=127.0.0.1"

echo Signing Server Certificate with Root CA...
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days %DAY_LENGTH% -sha256 -extfile %OPENSSL_CONF% -extensions req_ext

echo Generating Client Key...
openssl genrsa -out client.key 2048

echo Generating Client CSR...
openssl req -new -key client.key -out client.csr -subj "/C=CN/ST=ShangHai/L=ShangHai/O=test/CN=127.0.0.1"

echo Signing Client Certificate with Root CA...
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days %DAY_LENGTH% -sha256 -extfile %OPENSSL_CONF% -extensions req_ext


::输出server.crt: OK,表示验证成功 
echo Verifying Server Certificate...
openssl verify -CAfile ca.crt server.crt

::输出client.crt: OK,表示验证成功 
echo Verifying Client Certificate...
openssl verify -CAfile ca.crt client.crt

echo All tasks completed.
endlocal

2.openssl.cnf 配置文件

在使用时请确认你的

[ alt_names ]
IP.1                 = 127.0.0.1

中IP配置为你的服务器IP地址

[ req ]
default_bits        = 2048
prompt              = no
default_md          = sha256
distinguished_name  = req_distinguished_name
req_extensions      = req_ext
x509_extensions = v3_req

[ req_distinguished_name ]
countryName = CN
stateOrProvinceName = ShangHai
localityName = ShangHai
organizationName = test
commonName = test
CN                   = 127.0.0.1

[ req_ext ]
subjectAltName      = @alt_names

[ v3_req ]
subjectAltName      = @alt_names

[ alt_names ]
IP.1                 = 127.0.0.1

[ v3_ca ]
subjectAltName      = @alt_names
basicConstraints    = CA:TRUE
keyUsage            = digitalSignature, keyEncipherment, keyCertSign, cRLSign

二:打开EMQX的管理台,将文件依次配置

三:JAVA代码 

1.pom引入依赖

        我这边使用的是mqtt5版本,如需使用3版本,请自行切换依赖

   <dependency>
      <groupId>org.eclipse.paho</groupId>
      <artifactId>org.eclipse.paho.mqttv5.client</artifactId>
      <version>1.2.5</version>
    </dependency>

    <dependency>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bcpkix-jdk15on</artifactId>
      <version>1.70</version>
    </dependency>

2.连接启动类

 使用前请将IP:port改成你的服务器地址

package org.example;

import org.eclipse.paho.mqttv5.client.MqttClient;
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions;
import org.eclipse.paho.mqttv5.client.persist.MemoryPersistence;
import org.eclipse.paho.mqttv5.common.MqttException;
import org.eclipse.paho.mqttv5.common.MqttMessage;

import javax.net.ssl.SSLSocketFactory;


public class mqttV5SSL {
    public static void main(String[] args) throws Exception {
        String topic = "mqtt/test123";
        String username = "test";
        String password = "test";
        String clientId = "ssl-v5-test";
        String content = "Hello MQTT";

        // 设置 SSL/TLS 连接地址
        String broker = "ssl://IP:port";

        String caFilePath = "D:\\sslTest1\\ca.crt";
        String clientCrtFilePath = "D:\\sslTest1\\client.crt";
        String clientKeyFilePath = "D:\\sslTest1\\client.key";
        int qos = 1;
        try {
            MqttClient client = new MqttClient(broker, clientId, new MemoryPersistence());
            MqttConnectionOptions options = new MqttConnectionOptions();
            options.setUserName(username);
            options.setPassword(password.getBytes());
            SSLSocketFactory socketFactory = SSLUtils.getSocketFactory(caFilePath, clientCrtFilePath, clientKeyFilePath, "");
            options.setSocketFactory(socketFactory);
            options.setConnectionTimeout(30);
            options.setKeepAliveInterval(5);
            // 连接
            client.connect(options);
            // 创建消息并设置 QoS
            MqttMessage message = new MqttMessage(content.getBytes());
            message.setQos(qos);
            // 发布消息
            client.publish(topic, message);
//            client.subscribe("$file/#");
            client.subscribe("topic/test",1);
//            client.setCallback(new MqttMessageCallback());
            System.out.println("Message published");
            System.out.println("topic: " + topic);
            System.out.println("message content: " + content);
            // 关闭连接
//                client.disconnect();
            // 关闭客户端
//                client.close();
        } catch (MqttException e) {
            throw new RuntimeException(e);
        }
    }
}

3.SSLUtils类

package org.example;

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;


import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Collection;

public class SSLUtils {

    public static SSLSocketFactory getSingleSocketFactory(final String caCrtFile) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        // Load CA certificates
        KeyStore caKs = loadCAKeyStore(caCrtFile);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(caKs);
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, tmf.getTrustManagers(), null);
        return sslContext.getSocketFactory();
    }


    public static SSLSocketFactory getSocketFactory(final String caCrtFile,
                                                    final String crtFile, final String keyFile, final String password)
            throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        // Load CA certificates
        KeyStore caKs = loadCAKeyStore(caCrtFile);
        // Load client certificate chain and key
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(null, null);
        // Load the entire client certificate chain
        Certificate[] chain;
        try (FileInputStream fis = new FileInputStream(crtFile)) {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            Collection<? extends Certificate> certs = cf.generateCertificates(fis);
            chain = certs.toArray(new Certificate[0]);
        }
        try (PEMParser pemParser = new PEMParser(new FileReader(keyFile))) {
            Object object = pemParser.readObject();
            // 检查读取的对象类型并相应处理
            if (object instanceof PEMKeyPair) {
                // 如果是PEMKeyPair,说明包含公钥和私钥对
                JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
                KeyPair key = converter.getKeyPair((PEMKeyPair) object);
                ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), chain);
            } else if (object instanceof PrivateKeyInfo) {
                // 如果是PrivateKeyInfo,说明只有私钥
                JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
                PrivateKey privateKey = converter.getPrivateKey((PrivateKeyInfo) object);
                // 注意:此处设置KeyEntry时没有公钥链,如果你需要公钥链,请确保链信息是正确的
                ks.setKeyEntry("private-key",privateKey, password.toCharArray(), chain); // 或者适当的chain,如果有的话
            } else {
                throw new IllegalArgumentException("Unsupported object type in PEM file.");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // Set up key managers and trust managers
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
        tmf.init(caKs);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, password.toCharArray());
        // finally, create SSL socket factory
        SSLContext context = SSLContext.getInstance("TLSv1.2");
        context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        return context.getSocketFactory();
    }

    private static KeyStore loadCAKeyStore(String caCrtFile) throws Exception {
        KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
        caKs.load(null, null);
        try (FileInputStream fis = new FileInputStream(caCrtFile)) {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            Collection<? extends Certificate> caCerts = cf.generateCertificates(fis);
            int certIndex = 1;
            for (Certificate caCert : caCerts) {
                String alias = "ca-certificate-" + certIndex++;
                caKs.setCertificateEntry(alias, caCert);
            }
        }
        return caKs;
    }
}

四:连接测试

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值