Wrinkle-free SSL: Using TLS/SSL with micro broker v3 in IBM Lotus Expeditor

One of the new security features provided with micro broker v3, which is shipped with IBM® Lotus® Expeditor 6.2, is the ability to encrypt network data transmitted to and from the broker using the industry-standard Transport Layer Security/Secure Sockets Layer (TLS/SSL) protocols. This article describes several ways to secure your micro broker communication using this new feature.

Steve Russell (steve_russell@uk.ibm.com), Software test, Pervasive and Advanced Messaging Technologies, IBM

Steve Russell works at the IBM UK laboratory in Hursley Park. He specializes in software test for pervasive and advanced messaging technologies. You can reach Steve at steve_russell@uk.ibm.com.



29 June 2009 (First published 14 April 2009)

Also available in Chinese

Editor's note: Know a lot about this topic? Want to share your expertise? Participate in the IBM Lotus software wiki program today.

Introduction


Using TLS/SSL with micro broker

This article is not intended to be a tutorial about TLS/SSL and all its capabilities. To fully describe the ways they can and should be used to ensure secure communication, and to minimize the possibility of the interception and compromising of your data, would take a large book, and many of these descriptions have already been written. The pleasure of developing the necessary level of expertise is, therefore, left as an exercise for the reader.

Instead, this article describes several ways to secure your micro broker communication using a new security feature. After a brief introduction to SSL connection types, it gives specific code examples that can help you get up and running quickly and securely.

Transport Layer Security and its predecessor, Secure Sockets Layer, are often referred to jointly as SSL. Even though this use of the term SSL is not strictly correct, that is the convention followed in this article from here onward. As you gain familiarity with the micro broker security API, you can see that a programmer can determiine the actual protocol used for a specific link.


SSL connection types

Typical SSL connections fall into one of three categories:

  • Encrypted communication, no authentication
  • Encrypted communication, server authentication
  • Encrypted communication, mutual authentication

The first type of connection is not often used, as authentication is one of the important capabilities of SSL, providing a mechanism for an entity to prove that it is what it says it is. The second type of connection, server authentication, is achieved by the server presenting the client with a certificate that declares the identity of the owning organization. A valid certificate includes a chain of trust from the owning organization back to a certificate authority (CA), which is a well-known trusted third party. Included in the certificate is the server’s public key, which is used to initiate the encrypted data flow.

You might be familiar with certificates from browsing the Web. Occasionally, when you visit a new Web site, a window displays, asking you whether you trust the certificate provided by the site. Web browsers are usually configured to recognize a number of well-known CAs, but if the site doesn’t have a valid, recognized certificate (the certificate might be expired, from an unknown CA, self-certified, and so on) a message is presented to users. If this happens, it is up to the user to look at the certificate and decide whether to trust it and the server that is providing it.

Server authentication is the most common SSL connection in use. This authentication provides the client with a way to ensure that it is talking to the correct server and not a bogus one that is attempting to intercept communications. In some cases, the server might also need assurance that the client is what it claims to be, and so a mutually authenticated connection is required; this category is the third one listed previously. The choice of which type of SSL connection is to be used is a system design issue for the implementer. Simple ways to set up these two types of connection, server authentication and mutual authentication, are described in the remainder of this article.


Server authentication

As discussed, a server that supports SSL connections must have a certificate to present to the user or client. A certificate contains the owner’s public key, which is used to initiate the encrypted communication between client and server. After a connection is established, new random keys are used for data exchange for the remaining duration of the session.

Keys and certificates are kept in files known as keystores and truststores. The server keeps its private key and the certificate containing its public key (which is sent to the client) in a keystore. When the client receives the certificate, the certificate is placed in the client’s truststore if the client chooses to trust the certificate.

Java™ provides the keytool utility to manage keys, certificates, keystores, and truststores. For information on the options used in the following commands, see the keytool documentation. You should substitute your own values for file names and other parameters as required.

Important: Lotus Expeditor can be configured to use different Java Virtual Machines (JVMs). The standard installation configures Lotus Expeditor to use the DesktopEE JVM. Keystores, truststores, and the keytool utility for the DesktopEE environment are not compatible with the Java 2 Platform, Standard Edition (J2SE) environment, and the reverse is true also. Use the keytool utility that matches the JVM used in your environment to manipulate your keystore. Keystores and truststores cannot, therefore, be transferred from one environment to the other. If you have to support both environments, the necessary key and truststores must be created separately for each of them.

Note that keytool commands should be entered as a single line. Keytool options used here have been tested and work in both DesktopEE and J2SE environments. This selection might not be the case for all available options.

You should review the keytool documentation to understand the options that are used. The passwords and other user-provided data used should, of course, be of your own choosing.

Creating SSL-enabled ports

Follow these steps to create a secure client-micro broker link using server authentication, with minimum SSL configuration.

Create the micro broker’s keystore

Assuming that no current certificate exists for your micro broker, first create the micro broker’s private/public key pair and certificate and place them in a keystore. A single invocation of the keytool command does this:

keytool -genkey -alias examplecert -keystore mbrokerdtee.jks
-keypass default -storepass default -dname "CN=Micro broker, OU=PVC,
O=IBM, C=UK" -keyalg RSA

On completion, the file mbrokerdtee.jks is created, which contains the broker’s key pair and a matching self-signed certificate.

Create a secure listener

A new default micro broker is created with a single unencrypted TCPListener on port 1883. A secure listener can be created in one of two ways:

  • A secure TCPListener can be added to a running broker.
  • The broker can be created with a default secure TCPListener (and no unencrypted TCPListeners).

We cover both options.

First, we add a new secure TCPListener to an existing micro broker as shown in listing 1. In this example, the code is assumed to be running in the same JVM as the broker, so a LocalBroker object reference is obtained. In practice, lbroker could instead be a reference to a RemoteBroker object. In either case, the referenced broker must be running before a listener can be added to it.

Listing 1. Adding a secure listener to a micro broker
private final String BROKER_NAME = "MBroker";
private final int SECURE_PORT = 8883;
private final String KEYSTORE_DTEE = "/mbrokerdtee.jks";
private final char[] KSPASSWD_DTEE ={'d','e','f','a','u','l','t'};

void _addSecurePort() throws Exception
{
    BrokerFactory factory = BrokerFactory.INSTANCE;
    LocalBroker lbroker = factory.getByName(BROKER_NAME);
    Communications bc = lbroker.getCommunications();
    // Specify the port number in this method call. 8883 is the IANA
    // default port for secure MQTT connections.
    MQTTTCPListenerDefinition mqttld = 
                 bc.createMQTTTCPListenerDefinition(SECURE_PORT);
    mqttld.setSecure(true);
    ServerSSLDefinition ssld = mqttld.createSSLDefinition();
    ssld.setKeyStore(KEYSTORE_DTEE);
    ssld.setKeyStorePassword(KSPASSWD_DTEE);
    mqttld.setSSLDefinition(ssld);
    bc.addTCPListener(mqttld);
}

This technique creates a new, encrypted listener on the specified port and leaves the broker’s unencrypted port (1883 by default) available, which can be appropriate if you want to provide secured access from, say, the Internet, and access to the unsecured port is available only from controlled locations. If you’re interested in this type of configuration, see the later section, "Binding a listener to a specific IP address."

Creating a default secure broker is no more difficult after you have a reference to a BrokerFactory object (factory in the example). In production code, you obtain this reference through the OSGi Service Registry; a direct reference is obtained in the example shown in listing 2 for clarity. You should create a default secure broker if you don’t want any connections to your micro broker to be unencrypted.

Listing 2. Creating a default secure micro broker
void createSecureBroker() throws Exception 
{
    BrokerFactory factory = BrokerFactory.INSTANCE;
    BrokerDefinition def = factory.createBrokerDefinition(BROKER_NAME);
    // setPort() is optional – 8883 is used if this method is not called
//  def.setPort(Integer.parseInt(SECURE_PORT));
    def.setDefaultListenerSecure(true);
    ServerSSLDefinition ssld = def.createSSLDefinition();
    ssld.setKeyStore(KEYSTORE_DTEE);
    ssld.setKeyStorePassword(KSPASSWD_DTEE);
    def.setSSLDefinition(ssld);
    factory.create(def);
}

When the broker’s secure listener has been successfully started, two messages are posted to the broker’s log:

>Info [3810] FMBD1128 MBroker Keystore '/mbrokerdtee.jks' will be used for SSL
>Info [3810] FMBD1125 MBroker Listening on port 8883 using SSL

If there is a problem (such as not being able to open the keystore), an error is posted to the log instead.

>Error [3810] FMBD3020 MBroker SSL-Initialization failed. Keystore file not found.

Creating a secure connection from a client

At this point, we have a running broker listening for incoming SSL connection requests on port 8883. Now we need a client to request a connection on that port.

Create a secure connection from a client

In the example shown in listing 3, an MQTTv5 client is created and connected to the broker using the secure port at 8883. By providing a broker URL prefix of ssl:// instead of tcp://, you indicate that the client should request a secure connection. The remainder of the URL (in the example) shows that you are connecting to a broker running on the same system as the client and selects the default secure port, 8883.

