I am running Mosquitto MQTT for TLS1.2 successfully with certificates generated from OpenSSL
and using in the mosquitto configuration file. This also involves the Java Client to manually specify the CA certificate file which connects to mosquitto
I want to use the DigiCert Global Root CA
which exists in my Java keystore.
The Mosquitto configuration file for this looks like the following:
cafile .\m2mqtt_ca.crt
# PEM encoded server certificate.
certfile .\m2mqtt_srv.crt
# PEM encoded keyfile.
keyfile .\m2mqtt_srv.key
tls_version tlsv1.2
These certificates were generated using OpenSSL using the following commands:
# generate key
openssl genrsa -des3 -out m2mqtt_ca.key 2048
# create CA certificate
openssl req -new -x509 -days 3650 -key m2mqtt_ca.key -out m2mqtt_ca.crt
# create private key for the server
openssl genrsa -out m2mqtt_srv.key 2048
# create certificate request from CA
openssl req -new -out m2mqtt_srv.csr -key m2mqtt_srv.key
# verify and sign the certificate request
openssl x509 -req -in m2mqtt_srv.csr -CA m2mqtt_ca.crt -CAkey m2mqtt_ca.key -CAcreateserial -out m2mqtt_srv.crt -days 3650
And it can be consumed in a client java app using the following:
package acme.messaging;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.Security;
public class SslUtil {
public static final SSLSocketFactory Instance;
static {
Instance = SslUtil.getSocketFactory(
"N:\\work\\acme\\mqtt-ssl\\messaging\\mqtt\\certs\\m2mqtt_ca.crt");
}
private static X509CertificateHolder loadCACert(String caCrtFile) throws IOException {
PEMParser reader =
new PEMParser(
new InputStreamReader(new ByteArrayInputStream(
Files.readAllBytes(Paths.get(caCrtFile)))));
X509CertificateHolder caCert = (X509CertificateHolder) reader.readObject();
reader.close();
return caCert;
}
static SSLSocketFactory getSocketFactory(
final String caCrtFile) {
try {
Security.addProvider(new BouncyCastleProvider());
JcaX509CertificateConverter jcaX509CertificateConverter = new JcaX509CertificateConverter();
X509CertificateHolder caCertificateHolder = loadCACert(caCrtFile);
// CA certificate is used to authenticate server
KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", jcaX509CertificateConverter.getCertificate(caCertificateHolder));
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
// finally, create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(null, tmf.getTrustManagers(), null);
return context.getSocketFactory();
} catch (Exception ex) {
return null;
}
}
}
// consume
String uri = "ssl://localhost:1884";
String clientId = "1002";
final MqttMessage mqttMessage = new MqttMessage();
String messageText = "Some data";
mqttMessage.setPayload(messageText.getBytes());
try (MqttClient client = new MqttClient(uri, clientId)) {
if (!client.isConnected()) {
final MqttConnectOptions options = new MqttConnectOptions();
options.setUserName("acme-user");
options.setPassword("acme-user".toCharArray());
options.setSocketFactory(SslUtil.Instance);
client.connect(options);
}
mqttMessage.setRetained(true);
client.publish("test/writeme", mqttMessage);
}
Is it possible to use certificates that are based on the DigiCert Global Root CA
in Mosquitto?
At present it exists in my Java Keystore:
It can be exported, but not sure whether this can be used in the OpenSSL workflow correctly. I have tried using it in the steps above but this yielded errors. The idea being to generate the same files and import those into the mosquito configuration.
Here I did try to use the generated file and a downloaded one from DigiCert
The following command line generated on DigiCert website
openssl req -new -newkey rsa:2048 -nodes -out localhost.csr -keyout localhost.key -subj "/C=GB/ST=Durham/L=Durham/O=quorum/CN=localhost"
Can't load ./.rnd into RNG
8016:error:2406F079:random number generator:RAND_load_file:Cannot open file:crypto\rand\randfile.c:88:Filename=./.rnd
Generating a RSA private key
......................................................................................................................................................................+++++
.....................................................................................................+++++
writing new private key to 'localhost.key'
NOTE: I am lost with my order of events here.
Then these:
openssl x509 -req -in localhost.csr -CA DigiCertAssuredIDRootCA.crt -CAkey localhost.key -CAcreateserial -out m2mqtt_srv.crt -days 3650
errors with:
Signature ok
subject=C = GB, ST = Durham, L = Durham, O = quorum, CN = localhost
unable to load certificate
9896:error:0909006C:PEM routines:get_name:no start line:crypto\pem\pem_lib.c:745:Expecting: TRUSTED CERTIFICATE
Also tried this:
openssl x509 -req -in localhost.csr -CA DigiCertAssuredIDRootCA.crt -CAkey DigiCertAssuredIDRootCA.crt.pem -CAcreateserial -out m2mqtt_srv.crt -days 3650
errors with:
Signature ok
subject=C = GB, ST = Durham, L = Durham, O = quorum, CN = localhost
unable to load certificate
12904:error:0909006C:PEM routines:get_name:no start line:crypto\pem\pem_lib.c:745:Expecting: TRUSTED CERTIFICATE
As hashed out in the comments.
You can not sign a certificate as a trusted CA (if you could why would you ever trust the CA?).
You have to generate a CSR (Certificate Signing Request) and send this to the CA for them to sign it (and usually charge you for it). They will also prove that you own the domain the certificate will represent. (LetsEncrypt does this all for you using the certbot tool and will do it for free).
Also no trusted CA will ever sign a certificate for localhost
.