AWS IOT Just-in-Time Registration of Certificate in Android

4

I am integrating the JITR by follwing this article.

https://aws.amazon.com/blogs/iot/just-in-time-registration-of-device-certificates-on-aws-iot/

I am done with all these steps and able to authenticate the certificate through command-line 'mosquitto_pub'.

First time when I run 'mosquitto_pub' command it calls lambda function to authorize it and attach policy and second time it publish message to IOT successfully.

Here is command that I am using.

mosquitto_pub --cafile ../root.cert --cert hassanAndCACert.crt --key hassan.key -h <###>.iot.us-east-1.amazonaws.com
-p 8883 -q 1 -t  topic5 -i  123456789 --tls-version tlsv1.2 -m '{"hello":"3"}' -d

But when I try to authenticate this in android SDK I am getting 'handshake' fail error. Here is exception that I am getting.

MqttException (0) - javax.net.ssl.SSLHandshakeException: Handshake failedat org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38)at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:664)at java.lang.Thread.run(Thread.java:818)Caused by: javax.net.ssl.SSLHandshakeException: Handshake failedat com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:441)at org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:93)at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:650) ... 1 moreCaused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0xb91e9b40: Failure in SSL library, usually a protocol errorerror:100c5416:SSL routines:ssl3_read_bytes:SSLV3_ALERT_CERTIFICATE_UNKNOWN (external/boringssl/src/ssl/s3_pkt.c:972 0xb9215530:0x00000001)at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:353)

Interestingly if device-certificate is already active and we try to publish message through android it get published successfully. The only problem is to authenticate the certificate at first call. If my code is faulty it should not send message against activated-certificates.

The difference that I see between mosquitto call and the android-code is that mosquitto is making a single command to connect and publish the message, where as PAHO-MQTT in AWS SDK needs to connect first before publishing, and I get exception in 'connect'. I even did not get any logs in AWS logs.

android
mqtt
iot
paho
aws-certificate-manager
asked on Stack Overflow Jul 10, 2017 by Hassan Siddique • edited Jul 10, 2017 by Hassan Siddique

2 Answers

1

Possible Reasons

SSL/TLS Handshake can fail for various reasons and not only for certificate validation problems.

It could be for:

  • Not sharing same cipher suites
  • Not sharing SSL versions
  • Certificate validation
  • Intent to change TLS version
  • Others issues

The best way to figure out what your problem is is to install Wireshark and see the handshake messages. Then based on the SSL alert message sent from server to client you can have more information on the SSL handshake failure, where specifically it happened.

Possible solution

Helpful resources

answered on Stack Overflow Jul 13, 2017 by Suriyaa
0

Make your own KeyStoreHelper that put CA Cert into your KeyStore, and use it instead of AWS IoT SDK's KeyStoreHelper.

Note: I omitted all exception handlings in the codes below, and createKeyPair(), createCSR(), parsePemObject() and signCSR() are my methods.

public class MyKeystoreHelper {

    public KeyStore createKeystoreJIT(String certId, String keystorePath,
        String keystoreName, String keystorePassword, HashMap<String, String> directory) {

        // Generate KeyPair
        KeyPair key = createKeyPair();

        // Generate CSR
        PKCS10CertificationRequest csr = createCSR(key, directory);

        // Read CA Private key
        PEMKeyPair pemKey = (PEMKeyPair)parsePemObject(context, PATH_TO_CAROOT_KEY_FILE);
        KeyPair caKey = new JcaPEMKeyConverter().getKeyPair(pemKey);

        // Read CA Cert
        X509CertificateHolder pemCert = (X509CertificateHolder)parsePemObject(context, PATH_TO_CAROOT_CERT_FILE);
        X509Certificate caCert = new JcaX509CertificateConverter().getCertificate(pemCert);
        X500Name issuer = pemCert.getIssuer();

        // Generate CA Signed CSR
        X509Certificate cert = signCSR(csr, caKey.getPrivate(), caCert, issuer);

        // Create Key Store
        saveKeystore(certId, cert, caCert, key.getPrivate(), keystorePath, keystoreName, keystorePassword); // <-- HERE!! Pass CA Cert

        KeyStore keystore = getKeystore(certId, keystorePath, keystoreName, keystorePassword);
        return keystore;

    }
    ....