SSL settings for the client side of the connection, in particular the truststore where certificates can be stored, are provided by Lotus Expeditor (see the later section, "SSL settings").

There are minor differences in the way SSL properties are passed to the different types of micro broker clients (MQTTv5, JMS, and MQTTv3). For more information, see the later section, "JMS and MQTTv3 clients."

Listing 3. Connecting with a secure client
private MqttClient myClient;
private static final String BROKER_URL = "ssl://localhost:8883";
private static final String CLIENT_ID = "AnonUser";

public void createClient() throws Exception
{
    // Define the connection options for the MQTTv5 client
    // We'll connect anonymously, so we don't set username/password
    MqttConnectOptions connOpts = new MqttConnectOptions();
    connOpts.setPurge(true);
    // Create the client object
    myClient = new MqttClient(BROKER_URL, CLIENT_ID);
    // We've created the client. For normal use, we'd also define and set
    // a callback handler, but we're only connecting so won't bother.
    myClient.connect(connOpts);
}

When the last statement is executed, the client is presented with a window asking whether the broker’s certificate is to be trusted. On Microsoft® Windows®, it looks like the window shown in figure 1.

Figure 1. Security Alert window
Security Alert window

As the certificate is self-signed (that is, it is not signed by a CA), its trustworthiness cannot be established. The user is therefore given access to the certificate and asked to decide whether to trust it. The default option is to trust the certificate for the current session only. If, instead, you select the Do not trust this certificate option, the certificate is rejected and the SSL connection is not established. Selecting either of these options means that when Lotus Expeditor is shut down and restarted, the window is presented the next time that the client attempts to connect to the micro broker.

The final option, Trust this certificate, tells Lotus Expeditor that you believe the certificate is genuine. The certificate is thus stored in the client’s Lotus Expeditor platform truststore, and the client allows future connections to the micro broker without question.

Check that the connection has been established

At this point, if all went as it should, the client is connected and the following message is posted to the micro broker log:

>Info [3810] FMBT1741 MBroker Client 'AnonUser'
connected on port 8883 from /127.0.0.1:0

Creating a secure bridge connection

Some system topologies require a micro broker to connect to other micro brokers or message brokers using the micro broker’s bridge facility. Bridge connections to micro brokers or IBM WebSphere® MQ can be secured by SSL. Listing 4 shows the code used to set up an SSL bridge connection.

Create the bridge connection

In this example, the remote broker is MBroker, created as above, with a default secure port at 8883. A second micro broker, LocalBroker, runs on the same system, listening on a different port. The code creates a secure MQTTv5 pipe (bridge connection) from LocalBroker to the secure port of MBroker. The same technique can be used for MQTTv3 and MQJMS bridge connections. See the code in listing 4.

The significant difference between creating a bridge connection and a client connection is that the remote broker’s host name (or IP address) is passed to the bridge connection, not its URL. This difference means that the ssl:// prefix is not available to indicate that a secure connection is required. Instead, the setSecure(true) method call on the MQTTConnectionDefinition object performs this task.

Listing 4. Creating a secure bridge connection
private final String BROKER_NAME = "LocalBroker";
private final int REMOTE_SECURE_PORT = 8883;

public void _createSecurePipe(CommandInterpreter ci) throws Exception
{
    BrokerFactory factory = BrokerFactory.INSTANCE;
    LocalBroker lbroker = factory.getByName(BROKER_NAME);
    bridge = lbroker.getBridge();
    PipeDefinition pipeDef = bridge.createPipeDefinition("securePipe");
    MQTTConnectionDefinition secBridge = 
                bridge.createMQTTConnectionDefinition("secConn");
    // Uncomment this method call for an MQTTv3 connection
//  secBridge.setProtocolVersion(3);
    secBridge.setHost("localhost");
    secBridge.setPort(REMOTE_SECURE_PORT);
    secBridge.setSecure(true);
    pipeDef.setConnection(secBridge);
    // A pipe has to have at least one flow...
    FlowDefinition outflow = bridge.createFlowDefinition("secureflow");
    outflow.setSources(new DestinationDefinition[]{
                bridge.createTopicDefinition("SSLBridge/#")});
    outflow.setQos(2);
    pipeDef.addOutboundFlow(outflow);
    Pipe pipe = bridge.addPipe(pipeDef);
    pipe.start();
}

Check that the bridge connection is established

When the pipe is started (in the last line of code in listing 4), LocalBroker is presented with the Certificate trust window just as happens for a normal client. After accepting the certificate, the LocalBroker log shows the message detailed in listing 5.

Listing 5. The LocalBroker log message
>Info [1240] FMBB2006 bridge Pipe securePipe initializing.
>Info [1240] FMBB2004 bridge Pipe securePipe starting. 
>Info [1240] FMBB2000 bridge Pipe securePipe started. 
>Info [1240] FMBB2628 bridge Pipe securePipe has established its outbound connection.

At the remote broker (MBroker), the log shows that the connection has been made:

>Info [3810] FMBT1741 MBroker Client 'securePipe'
connected on port 8883 from /127.0.0.1:0


Mutual authentication

Creating a mutually authenticated connection between the broker and client (or bridge) is slightly more complex. Once again, the broker presents its certificate for validation to a client requesting a connection, which the client accepts or rejects as before. In a mutually authenticated connection, the client (or bridge) also presents its own certificate back to the broker.

The significant difference in this transaction is that the broker is not presented with options to accept or reject the client’s certificate. Instead, the certificate must be already known to the broker (that is, stored in its truststore) prior to the request to establish a mutually authenticated connection. The default Lotus Expeditor truststore is the Java runtime cacerts keystore found at:

$Expeditor_INSTALL_DIR\rcp\eclipse\plugins\$runtime_jre\jre\lib\security\cacerts

That keystore is where the examples that follow store the client certificate.

Setting up the micro broker for mutual authentication

To set up mutual authentication for connections to a micro broker, you have to define the secure port such that it requires a valid client certificate before a connection is allowed.

Set up mutual authentication for an additional port

As you can see in listing 6, it requires just one extra line of code (in bold text) when you add a secure port to a running broker:

Listing 6. Adding a mutually authenticated secure listener to a micro broker
private final String BROKER_NAME = "MBroker";
private final int SECURE_PORT = 8883;
private final String KEYSTORE_DTEE = "/mbrokerdtee.jks";
private final char[] KSPASSWD_DTEE ={'d','e','f','a','u','l','t'};

void _addSecurePort() throws Exception
{
    BrokerFactory factory = BrokerFactory.INSTANCE;
    LocalBroker lbroker = factory.getByName(BROKER_NAME);
    Communications bc = lbroker.getCommunications();
    // Specify the port number in this method call. 8883 is the IANA
    // default port for secure MQTT connections.
    MQTTTCPListenerDefinition mqttld = 
                 bc.createMQTTTCPListenerDefinition(SECURE_PORT);
    mqttld.setSecure(true);
    ServerSSLDefinition ssld = mqttld.createSSLDefinition();
    ssld.setKeyStore(KEYSTORE_DTEE);
    ssld.setKeyStorePassword(KSPASSWD_DTEE);
    ssld.setClientCertificateRequired(true);
    mqttld.setSSLDefinition(ssld);
    bc.addTCPListener(mqttld);
}

Set up mutual authentication for a default secure broker

The same line of code applies when creating a default secure micro broker that requires mutual authentication, as shown in listing 7.

Listing 7. Creating a mutually authenticated default secure micro broker
void createSecureBroker() throws Exception 
{
    BrokerFactory factory = BrokerFactory.INSTANCE;
    BrokerDefinition def = factory.createBrokerDefinition(BROKER_NAME);
    // setPort() is optional – 8883 is used if this method is not called
//  def.setPort(Integer.parseInt(SECURE_PORT));
    def.setDefaultListenerSecure(true);
    ServerSSLDefinition ssld = def.createSSLDefinition();
    ssld.setKeyStore(KEYSTORE_DTEE);
    ssld.setKeyStorePassword(KSPASSWD_DTEE);
    ssld.setClientCertificateRequired(true);
    def.setSSLDefinition(ssld);
    factory.create(def);
}

The log messages from the broker when it is started are identical to those seen when using server authentication.

Note that if you attempt to connect to the broker from a client that does not have a certificate (or that has a certificate that is not known to the micro broker), then an MQTTException with a nested SSLHandshakeException is thrown by the client code.

In the case of an attempted bridge connection with no certificate, the following messages, shown in listing 8, are posted to the LocalBroker (that is, the broker attempting to connect to the secure port) log:

Listing 8. Messages posted to the LocalBroker
> 2009/01/05 14:39:11.843 SEVERE FMBB2117 bridge MQTT connection for the pipe 
securePipe threw an MqttException. 
> 2009/01/05 14:39:11.859 WARNING FMBB2098 bridge Connector Class loading 
threw the exception  (0) - javax.net.ssl.SSLHandshakeException: The operation timed 
out for the Pipe securePipe 

