Skip to content

Instantly share code, notes, and snippets.

@jimrok
Last active February 2, 2025 23:23
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);

@choutianxius
Copy link

choutianxius commented Feb 2, 2025

Works like a charm for AWS IoT. I'm using OpenJDK 21, with paho mqtt v5. Only some small modifications to adapt to v5 API is needed.

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