MQTT: Android Client Encryption
Creating an SSL/TLS Android MQTT Client
Photo by cottonbro from Pexels In this guide, we will be creating an Android MQTT client application that can send and receive messages to and from an Eclipse Mosquitto broker. It also assumes you already have a self-signed CA certificate and the corresponding files. Note that when you generate keys to work with MQTT, you shouldn’t use encryption (the -ds3 flag) for the server certificate as this creates a password-protected key which the broker can’t decode. Setting up the Broker First, we will import the certificates for the Broker to use.
Windows
Create a directory called certs in the location where Mosquitto was installed. By default, this is under
C:\Program Files\mosquitto\.
Adding certificates to Mosquitto
Secondly, we modify the mosquitto.conf file for it to support SSL/TLS support. This is done by changing the port (You can add an extra listener instead of changing the default port if you wish) and by pointing the broker to the certificate files
Change default port
SSL/TLS file paths Once that is updated, you can run the broker by typing the following command
Mosquitto Broker running with SSL/TLS configuration To verify client connections, you can test by opening a new CMD and connect a client by running the following command
Mosquitto client
Linux
Move the certificates to the Mosquitto folder as shown
$ sudo cp ca.crt /etc/mosquitto/ca_certificates/ $ sudo cp server.key /etc/mosquitto/certs/ $ sudo cp server.crt /etc/mosquitto/certs/
Create a configuration file and paste the lines below
listener 8883 cafile /etc/mosquitto/ca_certificates/ca.crt certfile /etc/mosquitto/certs/server.crt keyfile /etc/mosquitto/certs/server.key
Linux Mosquitto configuration Once you modify the configuration you will need to restart the Mosquitto
$ sudo systemctl restart mosquitto
Unable to start Mosquitto Note that if you are having problems restarting Mosquitto and you get this error after updating the configuration, it is possible you have the incorrect permissions for your certificates. If running as a regular user, all certificates require read permission at a user level. Then you can check the status of your broker
$ sudo systemctl status mosquitto
Mosquitto Broker successfully started
Note that certificates and keys created can be used on the Mosquitto broker/server, and also on a web server, which is the reason the term “server” is used in the Mosquitto manual and not the term of broker. Now that the broker is properly configured and is up and running we can create the Android client.
Android Client
The Android edge must have the Broker’s CA certificate inside the application when configuring the Secure Sockets Layer (SSL). For Android, the CA certificate is stored in a BKS (Bouncy Castle) file. There are 2 ways of generating and using the BKS file. Option 1, is to generate the file ourselves and import it to the project. Option 2, is to generate it programmatically using a .crt file.
Implementation 1: Importing a BKS file
In order to connect to the MQTT broker using SSL/TLS, we need a BKS file as mentioned above. One can be generated from the CA certificate, see the steps below.
Step 1 In order to generate the BKS file, the following is required: The CA Certificate of the Broker
Openssl Bouncy Castle: You can download Bouncy Castle from here. At the time of this tutorial, version is being used bcprov-jdk15to18–167.jar
Step 2 Convert the CA certificate to .PEM format.
$ openssl x509 -in cert.crt -out ca.pem
Step 3 Now, we convert the .pem certificate to .BKS by running the following commands Linux
$ keytool -import -v -trustcacerts -alias 0 -file <(openssl x509 -in “.\ca.pem”) -keystore “test.bks” -storepass test-key -storetype BKS -provider “org.bouncycastle.jce.provider.BouncyCastleProvider” -providerpath “.\bcprov-jdk15to18–167.jar”
Windows
$ keytool -import -v -trustcacerts -alias 0 -file ca.pem -keystore “test.bks” -storepass test-key -storetype BKS -provider “org.bouncycastle.jce.provider.BouncyCastleProvider” -providerpath “.\bcprov-jdk15to18–167.jar”
If successful, you should see the following output
Successfully created BKS file
Step 4
Import the .BKS file to your Android project. This can be placed under raw resources. If that directory doesn’t exist, you can create a new directory as shown below
Raw Resource directory Step 5 Setup the Android Keystore using a BKS instance, load the file we created, configure the Trust Manager Factory to trust our certificate. Finally, we create the SSL/TLS context that will be used by the MQTT client to establish the connection.
// SSL/TLS Setup // Get the BKS Keystore type required by Android KeyStore trustStore = KeyStore.getInstance("BKS"); // Read the BKS file we generated (droidstore.bks) InputStream in = getResources().openRawResource(R.raw.droidstore);
trustStore.load(input, null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
SSLContext sslCtx = SSLContext.getInstance("TLS"); sslCtx.init(null, tmf.getTrustManagers(), null);
Step 6 Create an MQTT Client and Options object and pass the SSL/TLS context we initialized above.
// MQTT Client // Define your server URI this needs to match the name provided // in the CN Section of your CERT String serverURI = "ssl://<Your_IP_OR_DOMAIN>:8883";
MqttClient client = new MqttClient(serverURI, "DroidClientExample", new MemoryPersistence()); // Create an MQTT Options object MqttConnectOptions options = new MqttConnectOptions(); // Pass the SSL context we previously configured options.setSocketFactory(sslCtx.getSocketFactory()); client.setCallback(this); client.connect(options); client.subscribe(topic);
Step 7 Send a message from your Android application
MqttMessage msg = new MqttMessage("Android SSL Message".getBytes()); try { client.publish("topic/SSLTest", msg); } catch (MqttException e) { e.printStackTrace(); } catch (MqttException e) { e.printStackTrace(); }
You should see the message from Android in the terminal
Successfully received MQTT message from Android device using method 1
Implementation 2: Generating a BKS file in Android
Step 1
Copy the CA certificate to your application external directory. Make sure you provide READ_EXTERNAL_STORAGE permissions to your application.
Step 2
Load the file and store it in a Certificate object.
Security.addProvider(new BouncyCastleProvider()); // Load CAs from an InputStream CertificateFactory cf = CertificateFactory.getInstance("X.509"); File file = new File("your_ca.crt"); InputStream caIn = new BufferedInputStream(new FileInputStream(file)); Certificate ca; try { // Convert crt to Certificate Java Object ca = cf.generateCertificate(caIn); } catch { Log.e(TAG, "Error generating the certificate: " + e); } finally { caIn.close(); }
Step 3
Setup the Android Keystore and Trust Manager Factory to trust the certificate. Then we create the SSL/TLS context that will be used by the MQTT client to establish the connection.
// Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager SSLContext sslCtx = SSLContext.getInstance("TLS"); sslCtx.init(null, tmf.getTrustManagers(), null);
Step 4
Create an MQTT Client and Options object and pass the SSL/TLS context we initialized above.
// MQTT Client // Define your server URI this needs to match the name provided // in the CN Section of your CERT String serverURI = "ssl://<Your_IP_OR_DOMAIN>:8883"; MqttClient client = new MqttClient(serverURI, "DroidClientExample", new MemoryPersistence()); // Create an MQTT Options object MqttConnectOptions options = new MqttConnectOptions(); options.setSocketFactory(sslCtx.getSocketFactory()); client.setCallback(this); client.connect(options); client.subscribe(topic);
Step 5
Send a message from your Android application
MqttMessage message = new MqttMessage("Android SSL Message".getBytes()); try { client.publish("topic/SSLTest", message); } catch (MqttException e) { e.printStackTrace(); } catch (MqttException e) { e.printStackTrace(); }
You should see the message from Android in the terminal
Successfully received MQTT message from Android device using method 2
Troubleshooting the Android Client
Error: TLS alert unknown ca
The server does not accept the client certificate you have send. You might be using wrong certificate.
Another reason might be that you’ve used the correct certificate but failed to add the necessary chain certificates (server.crt and server.key) You might be using the wrong Common Name (CN) defined when you created the certificate