Skip to content

Instantly share code, notes, and snippets.

@jimrok
Last active April 17, 2024 06:10
Show Gist options
  • Save jimrok/d25cb45b840f5a4ad700 to your computer and use it in GitHub Desktop.
Save jimrok/d25cb45b840f5a4ad700 to your computer and use it in GitHub Desktop.
A simple java code for SSL/TLS connection from Paho java client to mosquitto MQTT broker
package test_mqtt;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileReader;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
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.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
public class TestMQTT {
public static void main(String[] args) {
String serverUrl = "ssl://serverip:1883";
String caFilePath = "/your_ssl/cacert.pem";
String clientCrtFilePath = "/your_ssl/client.pem";
String clientKeyFilePath = "/your_ssl/client.key";
String mqttUserName = "guest";
String mqttPassword = "123123";
MqttClient client;
try {
client = new MqttClient(serverUrl, "2");
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(mqttUserName);
options.setPassword(mqttPassword.toCharArray());
options.setConnectionTimeout(60);
options.setKeepAliveInterval(60);
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1);
SSLSocketFactory socketFactory = getSocketFactory(caFilePath,
clientCrtFilePath, clientKeyFilePath, "");
options.setSocketFactory(socketFactory);
System.out.println("starting connect the server...");
client.connect(options);
System.out.println("connected!");
Thread.sleep(1000);
client.subscribe(
"/u/56ca327d17531d08e76bddd4a215e37f5fd6082f7442151c4d3f1d100a0ffd4e",
0);
client.disconnect();
System.out.println("disconnected!");
} catch (MqttException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private static SSLSocketFactory getSocketFactory(final String caCrtFile,
final String crtFile, final String keyFile, final String password)
throws Exception {
Security.addProvider(new BouncyCastleProvider());
// load CA certificate
X509Certificate caCert = null;
FileInputStream fis = new FileInputStream(caCrtFile);
BufferedInputStream bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
caCert = (X509Certificate) cf.generateCertificate(bis);
// System.out.println(caCert.toString());
}
// load client certificate
bis = new BufferedInputStream(new FileInputStream(crtFile));
X509Certificate cert = null;
while (bis.available() > 0) {
cert = (X509Certificate) cf.generateCertificate(bis);
// System.out.println(caCert.toString());
}
// load client private key
PEMParser pemParser = new PEMParser(new FileReader(keyFile));
Object object = pemParser.readObject();
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder()
.build(password.toCharArray());
JcaPEMKeyConverter converter = new JcaPEMKeyConverter()
.setProvider("BC");
KeyPair key;
if (object instanceof PEMEncryptedKeyPair) {
System.out.println("Encrypted key - we will use provided password");
key = converter.getKeyPair(((PEMEncryptedKeyPair) object)
.decryptKeyPair(decProv));
} else {
System.out.println("Unencrypted key - no password needed");
key = converter.getKeyPair((PEMKeyPair) object);
}
pemParser.close();
// 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("X509");
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", key.getPrivate(), 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.2");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
}
@mluis
Copy link

mluis commented Apr 10, 2019

I'm trying to connect to AWS IoT but can't manage to do it.
using mosquitto_sub all goes well and I connect and receive messages with the same certificates.
Although with yout code I'm getting:

.... snip ...
MQTT Rec: livedata, RECV TLSv1.2 ALERT:  warning, close_notify
MQTT Rec: livedata, called closeInternal(false)
MQTT Rec: livedata, SEND TLSv1.2 ALERT:  warning, description = close_notify
Padded plaintext before ENCRYPTION:  len = 2
0000: 01 00                                              ..
MQTT Rec: livedata, WRITE: TLSv1.2 Alert, length = 26
[Raw write]: length = 31
0000: 15 03 03 00 1A 00 00 00   00 00 00 00 02 60 FC 19  .............`..
0010: 23 71 F2 C8 17 0E DC 92   C6 4A 3F 98 6D 88 4D     #q.......J?.m.M
MQTT Rec: livedata, called closeSocket(false)
MQTT Rec: livedata, called close()
MQTT Rec: livedata, called closeInternal(true)
Connection lost (32109) - java.io.EOFException
	at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:189)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.EOFException
	at java.io.DataInputStream.readByte(DataInputStream.java:267)
	at org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream.readMqttWireMessage(MqttInputStream.java:92)
	at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:136)
	... 1 more

Any idea what might be wrong?

@filgiuff
Copy link

Which version of BouncyCastleProvider are you using in this code ?
Could you write the jar name ?

@sanre6
Copy link

sanre6 commented Jun 24, 2019

@mluis I have the same exact problem were you able to fix it ?

@mluis
Copy link

mluis commented Jun 24, 2019

I believe I was missing the bouncy castle plugin at all. After importing it to the right directory it was fixed. The JVM didn't had the plugin.

@jenifram
Copy link

jenifram commented Jul 11, 2019

hi,
i faced two issue
Issue 1:
Got exception in parsing private key excpetion java.lang.ClassCastException: org.bouncycastle.asn1.pkcs.PrivateKeyInfo cannot be cast to org.bouncycastle.openssl.PEMKeyPair.. then i modified code to parse private key

if (object instanceof PrivateKeyInfo) {
final JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
Log.d(TAG,"Unencrypted key - no password needed");
//key = converter.getKeyPair((PEMKeyPair) object);
PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) object;
key = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo);
with the above code change exception solved.
issue 2: i am getting error when i connect to mosquito mqtt broker in linux.
mosquito error-1562115998: OpenSSL Error: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
1562115998: OpenSSL Error: error:140940E5:SSL routines:ssl3_read_bytes:ssl handshake failure
AndroidGetting Exception: MqttException (0) - javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Using bouncy castle - bcprov-jdk15on:1.62,bcpkix-jdk15on:1.62
Help to fix this

@sanre6
Copy link

sanre6 commented Jul 12, 2019

@jenifram I believe this is a jdk issue use openjdk instead of oracle jdk.

@jenifram
Copy link

@sanre6 - I changed the openJDK in the Android Project structure window and still same issue facing again and my android tablet version is Android 7.1.2

@jenifram
Copy link

downloaded jdk from here

@qaqRose
Copy link

qaqRose commented Jun 23, 2020

i use maven dependency

<!-- mqtt client-->
<dependency>
  <groupId>org.eclipse.paho</groupId>
    <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
    <version>1.2.2</version>
</dependency>

<!--  ssl support  -->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.64</version>
</dependency>

and it work

@baoquach14
Copy link

Hi All

I am getting the following error show below when running with JDK 12.
Any idea, thank you for your help.

20:52:36.312 [main] INFO au.gov.nsw.rms.its.sdcs.mb.vernemq.TestMQTTSSL - starting connect the server...
20:52:36.454 [main] ERROR au.gov.nsw.rms.its.sdcs.mb.vernemq.TestMQTTSSL - Exception encounter with error MqttException
org.eclipse.paho.client.mqttv3.MqttException: MqttException
at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38)
at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:738)
at java.base/java.lang.Thread.run(Thread.java:835)
Caused by: javax.net.ssl.SSLHandshakeException: No subject alternative names present
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:320)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:263)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:258)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:641)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:460)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:360)
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:177)

@Rohitm7
Copy link

Rohitm7 commented Nov 11, 2021

java.io.FileNotFoundException: /your_ssl/cacert.pem (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.(FileInputStream.java:138)
at java.io.FileInputStream.(FileInputStream.java:93)
at gropu1.SSL_MQTT.getSocketFactory(SSL_MQTT.java:77)
at gropu1.SSL_MQTT.main(SSL_MQTT.java:50)
.... I am getting this earror plz help me.............

@baoquach14
Copy link

Hi All

Using Maven dependencies

org.eclipse.paho org.eclipse.paho.client.mqttv3 1.2.2 org.bouncycastle bcpkix-jdk15on 1.64

using JDK 12, SSLSocketFactory getSocketFactory(...) in TestMQTT.java class I am able to publish and subscribe using MQTT 3.x

However if include

org.eclipse.paho
org.eclipse.paho.mqttv5.client
1.2.5

I am getting an error of Unsupported protocol version. when I try to publish and subscribe using MQTT 5.x. Please exception below.

org.eclipse.paho.mqttv5.common.MqttException: Unsupported protocol version.
at org.eclipse.paho.mqttv5.client.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:32)
at org.eclipse.paho.mqttv5.client.internal.ClientState.notifyReceivedAck(ClientState.java:1074)
at org.eclipse.paho.mqttv5.client.internal.CommsReceiver.run(CommsReceiver.java:153)
at java.base/java.lang.Thread.run(Thread.java:835)

Can you please advise what I need to get MQTT 5.x. works with SSL certificate.
Thank you for your help

@srlohr
Copy link

srlohr commented Dec 8, 2022

@mluis , et. al. - Does this connect to AWS IOT core using X.509 cert authentication to port 8883?

I have only a slight difference in sample project below because it seems that bouncycastle is being deprecated, but since private key and cert is properly parsed into a data structure and successful connection to mosquitto.org:8883 proves it works.

https://github.com/srlohr/PublicPahoClient.git

eclipse-paho/paho.mqtt.java#972

https://stackoverflow.com/questions/74725722/paho-mqtt-android-client-to-aws-iot-connection-failure-connection-lost-32109

@pfostenberg
Copy link

if you get:
Received fatal alert: protocol_version
change the line to
SSLContext context = SSLContext.getInstance("TLSv1.3");

@y-fedorov
Copy link

hi, i faced two issue Issue 1: Got exception in parsing private key excpetion java.lang.ClassCastException: org.bouncycastle.asn1.pkcs.PrivateKeyInfo cannot be cast to org.bouncycastle.openssl.PEMKeyPair.. then i modified code to parse private key

if (object instanceof PrivateKeyInfo) { final JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter(); Log.d(TAG,"Unencrypted key - no password needed"); //key = converter.getKeyPair((PEMKeyPair) object); PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) object; key = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo); with the above code change exception solved. issue 2: i am getting error when i connect to mosquito mqtt broker in linux. mosquito error-1562115998: OpenSSL Error: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown 1562115998: OpenSSL Error: error:140940E5:SSL routines:ssl3_read_bytes:ssl handshake failure AndroidGetting Exception: MqttException (0) - javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Using bouncy castle - bcprov-jdk15on:1.62,bcpkix-jdk15on:1.62 Help to fix this

    KeyPair key;
    if (object instanceof PEMEncryptedKeyPair pemEncryptedKeyPair) {
        ...
    } else if (object instanceof PrivateKeyInfo pKey) {
        var privateKey = converter.getPrivateKey(pKey);
        key = new KeyPair(null, privateKey);
    } else {
        ...
    }

I had a similar problem and i fixed with the code above

@phanminhtien
Copy link

Dear all,
If I have only cert.pem, privkey.pem. How can I connect to MQTT Broker.
I can conenct to MQTT Broker by using Python and React, but can not conenct to MQTT Broker by using Java/Kotlin.
Please help me. Thanks a lots

@rover886
Copy link

rover886 commented Jan 9, 2024

In my case I have only client certificate (crt) and client key (key) files, how can I connect to Azure Event Grid MQTT. With the above code I get error as follows:
javax.net.ssl.SSLException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

@rover886
Copy link

rover886 commented Jan 9, 2024

I checked in other MQTT client like MQTTBox and MQTTX the connection is successful by using only client certificate and client key.

@mluis
Copy link

mluis commented Jan 13, 2024

@mluis , et. al. - Does this connect to AWS IOT core using X.509 cert authentication to port 8883?

I have only a slight difference in sample project below because it seems that bouncycastle is being deprecated, but since private key and cert is properly parsed into a data structure and successful connection to mosquitto.org:8883 proves it works.

https://github.com/srlohr/PublicPahoClient.git

eclipse/paho.mqtt.java#972

https://stackoverflow.com/questions/74725722/paho-mqtt-android-client-to-aws-iot-connection-failure-connection-lost-32109

yes it worked at the time for aws iot

@stillywud
Copy link

Hi All

I am getting the following error show below when running with JDK 12. Any idea, thank you for your help.

20:52:36.312 [main] INFO au.gov.nsw.rms.its.sdcs.mb.vernemq.TestMQTTSSL - starting connect the server... 20:52:36.454 [main] ERROR au.gov.nsw.rms.its.sdcs.mb.vernemq.TestMQTTSSL - Exception encounter with error MqttException org.eclipse.paho.client.mqttv3.MqttException: MqttException at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38) at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:738) at java.base/java.lang.Thread.run(Thread.java:835) Caused by: javax.net.ssl.SSLHandshakeException: No subject alternative names present at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:320) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:263) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:258) at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:641) at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:460) at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:360) at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392) at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443) at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421) at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:177)

  //禁止服务端检查
  options.setHttpsHostnameVerificationEnabled(false);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment