一:使用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;
}
}