简体   繁体   English

AWS IOT 在 Android 中即时注册证书

[英]AWS IOT Just-in-Time Registration of Certificate in Android

I am integrating the JITR by follwing this article.我正在按照这篇文章整合 JITR。

https://aws.amazon.com/blogs/iot/just-in-time-registration-of-device-certificates-on-aws-iot/ 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'.我完成了所有这些步骤,并且能够通过命令行“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.第一次运行“mosquitto_pub”命令时,它调用 lambda 函数对其进行授权并附加策略,第二次它成功地将消息发布到 IOT。

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.但是,当我尝试在 android SDK 中对此进行身份验证时,出现“握手”失败错误。 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)a MqttException (0) - javax.net.ssl.SSLHandshakeException:握手在 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。开始握手(OpenSSLSocketImpl.java:441)在 org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:93)在 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 (外部/ boringssl/src/ssl/s3_pkt.c:972 0xb9215530:0x00000001)at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(本地方法)a t com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:353) t 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.有趣的是,如果设备证书已经激活并且我们尝试通过 android 发布消息,它就会成功发布。 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'.我在 mosquitto 调用和 android 代码之间看到的区别是 mosquitto 正在执行一个命令来连接和发布消息,而 AWS SDK 中的 PAHO-MQTT 需要在发布之前先连接,我在“连接”中遇到异常'. I even did not get any logs in AWS logs.我什至没有在 AWS 日志中得到任何日志。

Possible Reasons可能的原因

SSL/TLS Handshake can fail for various reasons and not only for certificate validation problems. SSL/TLS 握手可能因各种原因而失败,而不仅仅是证书验证问题。

It could be for:它可能是为了:

  • Not sharing same cipher suites不共享相同的密码套件
  • Not sharing SSL versions不共享 SSL 版本
  • Certificate validation证书验证
  • Intent to change TLS version意图更改 TLS 版本
  • Others issues其他问题

The best way to figure out what your problem is is to install Wireshark and see the handshake messages.找出问题所在的最佳方法是安装Wireshark并查看握手消息。 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.然后根据从服务器发送到客户端的 SSL 警报消息,您可以获得有关 SSL 握手失败的更多信息,具体发生在何处。

Possible solution可能的解决方案

Helpful resources有用的资源

Make your own KeyStoreHelper that put CA Cert into your KeyStore, and use it instead of AWS IoT SDK's KeyStoreHelper .制作您自己的 KeyStoreHelper 将 CA 证书放入您的 KeyStore,并使用它代替 AWS IoT SDK 的KeyStoreHelper

Note: I omitted all exception handlings in the codes below, and createKeyPair(), createCSR(), parsePemObject() and signCSR() are my methods.注意:我在下面的代码中省略了所有异常处理,createKeyPair()、createCSR()、parsePemObject() 和 signCSR() 是我的方法。

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:在连接和发布 MQTT 之前,获取链接在 KeyStore 中的 CSR 和 CA 证书,如下所示:

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".一旦您首次向端点发布任何消息,AWS IoT Core 将自动创建一个“事物”。

I hope this helps you.我希望这可以帮助你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM