Taxonomy Icon

IoT

Internet of Things (IoT) solutions are complex networks of devices and sensors that collect and exchange data over networks and the cloud. With more and more data being exposed to more and more applications, security is a major challenge for IoT developers.

This series of articles focuses on security at the three architectural tiers of an IoT application: devices, data, and applications. Part 1 of this series introduces IoT security basics and then describes the various approaches for securing devices or gateways. Part 2 (this article) will focus on the security aspects of the network and transport tier, which includes the IBM Watson IoT Platform. Part 3 provides the security requirements for the application tier, and an implementation approach for an analytics IoT application that is created in the IBM Bluemix platform.

Securing data over the network

Transport Layer Security (TLS) provides security for transferring data over the network. The data is encrypted to prevent anyone from listening to and understanding the content. TLS (also known as SSL) is widely used to provide secured access to many websites. TLS ensures that trust is established between the server and the client before data transfer happens. It does so by use of server certificates that clients must validate. In some cases, the server also validates client-specific certificates.

MQTT relies on TCP as the transport protocol, and by default the connection does not use encrypted communication. Although implementing TLS impacts performance of the data transfer and the load on the server, most MQTT brokers (including IBM Watson IoT Platform) support the use of TLS to enable applications to secure the exchange of sensitive data. Watson IoT Platform uses TLS as the default connection security setting for devices and gateways by using MQTT.

However, you can add additional security by having your IoT application encrypt the data that it sends and receives. For instance, IoT applications can use MQTT PUBLISH to encrypt data that they send. This implementation is particularly important for untrusted environments or insecure network connections among the devices and the MQTT broker.

If you are using the username and password fields of the MQTT CONNECT packet for authentication and authorization, you should strongly consider using TLS. Port 8883 is standardized for a secured MQTT connection.

Data encryption

Most deployments of MQTT use transport layer security (TLS), so the data is encrypted and its integrity is validated. IBM Watson IoT Platform supports TLS 1.2 for securing the data transported over the network. In this article, we describe how applications can provide additional security mechanisms by supporting the exchange of encrypted messages even when the underlying network may or may not support TLS.

Only the payload data (private sensor information) of the message needs to be encrypted. The message fields of MQTT PUBLISH messages are not changed. The payload information is binary, so no special encoding mechanism is required while it transmits the message. Also, no specific changes are required on the broker side because the MQTT message format remains the same. Only the application that is interpreting the message payload needs to decrypt the message to understand the content.

TLS provides security on the network layer while MQTT payload encryption provides security on the application layer, so they can be used in conjunction without conflict. MQTT payload encryption only solves the problem of protecting application messages from eavesdroppers or untrusted MQTT clients (if no authentication mechanism is in place). An attacker can still replay the message or modify parts of the message, such as the topic, if there is no secure communication channel over TLS.

MQTT payload encryption is useful when you can’t use TLS but still don’t want to send your application data in plain text. This implementation gives an additional layer of security, since all your application data is secured. Table 1 outlines the advantages and disadvantages of payload encryption. While payload encryption might be a good fit for constrained devices that can’t use TLS, encryption and decryption can use considerable processing power, so use an algorithm that offers high security without taxing resources.

Table 1. Advantages and disadvantages of payload encryption
Advantages Disadvantages
  • Complete end-to-end message security
  • Adds another layer of security for applications that are transmitting highly sensitive data
  • Appropriate in situations where TLS cannot be used
  • Might not be possible to implement in devices with very low resources.
  • Messages can still be modified by attacker if a secured communication channel is not used.
  • If the payload is encrypted, IBM Watson IoT Platform is not able to determine the data points from the message. Hence, the Real-Time Insights service cannot display the dashboard in the usual way.

As mentioned earlier, if you use MQTT over TLS, it impacts performance in terms of additional CPU usage and communication. While this performance cost is not significant for brokers, it can be a challenge for devices with limited resources. Here are some tested techniques for improving TLS performance:

Encrypting message payloads

The following code samples depict message payload encryption.

You can download this sample code from the DeviceSimulatorDemo project in GitHub.

While Listing 3 shows how MQTT messages are encrypted before they are published to the topic, Listing 4 provides a sample encryption approach that is based on the AES method and that uses a secret key.

