Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PIOT-GDA-10-001: Update MqttClientConnector to support simple authentication and encrypted connections to the broker #112

Open
labbenchstudios opened this issue Nov 18, 2020 · 0 comments
Labels
exercise New feature to implement as an exercise
Milestone

Comments

@labbenchstudios
Copy link
Contributor

labbenchstudios commented Nov 18, 2020

Description

  • Update the Java class named MqttClientConnector so it can support loading and setting authorization value (user and password) as well as TLS encrypted connections to the broker.

Review the README

  • Please see README.md for further information on, and use of, this content.
  • License for embedded documentation and source codes: PIOT-DOC-LIC

Estimated effort may vary greatly

  • The estimated level of effort for this exercise shown in the 'Estimate' section below is a very rough approximation. The actual level of effort may vary greatly depending on your development and test environment, experience with the requisite technologies, and many other factors.

Actions

NOTE: The following actions are for your GDA's MqttClientConnector class.

  • Add the following import statements, as follows:
import java.io.File;
import javax.net.ssl.SSLSocketFactory;

import org.eclipse.paho.client.mqttv3.IMqttMessageListener;

import programmingtheiot.common.SimpleCertManagementUtil;
  • Add the following class-scoped parameters to MqttClientConnector:
private String pemFileName = null;
private boolean enableEncryption = false;
private boolean useCleanSession = false;
private boolean enableAutoReconnect = true;
  • Add (or edit) the private method named initCredentialConnectionParameters(String configSectionName) as follows:
private void initCredentialConnectionParameters(String configSectionName)
{
	ConfigUtil configUtil = ConfigUtil.getInstance();
	
	try {
		_Logger.info("Checking if credentials file exists and is loadable...");
		
		Properties props = configUtil.getCredentials(configSectionName);
		
		if (props != null) {
			this.connOpts.setUserName(props.getProperty(ConfigConst.USER_NAME_TOKEN_KEY, ""));
			this.connOpts.setPassword(props.getProperty(ConfigConst.USER_AUTH_TOKEN_KEY, "").toCharArray());
			
			_Logger.info("Credentials now set.");
		} else {
			_Logger.warning("No credentials are set.");
		}
	} catch (Exception e) {
		_Logger.log(Level.WARNING, "Credential file non-existent. Disabling auth requirement.");
	}
}

NOTE: The ConfigUtil class provides a simple public Properties getCredentials(string configSectionName) method that will attempt to load the file referenced by the credFile key in the configSectionName of the loaded configuration file. This will store any key=value pairs within the file within the returned Properties instance until it is garbage collected. It's provided as a convenience method to allow a credential file to be stored separately from the PiotConfig.props file.

IMPORTANT! You should NEVER store secrets or credentials in a shared git repository or other storage medium without fully understanding the risks.

  • Add (or edit) the private method named initSecureConnectionParameters(String configSectionName) as follows:
private void initSecureConnectionParameters(String configSectionName)
{
	ConfigUtil configUtil = ConfigUtil.getInstance();
	
	try {
		_Logger.info("Configuring TLS...");
		
		if (this.pemFileName != null) {
			File file = new File(this.pemFileName);
			
			if (file.exists()) {
				_Logger.info("PEM file valid. Using secure connection: " + this.pemFileName);
			} else {
				this.enableEncryption = false;
				
				_Logger.log(Level.WARNING, "PEM file invalid. Using insecure connection: " + this.pemFileName, new Exception());
				
				return;
			}
		}
		
		SSLSocketFactory sslFactory =
			SimpleCertManagementUtil.getInstance().loadCertificate(this.pemFileName);
		
		this.connOpts.setSocketFactory(sslFactory);
		
		// override current config parameters
		this.port =
			configUtil.getInteger(
				configSectionName, ConfigConst.SECURE_PORT_KEY, ConfigConst.DEFAULT_MQTT_SECURE_PORT);
		
		this.protocol = ConfigConst.DEFAULT_MQTT_SECURE_PROTOCOL;
		
		_Logger.info("TLS enabled.");
	} catch (Exception e) {
		_Logger.log(Level.SEVERE, "Failed to initialize secure MQTT connection. Using insecure connection.", e);
		
		this.enableEncryption = false;
	}
}
  • Add (or edit) the private method named initClientParameters(String configSectionName). This update will also allow you to load your client ID from the configuration file.
    • Move your property initialization code from the no-arg constructor to this new method, as follows:
private void initClientParameters(String configSectionName)
{
	ConfigUtil configUtil = ConfigUtil.getInstance();
	
	this.host =
		configUtil.getProperty(
			configSectionName, ConfigConst.HOST_KEY, ConfigConst.DEFAULT_HOST);
	this.port =
		configUtil.getInteger(
			configSectionName, ConfigConst.PORT_KEY, ConfigConst.DEFAULT_MQTT_PORT);
	this.brokerKeepAlive =
		configUtil.getInteger(
			configSectionName, ConfigConst.KEEP_ALIVE_KEY, ConfigConst.DEFAULT_KEEP_ALIVE);
	this.enableEncryption =
		configUtil.getBoolean(
			configSectionName, ConfigConst.ENABLE_CRYPT_KEY);
	this.pemFileName =
		configUtil.getProperty(
			configSectionName, ConfigConst.CERT_FILE_KEY);
	
	// This next config file boolean property is optional; it can be
	// set within the [Mqtt.GatewayService] and [Cloud.GatewayService]
	// sections of PiotConfig.props. You can use it to create a logical
	// flow within this class to determine whether to use MqttClient
	// or MqttAsyncClient, or simply choose one of the two classes based
	// on your usage needs. Generally speaking, MqttAsyncClient will
	// be necessary when running the GDA as an application, as it will
	// need to handle incoming and outgoing messages using MQTT
	// simultaneously. For GDA-only testing using the test cases
	// specified in this lab module and others, it's generally best -
	// and likely required - to use MqttClient.
	// 
	// IMPORTANT: If you're using an older version of ConfigConst.java,
	// you'll need to add the following line of code to ConfigConst.java:
	// public static final String USE_ASYNC_CLIENT_KEY = "useAsyncClient";
	this.useAsyncClient =
	    configUtil.getBoolean(
	        ConfigConst.MQTT_GATEWAY_SERVICE, ConfigConst.USE_ASYNC_CLIENT_KEY);

	// NOTE: updated from Lab Module 07 - attempt to load clientID from configuration file
	this.clientID =
		configUtil.getProperty(
			ConfigConst.GATEWAY_DEVICE, ConfigConst.DEVICE_LOCATION_ID_KEY, MqttClient.generateClientId());
	
	// these are specific to the MQTT connection which will be used during connect
	this.persistence = new MemoryPersistence();
	this.connOpts    = new MqttConnectOptions();
	
	this.connOpts.setKeepAliveInterval(this.brokerKeepAlive);
	this.connOpts.setCleanSession(this.useCleanSession);
	this.connOpts.setAutomaticReconnect(this.enableAutoReconnect);
	
	// if encryption is enabled, try to load and apply the cert(s)
	if (this.enableEncryption) {
		initSecureConnectionParameters(configSectionName);
	}
	
	// if there's a credential file, try to load and apply them
	if (configUtil.hasProperty(configSectionName, ConfigConst.CRED_FILE_KEY)) {
		initCredentialConnectionParameters(configSectionName);
	}
	
	// NOTE: URL does not have a protocol handler for "tcp" or "ssl",
	// so construct the URL manually
	this.brokerAddr  = this.protocol + "://" + this.host + ":" + this.port;
	
	_Logger.info("Using URL for broker conn: " + this.brokerAddr);
}
  • Update the no-arg constructor so it simply calls the initClientParameters(String configSectionName) method as follows:
public MqttClientConnector()
{
	super();
	
	initClientParameters(ConfigConst.MQTT_GATEWAY_SERVICE);
}

NOTE: For now, you don't need to change the configuration in PiotConfig.props - you can leave encryption disabled for the time being.

Estimate

  • Medium

Tests

  • Start your local MQTT broker using the default configuration and execute the following - for now, WITHOUT TLS enabled:
    • Simply re-run your MqttClientConnectorTest test cases - no encryption is required for this, as it's only to verify that your code is still functioning properly.
    • NOTE: TLS-specific integration tests will be outlined in PIOT-INT-10-004
@labbenchstudios labbenchstudios added the exercise New feature to implement as an exercise label Nov 18, 2020
@labbenchstudios labbenchstudios added this to the Chapter 10 milestone Nov 18, 2020
@labbenchstudios labbenchstudios added this to Chapter 10 - Edge Integration in Programming the IoT - Exercises Kanban Board Nov 18, 2020
@labbenchstudios labbenchstudios changed the title PIOT-GDA-10-001: Update MqttClientConnector to support encrypted connections to the broker PIOT-GDA-10-001: Update MqttClientConnector to support simple authentication and encrypted connections to the broker Mar 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
exercise New feature to implement as an exercise
Projects
Programming the IoT - Exercises Kanba...
  
Lab Module 10 - Edge Integration
Development

No branches or pull requests

1 participant