These messages are posted at regular intervals as the LocalBroker retries to establish the connection.

The micro broker with the secure port never sees the connection attempt, as its underlying SSL network stack does not pass the incoming request up to the broker. As a result, no messages are posted to the secure broker’s log.

Creating the keystore for the client (bridge)

As we’ve seen, the client (or bridge) needs to have a certificate to present to the remote micro broker in order for the connection to be allowed.

Create the client (or bridge) keystore

Just as for the broker, you create a keystore containing the client’s credentials, using the keytool utility:

keytool -genkey -alias clientcert -keystore clientdtee.jks -keypass default -storepass
default -dname "CN=MB Client, OU=PVC, O=IBM, C=UK" -keyalg RSA

This utility creates a file containing the client’s key pair and a matching self-signed certificate.

Get a copy of the client’s certificate

As the Lotus Expeditor platform doesn’t provide a way for the micro broker to accept a certificate when the connection is requested, you need to put a copy of the client’s (or bridge’s) certificate into the Lotus Expeditor truststore prior to the request being made. First, you have to obtain a copy of the certificate from the client’s newly created keystore. The keytool utility provides this capability:

keytool -export -alias clientcert -keystore clientdtee.jks -file clientdtee.cer

This capability creates a file (clientdtee.cer) containing the client certificate.

Importing the client (bridge) certificate

As already discussed, Lotus Expeditor uses the cacerts file as its default truststore. You therefore have to import the client or bridge certificate into cacerts.

Importing the client (bridge) certificate into the Lotus Expeditor truststore

You put the keytool utility to work once again to import the certificate:

keytool -import -alias clientcert -file clientdtee.cer -keystore cacerts

This command imports the certificate, but only after you have confirmed that you trust the certificate. This confirmation is a precautionary step to give you the opportunity to reject the certificate if you have any reason to suspect it might not be genuine. Note that the default password for the Lotus Expeditor cacerts keystore is changeit as shown in listing 9.

Listing 9. Confirming trust in the certificate
Enter store password:changeitOwner : CN=MB Client,OU=PVC,O=IBM,C=UK
Issuer : CN=MB Client,OU=PVC,O=IBM,C=UK
Serial number : 4962101e
Valid from Mon Jan 05 13:50:22 GMT 2009 to Sun Apr 05 14:50:22 BST 2009
Certificate fingerprints :
  [MD5]   : a4:ab:83:af:14:2b:40:ae:a8:f4:67:92:d7:48:28:f8
  [SHA-1] : a4:13:05:4a:b2:12:96:ee:35:ee:fd:a8:ef:83:dc:bc:0e:27:44:32

Do you trust this certificate? [no]: yes

You can verify that the certificate is in cacerts by using the list option of the keytool utility:

keytool -list -alias clientcert -keystore cacerts

Assuming that the certificate was successfully imported, you can see the output shown in listing 10 (with the MD5 signature for your client’s certificate):

Listing 10. The MD5 signature for the client certificate
Enter store password: changeitclientcert, Mon Jan 05 15:31:51 GMT 2009, trusted certificate entry,
Certificate fingerprint:
  [MD5]   : a4:ab:83:af:14:2b:40:ae:a8:f4:67:92:d7:48:28:f8

Using the keytool list option against the client keystore allows you to verify that the certificate in the micro broker’s trust store does match the client (bridge) key:

keytool -list -keystore clientdtee.jks

As you can see in listing 11, the MD5 hash of the key matches that of the certificate.

Listing 11. The MD5 hash of the key
Enter store password: defaultKeystore type: jks
Keystore provider: OTI

Keystore contains 1 entry:

clientcert, Mon Jan 05 13:50:22 GMT 2009, key entry,
Certificate fingerprint:
  [MD5]   : a4:ab:83:af:14:2b:40:ae:a8:f4:67:92:d7:48:28:f8

Creating a mutually authenticated client connection

Now that all of the pieces are in place, creating a mutually authenticated connection from a client to a broker is straightforward.

Create a mutually authenticated connection from a client

The client needs to know where to look to find its self-signed certificate to present to the remote broker. The client does this step by providing a Properties object that contains the name of the keystore and its password, as shown (in bold text) in listing12.

Listing 12. Creating a mutually authenticated client connection
private MqttClient myClient;
private static final String BROKER_URL = "ssl://localhost:8883";
private static final String CLIENT_ID = "AnonUser";
private static final String CLIENT_KS = “clientdtee.jks";
private static final String CLIENT_KSPW = “default”:

