日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

java nio ssl_java连接MQTT+SSL服务器

發布時間:2023/12/15 c/c++ 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java nio ssl_java连接MQTT+SSL服务器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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具體實現

導入依賴

org.bouncycastle

bcpkix-jdk15on

1.47

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(keyFile)))),

() -> password.toCharArray());

KeyPair key = (KeyPair)reader.readObject();

reader.close();

// 用CA證書創建TrustManagerFactory

KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());

caKs.load(null, null);

caKs.setCertificateEntry("ca-certificate", caCert);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

tmf.init(caKs);

// 用證書和私鑰創建KeyManagerFactory

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

ks.load(null, null);

ks.setCertificateEntry("certificate", cert);

ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(),

new java.security.cert.Certificate[] {cert});

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

kmf.init(ks, password.toCharArray());

SSLContext context = SSLContext.getInstance("TLSv1");

// kmf用于發送關鍵信息讓服務端校驗,tmf用于校驗服務端的證書。雙向認證

context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());

return context.getSocketFactory();

}

/**

* 通過keyStore加載

*

* @param keyStorePath

* keystore路徑(保存自己的秘鑰和證書)

* @param trustKeyStorePath

* truststore路徑(保存受信的證書)

* @param ksPass

* keystore密碼

* @param tsPass

* truststore密碼

* @return

* @throws Exception

*/

public static SSLSocketFactory getSocketFactoryByKeystore(String keyStorePath, String trustKeyStorePath,

String ksPass, String tsPass) throws Exception {

// keytool生成的keystore的類型就是JKS

KeyStore keyStore = KeyStore.getInstance("JKS");

KeyStore trustKeyStore = KeyStore.getInstance("JKS");

// 通過密碼加載keystore

keyStore.load(new FileInputStream(keyStorePath), ksPass.toCharArray());

// 加載trustKeyStore

trustKeyStore.load(new FileInputStream(trustKeyStorePath), tsPass.toCharArray());

// 創建管理JKS密鑰庫的密鑰管理器 (SunX509)

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

// 使用密鑰內容源初始化此工廠。 提供者通常使用 KeyStore 來獲取在安全套接字協商期間所使用的密鑰內容

kmf.init(keyStore, ksPass.toCharArray());

// SunX509

TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

tmFactory.init(trustKeyStore);

// 初始sslcontext

SSLContext sslContext = SSLContext.getInstance("SSLv3");

// SSLContext sslContext = SSLContext.getInstance("TLS");

sslContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), new SecureRandom());

return sslContext.getSocketFactory();

}

}

三、不引包的方式

將pem秘鑰文件pkcs8編碼

openssl pkcs8 -topk8 -in client.private.pem -out pkcs8.client.private.pem -nocrypt

代碼

/**

* 用證書和私鑰配置sslContext

*

* @param caCrtFile

* CA證書(驗證連接)

* @param crtFile

* 發給對方的證書

* @param keyFile

* 私鑰(請求連接的消息是用公鑰加密的,需要用私鑰解密)

* @param password

* 私鑰密碼

* @return

* @throws Exception

*/

public static SSLSocketFactory getSocketFactoryByCert(final String caCrtFile, final String crtFile,

final String keyFile, final String password) throws Exception {

// 加載CA證書(用于驗證的根證書)

X509Certificate caCert = getCertificate(caCrtFile);

// 加載自己的證書,用于發送給客戶端

X509Certificate cert = getCertificate(crtFile);

// 加載私鑰

final PrivateKey privateKey = getPrivateKey(keyFile);

// 用CA證書創建TrustManagerFactory

KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());

caKs.load(null, null);

caKs.setCertificateEntry("ca-certificate", caCert);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

tmf.init(caKs);

// 用證書和私鑰創建KeyManagerFactory

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

ks.load(null, null);

ks.setCertificateEntry("certificate", cert);

ks.setKeyEntry("private-key", privateKey, password.toCharArray(), new java.security.cert.Certificate[] {cert});

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

kmf.init(ks, password.toCharArray());

SSLContext context = SSLContext.getInstance("TLSv1");

// kmf用于發送關鍵信息讓服務端校驗,tmf用于校驗服務端的證書。雙向認證

context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());

return context.getSocketFactory();

}

/**

* 讀取x509格式的證書

*

* @param certPath

* @return

* @throws FileNotFoundException

* @throws CertificateException

*/

private static X509Certificate getCertificate(String certPath) throws FileNotFoundException, CertificateException {

InputStream inStream = new FileInputStream(certPath);

CertificateFactory cf = CertificateFactory.getInstance("X.509");

X509Certificate caCert = (X509Certificate)cf.generateCertificate(inStream);

return caCert;

}

/**

* 讀取 PKCS8 編碼的 RSA 秘鑰文件

*

* @param path

* @return

* @throws IOException

* @throws NoSuchAlgorithmException

* @throws InvalidKeySpecException

*/

private static PrivateKey getPrivateKey(String path)

throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {

BufferedReader br = new BufferedReader(new FileReader(path));

String s = br.readLine();

String str = "";

s = br.readLine();

while (s.charAt(0) != '-') {

str += s + "\r";

s = br.readLine();

}

// BASE64Decoder base64decoder = new BASE64Decoder();

byte[] bytes = Base64.getMimeDecoder().decode(str);

// byte[] bytes = base64decoder.decodeBuffer(str);

// 生成私鑰

KeyFactory kf = KeyFactory.getInstance("RSA");

PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);

PrivateKey privateKey = kf.generatePrivate(keySpec);

return privateKey;

}

發現項目中有生成好的p12證書,可以直接使用。這里再追加一種p12證書和CA證書驗證的方式

/**

* 通過p12證書和ca證書雙向認證

*

* @param caCrtFile

* @param p12Keystore

* @param p12Pwd

* @return

* @throws Exception

*/

public static SSLSocketFactory getSocketFactoryByP12AndCA(String caCrtFile, String p12Keystore, String p12Pwd)

throws Exception {

// 加載CA證書(用于驗證的根證書)

X509Certificate caCert = getCertificate(caCrtFile);

// 用CA證書創建TrustManagerFactory

KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());

caKs.load(null, null);

caKs.setCertificateEntry("ca-certificate", caCert);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

tmf.init(caKs);

KeyStore keyStore = KeyStore.getInstance("pkcs12");

keyStore.load(new FileInputStream(p12Keystore), p12Pwd.toCharArray());

// 創建管理JKS密鑰庫的密鑰管理器 (SunX509)

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

// 使用密鑰內容源初始化此工廠。 提供者通常使用 KeyStore 來獲取在安全套接字協商期間所使用的密鑰內容

kmf.init(keyStore, p12Pwd.toCharArray());

// 初始sslcontext

SSLContext sslContext = SSLContext.getInstance("SSLv3");

sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());

return sslContext.getSocketFactory();

}

總結

以上是生活随笔為你收集整理的java nio ssl_java连接MQTT+SSL服务器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。