Listing 3. Publishing encrypted messages

handler.publishBytes("iot‑2/evt/" + MqttUtil.DEFAULT_EVENT_ID
        + "/fmt/json", IOTSecurityUtil.encryptString(objtd.toString(), strKey, uniqueParam), false, 0);


public void publishBytes(String topic, byte[] message, boolean retained, int qos) {
        // Check if client is connected
        if (isMqttConnected()) {
            // Create a new MqttMessage from the message string
            MqttMessage mqttMsg = new MqttMessage(message);
            // Set retained flag
            mqttMsg.setRetained(retained);
            // Set quality of service
            mqttMsg.setQos(qos);
            try {
                client.publish(topic, mqttMsg);
            } catch (MqttPersistenceException e) {
                e.printStackTrace();
            } catch (MqttException e) {
                e.printStackTrace();
            }
        } else {
            connectionLost(null);
        }
    }  

Listing 4. AES-based encryption method

    public static byte[] encryptString(String strMsg, String strKey, String iv) {
        byte[] encrypted = null;
        IvParameterSpec ivspec = null;
        SecretKeySpec keyspec;
        try {
            // Create key and cipher
            ivspec = new IvParameterSpec(iv.getBytes());
            keyspec = new SecretKeySpec(strKey.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            // encrypt the text
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            encrypted = cipher.doFinal(strMsg.getBytes("UTF‑8"));

        } catch (Exception e) {
            e.printStackTrace();
        }
        return encrypted;
    }

Decrypting message payloads

The following code samples depict message payload decryption.

While Listing 5 shows how raw bytes that are read from the topic can be decrypted by the application when a message encryption approach is implemented, Listing 6 provides a sample decryption implementation based on the AES method.

Listing 5. Receiving and decrypting the message

    @Override
public void messageArrived(String topic, MqttMessage mqttMessage)
                throws Exception {

    super.messageArrived(topic, mqttMessage);

    System.out.println("topic " + topic);

    Matcher matcher = pattern.matcher(topic);
    if (matcher.matches()) {
        String deviceid = matcher.group(1);
        byte[] rawPayload = mqttMessage.getPayload();
        String payload = IOTSecurityUtil.decryptString(
                        rawPayload, strKey, uniqueParam);
             // Further processing of decrypted message as per your need
       }
}

Listing 6. AES-based decryption method


public static String decryptString(byte[] byteMsg, String strKey, String iv) {
        String strReturn = null;
        IvParameterSpec ivspec = null;
        SecretKeySpec keyspec;
        try {
            // Create key and cipher
            ivspec = new IvParameterSpec(iv.getBytes());
            keyspec = new SecretKeySpec(strKey.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            // Decrypt the payload
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
            strReturn = new String(cipher.doFinal(byteMsg));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return strReturn;
}

Checking the integrity of a message

Message integrity checks ensure that the MQTT message is not modified by an attacker. Use message integrity checks for untrusted communication among the devices and the broker.

Consider these approaches for message integrity checks:

  • Checksum
    A cryptographic checksum is a mathematical value calculated based on the contents of the MQTT message. A checksum is created by a series of calculations that converts the message payload into a fixed string of digits called a hash value. Example checksum or hash algorithms include MD5, Cyclic Redundancy Check (CRC), and Secured Hash Algorithm (SHA-1 or SHA-2). This checksum value might be added at the beginning of the MQTT message payload. The application that receives the messages would recalculate the checksum to verify the integrity of the message.
  • Message Authentication Code (MAC)
    A message authentication code (MAC) is information that is used to verify that a message came from a trusted sender and that it was not modified by anyone or anything during transmission. A MAC algorithm takes both a secret key and an MQTT message payload to be authenticated as input, and the algorithm outputs a MAC. This MAC needs to be sent in the MQTT message. The application also needs to have access to the same secret key that was used to generate the MAC.
  • Digital Signature
    A digital signature is a digital code—which is generated and authenticated by public key encryption—that can be attached to the MQTT message in order to verify its contents and the sender’s identity.

Digital signatures provide the most comprehensive method for verifying message integrity, but they do impact performance and require additional resources. While simple checksums are easy to implement and verify, MACs or digital signatures require more computation and should be used in appropriate scenarios – where either the underlying network is not reliable or the transmitted message requires extra security measures.

API Security in the Watson IoT Platform REST APIs

The Watson IoT Platform REST APIs provide various security measures for accessing the REST APIs, for reading encrypted data, and for validating message identity.

Accessing the REST APIs

IBM Watson IoT Platform provides a REST-like API to support certain functions, including managing devices and accessing data that comes from devices. Access to the API is secured by using basic authentication in combination with HTTPS:

  • Use HTTPS (port 443) rather than HTTP (port 80)
  • Use your application’s API Key as the user name
  • Use the corresponding Auth Token as the password

To authenticate API calls, you must create an API Key in Watson IoT Platform. Follow the step-by-step guidelines for creating an API key in this recipe.

The API key can then be used to call the API Client com.ibm.iotf.client.api.APIClient that can call IBM Watson IoT Platform APIs. Listing 7 shows how to create the APIClient instance. It reads the properties that are required to instantiate the API client from a property file.

Listing 7. Instantiate APIClient

public void doApp() {
        // Read properties from the conf file
        Properties props = MqttUtil.readProperties("MyData/application.prop");

        try {
            //Instantiate the class by passing the properties file
            this.apiClient = new APIClient(props);

            System.out.println("Adding a new device..");
            addDevice();
            System.out.println("Get all devices..");
            getAllDevices();
            System.out.println("Delete a device..");
            deleteDevice();
            System.out.println("Success..Exiting..");

        } catch (Exception e) {
            e.printStackTrace();
            System.exit(‑1);
        }

    }

The property file with sample values or instantiating APIClient is shown in Listing 8. You need to replace the sample placeholder values with your App ID, Org ID, created API Key, and authorization token.

Listing 8. Contents of the application.prop file
   
id=<App_ID>
Organization‑ID=<Your_org_ID>
Authentication‑Method=apikey
API‑Key=<API key>
Authentication‑Token=<Auth_token>
Enable‑Shared‑Subscription=true
Sample API call getAllDevices() method:

The following code sample in Listing 9 demonstrates how to retrieve all the devices in an organization by using the Java Client Library.

Listing 9. Retrieving all the devices in an organization by using the Java Client Library

  /∗∗
  ∗ This sample showcases how to retrieve all the devices in an organization using the Java Client Library.
  ∗ @throws IoTFCReSTException
  ∗/

  private void getAllDevices() throws IoTFCReSTException {
        // Get all the devices of type SampleDT
        try {
            /∗∗
             ∗ The Java ibmiotf client library provides one argument constructor
             ∗ which can be used to control the output, for example, let's try to retrieve
             ∗ the devices in a sorted order based on device ID.
             ∗/

            ArrayList<NameValuePair> parameters = new ArrayList<NameValuePair>();
            parameters.add(new BasicNameValuePair("_sort","deviceId"));

            JsonObject response = this.apiClient.retrieveDevices(DEVICE_TYPE, parameters);

            // The response will contain more parameters that will be used to issue
            // the next request. The result element will contain the current list of devices
            JsonArray devices = response.get("results").getAsJsonArray();
            for(Iterator<JsonElement> iterator = devices.iterator(); iterator.hasNext(); ) {
                JsonElement deviceElement = iterator.next();
                JsonObject responseJson = deviceElement.getAsJsonObject();
                System.out.println(responseJson);
            }
        } catch(IoTFCReSTException e) {
            System.out.println("HttpCode :" + e.getHttpCode() +" ErrorMessage :: "+ e.getMessage());
            // Print if there is a partial response
            System.out.println(e.getResponse());
        }
    }

Reading encrypted messages by using the REST API for IBM Watson IoT Platform

The IBM Watson IoT Platform API can also be used to read historical data from the Watson IoT Platform message store. Devices can encrypt the payload and add additional checksum fields to protect the data. Figure 3 depicts the flow for this scenario.

Figure 1. IBM Watson IoT Platform API flow

As pictured above, the device encrypts the data part of the payload and sends it as a JSON element. It also generates the checksum of the data part (before encryption and encoding) and sends it as another JSON element. The application uses IBM Watson IoT Platform APIs to pull the message from Watson IoT Platform, decrypts and decodes the data part, and then finally performs checksum validation by comparing the generated checksum and the checksum stored in the message.

The following sections describe how to implement this scenario.

Device code: Encrypting the payload and adding a checksum field in message

The code below encrypts the payload and adds a checksum field in the message:


    JSONObject message = new JSONObject();
    JSONObject innerObj = new JSONObject();
    try {
        innerObj.put("evt", "Test");
        innerObj.put("fld", "This is a sensitive data");
        String checkSum = IOTSecurityUtil.getMD5(innerObj.toString());
        message.put("chksum", checkSum);
        message.put("ts", new SimpleDateFormat("yyyy‑MM‑dd HH:mm:ss")
                    .format(new Date()));
        message.put("d", IOTSecurityUtil.encryptEncodeString(innerObj.toString(), strKey, uniqueParam));
    } catch (JSONException e1) {
        e1.printStackTrace();
    }
    handler.publish("iot‑2/evt/" + "eid"
                + "/fmt/json", message.toString(), true, 1);

Listing-mc 1 shows some sample checksum generation code.

Sample checksum generation code

    public static String getMD5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes());
            BigInteger number = new BigInteger(1, messageDigest);
            String hashtext = number.toString(16);
            return hashtext;
        }
        catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

Application code: Reading the message, decrypting the message, and validating the checksum

The application code below uses the Watson IoT Platform API to read the messages from the persistent store, decrypts the message, and validates the checksum to ensure message integrity.



    /∗∗
     ∗ Method to get the latest historical event and process it
     ∗/
    private void getAllHistoricalEventsByDeviceID() {
        // Get the historical events
        try {
            //Get the list of historical events by device type and device id
            JsonElement response = this.apiClient.getHistoricalEvents(
                    DEVICE_TYPE, DEVICE_ID_TEST);
            JsonObject events = response.getAsJsonObject();
            JsonArray eventArray = events.getAsJsonArray("events");
            //Get the latest event
            JsonElement currentEvent = eventArray.get(0);
            JsonObject responseJson = currentEvent.getAsJsonObject();
            System.out.println("Most recent event ‑ " + responseJson.toString());

            JsonObject evtObject = responseJson.getAsJsonObject("evt");
            System.out.println("Complete raw payload ‑" + evtObject.toString());

            String dString = evtObject.get("d").getAsString();
            System.out.println("Encrypted data part ‑" + dString);

            String processedData = IOTSecurityUtil.decryptDecodeString(dString.getBytes(), strKey, uniqueParam);
            System.out.println("Data part after decryption and decoding ‑ " + processedData);

            String generatedChkSum = IOTSecurityUtil.getMD5(processedData);
            System.out.println("Generated checksum ‑ " + generatedChkSum);

            String chkSum = evtObject.get("chksum").getAsString();
            if(generatedChkSum.equals(chkSum))
                System.out.println("Checksum validation successful");
            else
                System.out.println("Checksum validation failed");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

The IBM Watson IoT Platform REST API expects a JSON structure in the message and persists it in a Cloudant database. It will not parse (and hence store) a fully encrypted message that is not in JSON format. Therefore, the binary encrypted data in the payload must be encoded because Watson IoT Platform will try to interpret the JSON. It’s not a valid JSON string if it is not encoded.

A good practice is to encrypt the payload and put it inside a JSON structure. In this case, the other fields of the JSON message can include checksum and time stamp.

Conclusion

This article, Part 2 of a three-part series, focused on securing the data exchanged between IoT devices and the MQTT broker. It also described how the IBM Watson IoT Platform REST API supports added levels of security between the application layer and the MQTT broker by securing the APIs that are used to manage devices and access device data.

The authors recommend that you use TLS for applications that deal with personal and sensitive data, such as patient records or vehicle data. In cases where the underlying network does not support TLS, or an extra level of security is required, consider payload encryption to ensure that device data is not transferred without any security. However, you need to remember that performance will decrease when you implement security mechanisms like encryption and checksum.