public void createClient() throws Exception
{
    // Define the connection options for the MQTTv5 client
    // We'll connect anonymously, so we don't set username/password
    MqttConnectOptions connOpts = new MqttConnectOptions();
    connOpts.setPurge(true);
    // Now set the SSL properties for the client
    Properties sslprops = new Properties();
    sslprops.setProperty("com.ibm.ssl.keyStore", CLIENT_KS);
    sslprops.setProperty("com.ibm.ssl.keyStorePassword", CLIENT_KSPW);
    connOpts.setSSLProperties(sslprops);
    // Finally create the client object
    myClient = new MqttClient(BROKER_URL, CLIENT_ID);
    // We've created the client. For normal use, we'd also define and set
    // a callback handler, but we're only connecting so won't bother.
    myClient.connect(connOpts);
}

The log messages posted by the broker are identical to those shown for the server-authentication case.

Creating a mutually authenticated bridge connection

Creating a bridge connection is similarly straightforward. In this example, the keystore and certificate used for the client connection are used for the bridge for simplicity. You might want to create a separate key pair/certificate and keystore for the bridge.

Create a mutually authenticated bridge connection

The bridge also needs to know where to find its self-signed certificate. Unlike the client, the bridge gets this information through the method calls shown in listing 13.

Listing 13. Creating a mutually authenticated bridge connection
private final String BROKER_NAME = "LocalBroker";
private final int REMOTE_SECURE_PORT = 8883;
private static final String BRIDGE_KS = “clientdtee.jks";
private static final String BRIDGE_KSPW = “default”:

public void _createSecurePipe(CommandInterpreter ci) throws Exception
{
    BrokerFactory factory = BrokerFactory.INSTANCE;
    LocalBroker lbroker = factory.getByName(BROKER_NAME);
    bridge = lbroker.getBridge();
    PipeDefinition pipeDef = bridge.createPipeDefinition("securePipe");
    MQTTConnectionDefinition secBridge = 
                bridge.createMQTTConnectionDefinition("secConn");
    // Uncomment this method call for an MQTTv3 connection
//  secBridge.setProtocolVersion(3);
    secBridge.setHost("localhost");
    secBridge.setPort(REMOTE_SECURE_PORT);
    secBridge.setSecure(true);
    SSLDefinition ssld = secBridge.createSSLDefinition();
    ssld.setKeyStore(BRIDGE_KS);
    ssld.setKeyStorePassword(BRIDGE_KSPW.toCharArray());
    secBridge.setSSLDefinition(ssld);
    pipeDef.setConnection(secBridge);
    // A pipe has to have at least one flow...
    FlowDefinition outflow = bridge.createFlowDefinition("secureflow");
    outflow.setSources(new DestinationDefinition[]{
                bridge.createTopicDefinition("SSLBridge/#")});
    outflow.setQos(2);
    pipeDef.addOutboundFlow(outflow);
    Pipe pipe = bridge.addPipe(pipeDef);
    pipe.start();
}

The log messages posted by the local and remote brokers are identical to those shown for the server-authentication case.


SSL settings

There are several places from which an SSL connection can obtain its settings. This fact can sometimes lead to confusion.

As you’ve seen, the client and bridge are similar in the way they are configured to create an SSL connection. The significant difference is that SSL settings are passed to the client through a properties object whereas the bridge uses setter methods to achieve the same goal. SSL settings for micro broker listeners use the same setter methods as the bridge.

The examples use a minimal number of settings to establish SSL connectivity. There are a number of additional settings that allow more complex configurations, but their discussion is beyond the scope of this article.

There is a one-to-one correlation between the two approaches as shown in table 1.

Table 1. Comparing the client property and the bridge setter method
Client propertyBridge setter methodComment
com.ibm.ssl.enabledCipherSuitessetEnabledCipherSuites(String[] ciphers)Specifies the cipher suites that the broker uses for connecting clients
com.ibm.ssl.keyStoresetKeyStore(String keyStore)Sets the path to the SSL/TLS keystore
com.ibm.ssl.keyStorePasswordsetKeyStorePassword(char[] keyStorePassword)Sets the password that is required to load the keystore
com.ibm.ssl.keyStoreProvidersetKeyStoreProvider(String keyStoreProvider)Sets the keystore provider
com.ibm.ssl.keyStoreTypesetKeyStoreType(String keyStoreType)Sets the type that is used for the keystore
com.ibm.ssl.keyManagersetKeyManager(String keyMgrAlgo)Sets the keystore manager
com.ibm.ssl.protocolsetProtocol(String protocol)Sets the version of the SSL/TLS protocol that should be used
com.ibm.ssl.contextProvidersetSecurityProvider(String securityProvider)Sets the security provider that is used to implement the TLS protocol
com.ibm.ssl.trustManagersetTrustManager(String trustMgrAlgo)Sets the algorithm that is used to instantiate a TrustManagerFactory object instead of using the default algorithm available in the platform
com.ibm.ssl.trustStoresetTrustStore(String trustStore)Sets the path name to the SSL/TLS truststore that is used by the broker
com.ibm.ssl.trustStorePasswordsetTrustStorePassword(char[] trustStorePassword)Sets the password of the truststore that is used
com.ibm.ssl.trustStoreProvidersetTrustStoreProvider(String trustStoreProvider)Sets the truststore provider
com.ibm.ssl.trustStoreTypesetTrustStoreType(String trustStoreType)Sets the type of truststore that is used

JMS and MQTTv3 clients

The examples show an MQTTv5 client using an SSL connection. Micro broker JMS and MQTTv3 clients also have their SSL properties provided through a Properties object as shown in table 2.

Table 2. Clients and their SSL properties
ClientSSL properties
MQTTv5The SSL Properties object (sslprops in the examples) is set in the connection options object (connOpts in the examples) passed as a parameter to the client.connect() method
JMS The SSL Properties object is set as an object property on the JmsConnectionFactory. ((JmsConnectionFactory) cf).setObjectProperty(MQTTConstants.MQTT_SSL_PROPERTIES,sslClientProps);
MQTTv3The SSL Properties object is set in the MqttProperties object passed as a parameter to the MqttClientFactory.createMqttClient() methods

More advanced configurations

A large number of options are available when you configure SSL for micro broker. This section provides a brief overview of some of the ways you can use them to meet specific requirements.

Bridging to WebSphere MQ

Micro broker has a specific connector for creating pipes to an instance of WebSphere MQ. The SSL setup for this type of connection is slightly different from that for an MQTT bridge. In addition to the standard properties needed for a bridge connection, some WebSphere MQ-specific properties have to be set. In particular, setQueueManager() and setChannel() are required for the connection definition. The SSL definition also requires that the enabled cipher suites are set; this setting has to match those available at the WebSphere MQ instance. See listing 14.

Listing 14. Creating a bridge connection to WebSphere MQ
private final String BROKER_NAME = "MBroker";
private String mqhost = "swtest.hursley.ibm.com";
private int mqport = 1414;
private String qmgr = "swtest";

public void _createSecureMqPipe() throws Exception
{
    BrokerFactory factory = BrokerFactory.INSTANCE;
    LocalBroker lbroker = factory.getByName(BROKER_NAME);
    bridge = lbroker.getBridge();
    PipeDefinition pipeDef = bridge.createPipeDefinition("secureMqPipe");
    MQJMSConnectionDefinition remote = 
        bridge.createMQJMSConnectionDefinition("remote");
    remote.setHost(mqhost);
    remote.setPort(mqport); 
    remote.setQueueManager(qmgr);
    remote.setChannel("SECURE.CHANNEL");
    remote.setSecure(true);
    SSLDefinition ssld = remote.createSSLDefinition();
    ssld.setEnabledCipherSuites(new String[]{
        "SSL_RSA_WITH_RC4_128_SHA"}); 
    remote.setSSLDefinition(ssld);
    pipeDef.setConnection(remote);

    // A pipe has to have at least one flow...
    FlowDefinition outflow = bridge.createFlowDefinition("secureflow");
    outflow.setSources(new DestinationDefinition[]{
                bridge.createTopicDefinition("SSLBridge/#")});
    outflow.setQos(2);
    pipeDef.addOutboundFlow(outflow);
    Pipe pipe = bridge.addPipe(pipeDef);
    pipe.start();
}

Note that the default setting for WebSphere MQ is that mutual authentication is required. In this example, the remote WebSphere MQ configuration has been changed so that only server (that is, the remote WebSphere MQ instance) authentication is required, so a keystore for the bridge (client) has not been specified. For mutual authentication, a valid bridge (client) certificate matching the key in the bridge’s keystore has to be available to WebSphere MQ.

Binding a listener to a specific IP address

If your system has multiple IP addresses allocated to it, perhaps on different networks, you can specify which of those addresses a listener monitors for connection requests. Requests received on other IP addresses are ignored. This binding is achieved by adding a single line of code (bold in the example shown in listing 15) to your listener definition.

Listing 15. Adding a listener to a specific network interface
private final String BROKER_NAME = "MBroker";
private final int SECURE_PORT = 8883;
private final String KEYSTORE_DTEE = "/mbrokerdtee.jks";
private final char[] KSPASSWD_DTEE ={'d','e','f','a','u','l','t'};

