When building Internet of Things (IoT) applications, Android devices can be excellent providers of data. The Message Queue Telemetry Transport (MQTT) protocol offers an efficient and effective way to transfer this data to another node in your application, such as a cloud server. If sensitive or private data is being collected, the transfer can be protected using SSL to encrypt the data.
This can sometimes be difficult even if the certificate was generated by a trusted source. This article demonstrates how to enable an Android application to access an MQTT Broker that has been secured with any certificate (including self-signed).
The Eclipse Paho MQTT client implementation is used in this example.
For information about creating the original SSL certificate or how to configure the MQTT Broker, see our previous article (Securing MQTT on Apache ActiveMQ →).
GeneratingBouncyCastleformatcertificates
Android uses an implementation of a cryptography API library created and supported by an organisation called BouncyCastle. (For more information please see BouncyCastle.org →) Trust-store files must be created in a BouncyCastle compatible format (BKS) before they can be used by the library.
The JAR file containing the BouncyCastleProvider class required for this can be downloaded from bcprov-ext-jdk15on-1.46.jar → . It is only needed for the keytool, not by the Android client code, as android.jar has an early version of the BouncyCastle code included.
Use the JDK keytool (Oracle keytool documentation →) utility to import the certificate that was used to secure the MQTT broker and generate a trust-store file in the appropriate format (see Figure 1). In our example, the certificate for our MQTT broker is in a file called ‘cacert.pem’ and we are going to use it to create a BKS format trust-store file called raw_key_file. You must provide a fully qualified path for the bcprov-ext-jdk15on-1.46.jar file using the -providerpath keytool option. The key store password in this example is ‘mykeystorepassword’ which will be needed later in the runtime code.
|
keytool
-
import
-
alias
mqtt
-
broker
-
file
cacert
.
pem
-
keypass
mykeystorepassword
-
keystore
raw_key_file
-
storetype
BKS
-
storepass
mykeystorepassword
-
providerClass
org
.
bouncycastle
.
jce
.
provider
.
BouncyCastleProvider
-
providerpath
bcprov
-
ext
-
jdk15on
-
1.46.jar
|
Figure 1: keytool command line options
Next, create a folder called ‘raw’ in the ‘res’ folder of your Android project if it does not already exist, and paste the generated raw_key_file in it as shown in Figure 2.
Figure 2: Android raw_key_file resource
We use a utility class to generate an SSLSocketFactory instance specifying the BKS format certificate to trust. The certificate is loaded from a raw resource by ID (See Figure 3).
This will be used as one of the parameters in the set of MQTT client connect options.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package
com
.
rijware
.
ssltest
;
import
java
.
security
.
KeyStore
;
import
java
.
security
.
SecureRandom
;
import
java
.
util
.
HashMap
;
import
javax
.
net
.
ssl
.
SSLContext
;
import
javax
.
net
.
ssl
.
SSLSocketFactory
;
import
javax
.
net
.
ssl
.
TrustManagerFactory
;
import
android
.
content
.
Context
;
public
class
SslUtility
{
private
static
SslUtility
mInstance
=
null
;
private
Context
mContext
=
null
;
private
HashMap
<
Integer
,
SSLSocketFactory
>
mSocketFactoryMap
=
new
HashMap
<
Integer
,
SSLSocketFactory
>
(
)
;
public
SslUtility
(
Context
context
)
{
mContext
=
context
;
}
public
static
SslUtility
getInstance
(
)
{
if
(
null
==
mInstance
)
{
throw
new
RuntimeException
(
"first call must be to SslUtility.newInstance(Context) "
)
;
}
return
mInstance
;
}
public
static
SslUtility
newInstance
(
Context
context
)
{
if
(
null
==
mInstance
)
{
mInstance
=
new
SslUtility
(
context
)
;
}
return
mInstance
;
}
public
SSLSocketFactory
getSocketFactory
(
int
certificateId
,
String
certificatePassword
)
{
SSLSocketFactory
result
=
mSocketFactoryMap
.
get
(
certificateId
)
;
// check to see if already created
if
(
(
null
==
result
)
&&
(
null
!=
mContext
)
)
{
// not cached so need to load server certificate
try
{
KeyStore
keystoreTrust
=
KeyStore
.
getInstance
(
"BKS"
)
;
// Bouncy Castle
keystoreTrust
.
load
(
mContext
.
getResources
(
)
.
openRawResource
(
certificateId
)
,
certificatePassword
.
toCharArray
(
)
)
;
TrustManagerFactory
trustManagerFactory
=
TrustManagerFactory
.
getInstance
(
TrustManagerFactory
.
getDefaultAlgorithm
(
)
)
;
trustManagerFactory
.
init
(
keystoreTrust
)
;
SSLContext
sslContext
=
SSLContext
.
getInstance
(
"TLS"
)
;
sslContext
.
init
(
null
,
trustManagerFactory
.
getTrustManagers
(
)
,
new
SecureRandom
(
)
)
;
result
=
sslContext
.
getSocketFactory
(
)
;
mSocketFactoryMap
.
put
(
certificateId
,
result
)
;
// cache for reuse
}
catch
(
Exception
ex
)
{
// log exception
}
}
return
result
;
}
}
|
Figure 3: SslUtility.java
EclipsePahoMQTTClient
The Paho project from the Eclipse organization provides a nice implementation of an MQTT client library that can be used to access an MQTT broker. More information about it can be obtained at the Paho website → The JAR file for the J2SE Java Client can be downloaded from org.eclipse.paho.client.mqttv3.jar →. After downloading the JAR file, ensure that the Android project Build Path has been configured to include the JAR as a library, as shown in Figure #4. In addition, you must make sure that it is exported as shown in Figure #5.

