TLS/SSL java 中的 MQTT 连接
TLS/SSL Connection for MQTT in java
我正在研究 MQTT 协议。我配置了它的服务器并在 java 中使用它的 mosquitto 库在端口 1883 上执行了通信。
现在我想进行此通信 secure.What 我知道端口 8883 是为其基于 tls 的安全通信保留的。
它需要 X.509 证书。
为此,我找到了以下教程。
但我的问题是
1.how 我们可以在 java 代码中生成这些证书吗?
2.how 我们可以根据上面的教程在 time.As 处使用多个证书吗?我们可以在 server.And 的 mosquitto.conf 文件中一次只指定一组证书然后我们需要重新启动服务器。(我不想这样做。)
3.how 我们能否让 运行 服务器知道这些新生成的证书。除了在服务器的 conf 文件中指定之外,还有其他方法可以做到这一点吗?
好的,我想您可能没有理解证书身份验证的工作原理。
它有 2 个部分(证明经纪人是它所说的人,然后证明连接的客户端是谁)
首先,经纪人将拥有 1 个向全世界标识它的证书。您将 Mosquitto 配置为在启动时使用此证书,并且永远不需要更改它。此证书将由 CA 签名。
传感器(客户端)将拥有 CA 证书的副本,当它们连接到代理时将使用该副本以确保它是它声称的身份。
其次,如果您想使用客户端证书来识别单独的传感器,那么它们也都需要一个证书。通常这将由与经纪人证书相同的 CA 签名,因此经纪人可以验证客户是否是他们声称的身份。可以将 Mosquitto 设置为使用证书 (use_identity_as_username true
) 中的 CN 作为连接客户端的用户名,然后您可以使用 mosquitto_auth_plugin 跟踪证书中的 CN 并将 ACL 应用于控制谁可以使用什么主题。
至于在 java 中创建证书,我建议您查看此 question
颁发新证书时无需重新启动 Mosquitto。
//add bcpkix-jdk15on-161, bcprov-jdk15on-1.52 and eclips pago-mqtt3.1
//lib in build path
import java.io.*;
import java.nio.file.*;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.net.ssl.*;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.*;
import org.bouncycastle.openssl.*;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
public class SslUtil
{
@SuppressWarnings("deprecation")
//It will return SSLSocketFactory
public static SSLSocketFactory getSocketFactory (final String
caCrtFile, final String crtFile, final String keyFile,
final String password) throws Exception
{
try{
Security.addProvider(new BouncyCastleProvider());
X509Certificate caCert =
(X509Certificate)SslUtil.getCertificate(caCrtFile);
X509Certificate cert =
(X509Certificate)SslUtil.getCertificate(crtFile);
FileReader fileReader = new FileReader(keyFile);
PEMParser parser = new PEMParser(fileReader);
PEMKeyPair kp = (PEMKeyPair) parser.readObject();
PrivateKeyInfo info = kp.getPrivateKeyInfo();
PrivateKey rdKey = new JcaPEMKeyConverter().setProvider("BC")
.getPrivateKey(info);
// CA certificate is used to authenticate server
KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
// client key and certificates are sent to server so it can authenticate us
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("certificate", cert);
ks.setKeyEntry("private-key", rdKey, password.toCharArray(), new java.security.cert.Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
// finally, create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context.getSocketFactory();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
//Get MqttClient for Subscripe Pulish
public static void mqttClient(String caCrtFile,String clientCrtFilePath,String clientKeyFilePath,String password){
try{
String serverUrl = "ssl://serverip:8883";
MqttClient client = new MqttClient(serverUrl, "consumerId" , null);
//this MyCallback class extends mqtt there we have to override some function //like message arriver etc
client.setCallback(new MyCallback());
MqttConnectOptions options = new MqttConnectOptions();
options.setConnectionTimeout(60);
options.setKeepAliveInterval(60);
options.setSocketFactory(SslUtil.getSocketFactory(caCrtFile, clientCrtFilePath, clientKeyFilePath, password));
client.connect(options);
client.subscribe("topic", 0);
}catch (Exception e) {
System.out.println("#Exception :"+e.getMessage());
}
}
//start execution
public static void main(String[] args) throws Exception {
String caCrtFile = "path Certification Authority";
String clientCrtFilePath ="path for client crt file";
String clientKeyFilePath ="path of client key";
String password = "password while generating files";
mqttClient(caCrtFile,clientCrtFilePath,clientKeyFilePath,password);
// getCertificate(caCrtFile);
}
//return certificate
public static java.security.cert.X509Certificate getCertificate(String pemfile) throws Exception
{
java.security.cert.X509Certificate cert = null;
try {
FileReader fRd = new FileReader(pemfile);
final PemReader certReader = new PemReader(fRd);
final PemObject certAsPemObject = certReader.readPemObject();
if (!certAsPemObject.getType().equalsIgnoreCase("CERTIFICATE")) {
throw new Exception("Certificate file does not contain a certificate but a " + certAsPemObject.getType());
}
final byte[] x509Data = certAsPemObject.getContent();
final CertificateFactory fact = CertificateFactory.getInstance("X509");
cert = (X509Certificate) fact.generateCertificate(new ByteArrayInputStream(x509Data));
if (!(cert instanceof X509Certificate)) {
throw new Exception("Certificate file does not contain an X509 certificate");
}
} catch (FileNotFoundException e) {
throw new IOException("Can't find file " + pemfile);
}catch (Exception e) {
System.out.println("#Exceotion :"+e.getMessage());
}
return cert;
}
//retuen keyPair Object form client key
public KeyPair decodeKeys(byte[] privKeyBits,byte[] pubKeyBits)
throws InvalidKeySpecException, NoSuchAlgorithmException {
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
PrivateKey privKey=keyFactory.generatePrivate(new
PKCS8EncodedKeySpec(privKeyBits));
PublicKey pubKey=keyFactory.generatePublic(new
X509EncodedKeySpec(pubKeyBits));
return new KeyPair(pubKey,privKey);
}
}
我正在研究 MQTT 协议。我配置了它的服务器并在 java 中使用它的 mosquitto 库在端口 1883 上执行了通信。 现在我想进行此通信 secure.What 我知道端口 8883 是为其基于 tls 的安全通信保留的。 它需要 X.509 证书。 为此,我找到了以下教程。
但我的问题是
1.how 我们可以在 java 代码中生成这些证书吗?
2.how 我们可以根据上面的教程在 time.As 处使用多个证书吗?我们可以在 server.And 的 mosquitto.conf 文件中一次只指定一组证书然后我们需要重新启动服务器。(我不想这样做。)
3.how 我们能否让 运行 服务器知道这些新生成的证书。除了在服务器的 conf 文件中指定之外,还有其他方法可以做到这一点吗?
好的,我想您可能没有理解证书身份验证的工作原理。
它有 2 个部分(证明经纪人是它所说的人,然后证明连接的客户端是谁)
首先,经纪人将拥有 1 个向全世界标识它的证书。您将 Mosquitto 配置为在启动时使用此证书,并且永远不需要更改它。此证书将由 CA 签名。
传感器(客户端)将拥有 CA 证书的副本,当它们连接到代理时将使用该副本以确保它是它声称的身份。
其次,如果您想使用客户端证书来识别单独的传感器,那么它们也都需要一个证书。通常这将由与经纪人证书相同的 CA 签名,因此经纪人可以验证客户是否是他们声称的身份。可以将 Mosquitto 设置为使用证书 (use_identity_as_username true
) 中的 CN 作为连接客户端的用户名,然后您可以使用 mosquitto_auth_plugin 跟踪证书中的 CN 并将 ACL 应用于控制谁可以使用什么主题。
至于在 java 中创建证书,我建议您查看此 question
颁发新证书时无需重新启动 Mosquitto。
//add bcpkix-jdk15on-161, bcprov-jdk15on-1.52 and eclips pago-mqtt3.1
//lib in build path
import java.io.*;
import java.nio.file.*;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.net.ssl.*;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.*;
import org.bouncycastle.openssl.*;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
public class SslUtil
{
@SuppressWarnings("deprecation")
//It will return SSLSocketFactory
public static SSLSocketFactory getSocketFactory (final String
caCrtFile, final String crtFile, final String keyFile,
final String password) throws Exception
{
try{
Security.addProvider(new BouncyCastleProvider());
X509Certificate caCert =
(X509Certificate)SslUtil.getCertificate(caCrtFile);
X509Certificate cert =
(X509Certificate)SslUtil.getCertificate(crtFile);
FileReader fileReader = new FileReader(keyFile);
PEMParser parser = new PEMParser(fileReader);
PEMKeyPair kp = (PEMKeyPair) parser.readObject();
PrivateKeyInfo info = kp.getPrivateKeyInfo();
PrivateKey rdKey = new JcaPEMKeyConverter().setProvider("BC")
.getPrivateKey(info);
// CA certificate is used to authenticate server
KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
// client key and certificates are sent to server so it can authenticate us
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("certificate", cert);
ks.setKeyEntry("private-key", rdKey, password.toCharArray(), new java.security.cert.Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
// finally, create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context.getSocketFactory();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
//Get MqttClient for Subscripe Pulish
public static void mqttClient(String caCrtFile,String clientCrtFilePath,String clientKeyFilePath,String password){
try{
String serverUrl = "ssl://serverip:8883";
MqttClient client = new MqttClient(serverUrl, "consumerId" , null);
//this MyCallback class extends mqtt there we have to override some function //like message arriver etc
client.setCallback(new MyCallback());
MqttConnectOptions options = new MqttConnectOptions();
options.setConnectionTimeout(60);
options.setKeepAliveInterval(60);
options.setSocketFactory(SslUtil.getSocketFactory(caCrtFile, clientCrtFilePath, clientKeyFilePath, password));
client.connect(options);
client.subscribe("topic", 0);
}catch (Exception e) {
System.out.println("#Exception :"+e.getMessage());
}
}
//start execution
public static void main(String[] args) throws Exception {
String caCrtFile = "path Certification Authority";
String clientCrtFilePath ="path for client crt file";
String clientKeyFilePath ="path of client key";
String password = "password while generating files";
mqttClient(caCrtFile,clientCrtFilePath,clientKeyFilePath,password);
// getCertificate(caCrtFile);
}
//return certificate
public static java.security.cert.X509Certificate getCertificate(String pemfile) throws Exception
{
java.security.cert.X509Certificate cert = null;
try {
FileReader fRd = new FileReader(pemfile);
final PemReader certReader = new PemReader(fRd);
final PemObject certAsPemObject = certReader.readPemObject();
if (!certAsPemObject.getType().equalsIgnoreCase("CERTIFICATE")) {
throw new Exception("Certificate file does not contain a certificate but a " + certAsPemObject.getType());
}
final byte[] x509Data = certAsPemObject.getContent();
final CertificateFactory fact = CertificateFactory.getInstance("X509");
cert = (X509Certificate) fact.generateCertificate(new ByteArrayInputStream(x509Data));
if (!(cert instanceof X509Certificate)) {
throw new Exception("Certificate file does not contain an X509 certificate");
}
} catch (FileNotFoundException e) {
throw new IOException("Can't find file " + pemfile);
}catch (Exception e) {
System.out.println("#Exceotion :"+e.getMessage());
}
return cert;
}
//retuen keyPair Object form client key
public KeyPair decodeKeys(byte[] privKeyBits,byte[] pubKeyBits)
throws InvalidKeySpecException, NoSuchAlgorithmException {
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
PrivateKey privKey=keyFactory.generatePrivate(new
PKCS8EncodedKeySpec(privKeyBits));
PublicKey pubKey=keyFactory.generatePublic(new
X509EncodedKeySpec(pubKeyBits));
return new KeyPair(pubKey,privKey);
}
}