void _addSecurePort() throws Exception
{
    BrokerFactory factory = BrokerFactory.INSTANCE;
    LocalBroker lbroker = factory.getByName(BROKER_NAME);
    Communications bc = lbroker.getCommunications();
    // Specify the port number in this method call. 8883 is the IANA
    // default port for secure MQTT connections.
    MQTTTCPListenerDefinition mqttld = 
                 bc.createMQTTTCPListenerDefinition(SECURE_PORT);
    mqttld.setSecure(true);
    ServerSSLDefinition ssld = mqttld.createSSLDefinition();
    ssld.setKeyStore(KEYSTORE_DTEE);
    ssld.setKeyStorePassword(KSPASSWD_DTEE);
    mqttld.setNetworkInterface("127.0.0.1");
    mqttld.setSSLDefinition(ssld);
    bc.addTCPListener(mqttld);
}

In the example, the new secure listener on port 8883 responds only to incoming requests from clients hosted on the same system as the micro broker. A more practical configuration would be to have an unsecured listener on a port that is on an internal network while the secure listener is on a network that is outside your departmental or company firewall, providing secure communications for connections from external organizations.


Troubleshooting micro broker SSL problems

This section gives suggestions to help you resolve problems that you might encounter when configuring SSL connections for your micro broker. It is by no means exhaustive, but covers some typical errors you might encounter.

Keytool issues

The keytool utility won’t open my keystore – I get a message that says "keytool error (likely untranslated): java.io.IOException: Invalid keystore format"

This issue occurs probably because you are trying to use the J2SE keytool utility to open a DesktopEE keystore.

The keytool utility won’t open my keystore – after entering the keystore password, I get a message that says "keytool error: Unable to open keystore. Check the file path and password"

This issue occurs probably because you are trying to use the DesktopEE keytool utility to open a J2SE keystore.

SSL connection failure issues

The SSL connection can fail for a number of reasons. In some cases, the exception message is straightforward and points to the problem directly, such as "Incorrect password."

In other cases, the message can be vague. Some examples follow.

I get an exception that says "SSL Handshake failure"

This message is presented for a number of different reasons. Check that the micro broker and client (or bridge) have access to their respective keystores and truststores.

I get an Exception that says "SSL-Initialization failed. I/O problem while loading the keystore"

This message is presented for a number of different reasons. It is generated by the underlying SSL code, so, unfortunately, the micro broker cannot provide any additional information. One common cause is that the broker is running in one JVM environment but trying to use a keystore created with the keytool for the other JVM environment.

I get an Exception that says "java.io.IOException: Invalid keystore format"

This exception could indicate that the keystore is corrupted. This exception can occur if you try to use a DesktopEE keystore in the J2SE environment.

Micro broker is trying to access a trust or keystore that is not the one you defined

Micro broker can obtain SSL settings from a number of different places:

  • Specific method calls for the listener or client
  • Broker-wide settings
  • Lotus Expeditor platform settings
  • Java Virtual Machine settings

If you set a system SSL property (javax.net.ssl) on the Java command line, it overrides the Lotus Expeditor platform settings.

You’re trying to connect a client (or bridge) to a secure port on a broker, but it’s failing and you’re not presented with the window requesting you to accept or deny the broker’s certificate

An example of the Security Alert window is shown as figure 1, along with a description of the various options available.

If you’ve previously connected to the broker and selected to trust the broker’s certificate, the client connects without displaying the window shown in figure 1. If this is not the case, you might be running Lotus Expeditor with a JVM that is not one of those shipped with the product.

A trust manager controls the way that incoming unknown certificates are handled. The Lotus Expeditor JVM trust manager displays the Security Alert window; other JVMs might not do so.

You created your keystore with the DesktopEE keytool utility, and you’ve checked that your broker is running in a DesktopEE JVM, but you’re still having problems

Did you use the –keyalg RSA parameter when you first created your keypair with the keytool utility? The default key algorithm used by keytool is DSA, which is not supported by the DesktopEE JVM.


Conclusion

This article has provided a simple introduction to implementing secure, SSL-enabled connections to and from a micro broker. The information provided is sufficient to allow users to protect their data as it travels over the network with a minimum of knowledge about the detailed operation of SSL. For those with the need and/or the interest, there is a lot more to know about SSL.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into IBM collaboration and social software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Lotus, WebSphere
ArticleID=381195
ArticleTitle=Wrinkle-free SSL: Using TLS/SSL with micro broker v3 in IBM Lotus Expeditor
publish-date=06292009