MQTT: Android Client Encryption

From OnnoWiki
Jump to navigation Jump to search

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



Referensi