Search code examples
azure-iot-hubazure-sdk-for-java

Azure Device SDK fails to connect to IoTHub


I'm trying to connect to a Azure IoTHub via using the device client library. I'm already having a X509-certificate registered at the dps and my private key.

As this is going to be an emulator for an IoT-Device (for testing) I know that the certificates are valid and work for connecting to the IoTHub (at least if I use them on the IoT-Device). But if I'm using the DeviceClient library, securityProvider.getSSLContext() fails with the following error:

Caused by: java.security.KeyStoreException: Certificate chain is not valid

I'm trying to connect to the iothub via

SecurityProvider securityProvider = new SecurityProviderX509Cert(clientCertificate, privateKey, Collections.singleton(ioTHubCaCertificate));
DeviceClient deviceClient = new DeviceClient("enox-iot-dev.azure-devices.net", deviceId, securityProvider, IotHubClientProtocol.MQTT);
deviceClient.open(true);

For the ioTHubCaCertificate I'm using the digicert certificate.


Solution

  • The certificate chain issue occurs because the jar has trouble verifying the signed JAR due to an inability to find a valid certification path. This is typically caused by missing or incorrect intermediate/root certificates.

    To avoid this issue, you can directly use the certificate key as shown below:

    private static final String MainPem = "-----BEGIN CERTIFICATE-----\n" +
    "MjsjjxjhhxggIIFOjCCAyKgAwIBAgIJAPzMa6s7mj7+MA0GCSqGSIb3DQEBCwUAMCoxKDAmBgNV\n" +
        ...
    "MDMwWhcNMjAzxxjjxMTIyMjEzMDMwWjAqMSgwJgYDVQQDDB9BenVyZSBJb1QgSHViIENB\n" +
    "-----END CERTIFICATE-----";
    
    

    I refer this MSDOC for an X.509 certificate simulated device connecting to Azure IoT Hub and code taken from git

            SecurityProvider securityProviderX509 = new SecurityProviderX509Cert(leafPublicCert, leafPrivateKey, signerCertificates);
            ProvisioningDeviceClient provisioningDeviceClient = ProvisioningDeviceClient.create(
                GLOBAL_ENDPOINT,
                ID_SCOPE,
                PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL,
                securityProviderX509);
    
            ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult = provisioningDeviceClient.registerDeviceSync();
            provisioningDeviceClient.close();
    
            if (provisioningDeviceClientRegistrationResult.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
            {
                System.out.println("IotHub Uri : " + provisioningDeviceClientRegistrationResult.getIothubUri());
                System.out.println("Device ID : " + provisioningDeviceClientRegistrationResult.getDeviceId());
    
                // connect to iothub
                String iotHubUri = provisioningDeviceClientRegistrationResult.getIothubUri();
                String deviceId = provisioningDeviceClientRegistrationResult.getDeviceId();
                DeviceClient deviceClient = new DeviceClient(iotHubUri, deviceId, securityProviderX509, IotHubClientProtocol.MQTT);
                deviceClient.open(false);
    
                System.out.println("Sending message from device to IoT Hub...");
                deviceClient.sendEvent(new Message("Hello world!"));
                deviceClient.close();
            }
    
    

    enter image description here

    Refer to this git to directly connect with Azure IoT Hub using X.509 certificates without going through the Device Provisioning Service (DPS).

    Update: Thanks, @Simon, for the additional statements below.

    I don't want to open a discussion/an issue on openjdk because I can't do this on Github, so:

    The root cause was this check. My ca-certificate had not the correct subject because it was from a sub-ca.

    I really don't get what's the purpose of this. It is irritating for the developer because passing the certificate-chain seems like it is actually doing something with it. But this check really does not make any sense because you could easily create an unsigned certificate that just matches this one plain property.