Figure 4: Java Build Path Libraries

Figure 5: Java Build Path Order and Export
The AccessMqtt class (Figure 6) in this example demonstrates how to create an MqttClient instance, set the logon credentials, and indicate the certificate to trust when establishing the connection. In this case, the MQTT broker is configured to require a user’s logon name and password for any publish or subscribe access.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
package
com
.
rijware
.
ssltest
;
import
org
.
eclipse
.
paho
.
client
.
mqttv3
.
MqttClient
;
import
org
.
eclipse
.
paho
.
client
.
mqttv3
.
MqttConnectOptions
;
import
org
.
eclipse
.
paho
.
client
.
mqttv3
.
MqttDeliveryToken
;
import
org
.
eclipse
.
paho
.
client
.
mqttv3
.
MqttException
;
import
org
.
eclipse
.
paho
.
client
.
mqttv3
.
MqttPersistenceException
;
import
org
.
eclipse
.
paho
.
client
.
mqttv3
.
persist
.
MemoryPersistence
;
public
class
AccessMqtt
{
private
MqttClient
mMqttClient
=
null
;
public
boolean
init
(
String
mqttBrokerURL
,
String
mqttClientId
,
String
mqttAccountName
,
String
mqttAccountPassword
,
int
rawCertificateResourceId
,
String
keystorePassword
)
{
if
(
null
==
mMqttClient
)
{
try
{
mMqttClient
=
new
MqttClient
(
mqttBrokerURL
,
mqttClientId
,
new
MemoryPersistence
(
)
)
;
if
(
null
!=
mMqttClient
)
{
MqttConnectOptions
options
=
new
MqttConnectOptions
(
)
;
options
.
setUserName
(
mqttAccountName
)
;
options
.
setPassword
(
mqttAccountPassword
.
toCharArray
(
)
)
;
options
.
setSocketFactory
(
SslUtility
.
getInstance
(
)
.
getSocketFactory
(
rawCertificateResourceId
,
keystorePassword
)
)
;
options
.
setCleanSession
(
true
)
;
mMqttClient
.
connect
(
options
)
;
}
}
catch
(
MqttException
ex
)
{
// log exception
reset
(
)
;
}
}
return
null
!=
mMqttClient
;
}
public
boolean
write
(
String
mqttTopic
,
String
mqttMessage
)
{
MqttDeliveryToken
deliveryToken
=
null
;
if
(
null
!=
mMqttClient
)
{
try
{
deliveryToken
=
mMqttClient
.
getTopic
(
mqttTopic
)
.
publish
(
mqttMessage
.
getBytes
(
)
,
1
,
false
)
;
}
catch
(
MqttPersistenceException
ex
)
{
// log exception
}
catch
(
MqttException
ex
)
{
// log exception
}
}
return
null
!=
deliveryToken
;
}
public
void
reset
(
)
{
if
(
null
!=
mMqttClient
)
{
try
{
if
(
mMqttClient
.
isConnected
(
)
)
{
mMqttClient
.
disconnect
(
0
)
;
}
}
catch
(
MqttException
ex
)
{
// log exception
}
mMqttClient
=
null
;
}
}
}
|
Figure 6: AccessMqtt.java
The following fragment (Figure 7) shows the SslUtility singleton being initialized, and the AccessMqtt class being called to establish a connection to the MQTT broker and post a single message to a topic.
|
SslUtility
.
newInstance
(
this
)
;
// initialize with Activity Context
String
BROKER_URL_SSL
=
"ssl://192.168.2.1:1885"
;
AccessMqtt
access
=
new
AccessMqtt
(
)
;
if
(
access
.
init
(
BROKER_URL_SSL
,
"myClientID"
,
"myMQTTAccount"
,
"myMQTTPassword"
,
R
.
raw
.
raw_key_file
,
"mykeystorepassword"
)
)
{
access
.
write
(
"topic"
,
"message"
)
;
access
.
reset
(
)
;
}
|
Figure 7: Java test code fragment
Now we can transfer private data in an encrypted format between an Android app and a server.
References
The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons Attribution 3.0 license →. Android is a trademark of Google Inc.