java连接MQTT+SSL服务器
摘自:https://www.jianshu.com/p/f498cd767d4b
java连接MQTT+SSL服务器
![](https://upload.jianshu.io/users/upload_avatars/17243965/af107d0c-4444-48c0-b306-51340c20cfc2.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96/format/webp)
策马踏清风IP属地: 福建
2021.02.09 18:27:11字数 744阅读 4,328
java
用ssl
加密方式连接mqtt
服务器。其它ssl
加密的也可以参考,SSLSocketFactory
获取部分都是一样的。踩了很多坑,根据生成工具不同(openssl
和keytool
)以及秘钥文件编码不同有若干种方法。这里把自己遇到的所有情况都统一记录一下。
一、连接MQTT服务器
不加密的连接方式之前有写过,就不赘述了,这里列出不同的地方
mqttClient = new MqttClient(host, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true);
// 这里多了一步设置SSLSocketFactory的步骤
options.setSocketFactory(SslUtil.getSocketFactoryByCert(caPath,certPath,privateKeyPath, privateKeyPwd));
SSLSocketFactory获取方式有两种:
- 通过
CA
证书、客户端证书、客户端私钥、私钥密码 获取(使用openssl
生成的,keytool
能生成证书,但是不能直接导出秘钥文件) - 直接通过
keystore
和truststore
获取(通过keytool
生成的)
读取证书和秘钥也有两种方式(证书获取的方式)
- 使用
bcpkix-jdk15on
包提供的方法,需要引包 - 使用原生方法,但是不支持直接读取
pom
秘钥文件,需要先把文件PKCS8
编码一下。(编码方法在openssl
的文章里)
稍微解释一下上面的两种方式
- 第一种,通过证书的方式
CA
证书是用来验证服务端发过来的证书,因为这里是双向认证,所以需要CA
证书来认证服务端发过来的是否是合法证书。- 客户端证书,发给服务端,让服务端验证的。(需要用
CA
证书签发,这样服务端那边才能用CA
证书验证合法) - 客户端私钥,服务端拿到客户端证书后会用证书里的公钥加密信息发过来,需要用私钥解密拿到原信息
- 私钥密码,
openssl
生成私钥的时候设置的密码(具体生成方式之前的文章有)
- 第二种,通过
keystore
和truststore
keystore
是用jdk
自带的工具keytool
生成的秘钥和证书管理库,用来保存自己的秘钥和证书。需要用keytool
生成并导入客户端的证书和秘钥。具体使用之前有文章可以参考。truststore
本质也是keystore
,只是里面存的是受信的证书。用来验证服务端证书是否可信,将CA
导入即可- 第一种方式本质也是通过
keystore
和truststore
验证,只不过导入的步骤用代码实现了,第二种方式使用命令实现的。
二、SslUtil具体实现
- 导入依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.47</version>
</dependency>
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
/***
* 两种方式验证
* @author colin
* @date 2021-02-03 14:39
* @since 1.0.0
*/
public class SslUtil {
/**
* 用证书和私钥配置sslContext
*
* @param caCrtFile
* CA证书(验证连接)
* @param crtFile
* 发给对方的证书
* @param keyFile
* pem 私钥(请求连接的消息是用公钥加密的,需要用私钥解密)
* @param password
* 私钥密码
* @return
* @throws Exception
*/
public static SSLSocketFactory getSocketFactoryByCert(final String caCrtFile, final String crtFile,
final String keyFile, final String password) throws Exception {
Security.addProvider(new BouncyCastleProvider());
// 加载CA证书(用于验证的根证书)
PEMReader reader =
new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
X509Certificate caCert = (X509Certificate)reader.readObject();
reader.close();
// 加载自己的证书,用于发送给客户端
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
X509Certificate cert = (X509Certificate)reader.readObject();
reader.close();
// 加载私钥
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get