    private boolean saveKeystore(String certId, X509Certificate cert, X509Certificate caCert,
        PrivateKey privatekey, String keystorePath, String keystoreName, String keystorePassword) {

        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        File keystoreFile = new File(keystorePath, keystoreName);

        if( ! keystoreFile.exists()) {
            createKeystore(keystorePath, keystoreName, keystorePassword);
        }

        FileInputStream fis = new FileInputStream(keystoreFile);
        keystore.load(fis, keystorePassword.toCharArray());
        fis.close();

        keystore.setCertificateEntry(certId, cert);
        keystore.setKeyEntry(certId, privatekey, keystorePassword.toCharArray(), new Certificate[] { cert, caCert }); // <-- HERE!! put CA Cert

        String caCertId = certId + "_CA";
        keystore.setCertificateEntry(caCertId, caCert);
        keystore.setKeyEntry(caCertId, privatekey, keystorePassword.toCharArray(), new Certificate[] { caCert });

        String keystoreFileAndPath;

        if(keystorePath.endsWith("/")) {
            keystoreFileAndPath = keystorePath + keystoreName;
        } else {
            keystoreFileAndPath = keystorePath + "/" + keystoreName;
        }

        FileOutputStream fos = new FileOutputStream(keystoreFileAndPath);
        keystore.store(fos, keystorePassword.toCharArray());
        fos.close();

        return true;

    }
     ....

    private KeyStore getMemoryKeystore(KeyStore customerKeystore, String certId, String customerKeystorePassword) {

        KeyStore memoryKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
        memoryKeystore.load(null);

        X509Certificate cert = (X509Certificate) customerKeystore.getCertificate(certId);
        memoryKeystore.setCertificateEntry("cert-alias", cert);

        Key key = customerKeystore.getKey(certId, customerKeystorePassword.toCharArray());

        String caCertId = certId + "_CA";
        X509Certificate caCert = (X509Certificate) customerKeystore.getCertificate(caCertId); // Pull CA Cert
        memoryKeystore.setCertificateEntry("cacert-alias", caCert);

        memoryKeystore.setKeyEntry("key-alias", key, AWS_IOT_INTERNAL_KEYSTORE_PASSWORD.toCharArray(), new Certificate[] { cert, caCert }); // <-- HERE!!
        return memoryKeystore;

    }
    ....    
}

And before you connect and publish MQTT, get CSR and CA Cert that are chained within the KeyStore as below:

keystoreHelper = new MyKeystoreHelper(...);

if(keystoreHelper.isKeystorePresent(keystorePath, KEYSTORE_NAME)) {
    keystore = keystoreHelper.getKeystore(CERTIFICATE_ID, keystorePath, KEYSTORE_NAME, KEYSTORE_PASSWORD);
} else {
    // Create your own KeyStroe if it is not exist yet.
    HashMap<String, String> directory = getDirectory(); // X.500 directory items for CSR
    keystoreHelper.createKeystoreJIT(CERTIFICATE_ID, keystorePath, KEYSTORE_NAME, KEYSTORE_PASSWORD, directory);
    keystore = keystoreHelper.getKeystore(CERTIFICATE_ID, keystorePath, KEYSTORE_NAME, KEYSTORE_PASSWORD);
}

Once you publish any message to the endpoint for the first time, AWS IoT Core will automatically create a "Thing".

I hope this helps you.

answered on Stack Overflow May 27, 2020 by Masaki Ohashi • edited Nov 6, 2020 by Masaki Ohashi

User contributions licensed under CC BY-SA 3.0