Client certificate documentation

Client certificate access

External systems and individual IoT devices (hereafter collectively called "devices") can send condition reports to the withthegrid application. Devices identify themselves with a client certificate or a token. This documentation is about client certificate access. Devices with a certificate can interact with the platform by sending:

  • HTTPS requests to a URL that starts with https://iot-api.withthegrid.com/ (self signed root CA, valid until 2049)
  • CoAP (over DTLS) request to api.withthegrid.com:5684

As developer, you can create a certificate signing request (CSR) with your private key:

openssl req -new -sha256 -key developer_private.pem -out developer.csr -subj "/O=supplier/OU=[connectivityEnvironmentHashId]/CN=[Certificate name]"

The subject of the CSR has the following requirements:

  • O should be supplier
  • OU should be your connectivity environment hashId (the value can be found in the connectivity environment under "Device authentication", "Client certificate access")
  • CN should be provided, with any name you like. It will be listed in the application under this name, which cannot be changed afterwards
  • all three fields should be utf-8 encoded

You upload the CSR to the withthegrid connectivity environment (under "Device authentication", "Certificate access"). The withthegrid application signs it into the chain and the resulting intermediate (developer) certificate can be downloaded. You can then create device certificates in similar fashion: devices have a private key and create a CSR. You sign that into the chain with your private key and intermediate certificate.

Devices should include the device certificate as client certificate in their HTTP or CoAP requests. The withthegrid server inspects the incoming request and tries to match the certificate to a known intermediate (developer) certificate. If that is found, the identifier function belonging to that certificate is run, with the incoming request as parameter. The identifier function is then expected to return the device type hashId and a identifier that uniquely identifies the device among all devices in the connectivity environment.

The identifier function should:

  • be written in Typescript,
  • be named handle
  • have the following signature: handle(args: Arguments): Result

When the identifier function throws an error or when the return is conform the signature, the request will be rejected with a 502 status code.

When the device type is not known by the application, the request will be rejected with a 404 status code. Otherwise:

  • when the device identifier does not yet exist, a new device is created and the event handler of the specified device type is run to process the request
  • when the device identifier does exist and the device matches the device type, the event handler of the specified device type is run to process the request
  • when the device identifier does exist and the device does not match the provided device type, the request will be rejected with a 502 status code.

Example

/**
* This identifier assumes that the device presents a certificate
* that has a subject with OU=deviceTypeHashId and CN=deviceIdentifier
*/

function handle(args: Arguments): Result {
// the subjects of the device are in the first element of the subjects array,
// those of its parent in the second, and so forth.
const deviceSubjects = args.request.certificate.subjects[0];
if (deviceSubjects === undefined) {
throw new Error('Device certificate subjects not available');
}
// the device is using utf8
const cnSubject = deviceSubjects.find((s) => s.key.value === 'CN' && s.key.encoding === 'utf8');
if (cnSubject === undefined) {
// will result in a 502
throw new Error('Device certificate CN subject is not available');
}
const ouSubject = deviceSubjects.find((s) => s.key.value === 'OU' && s.key.encoding === 'utf8');
if (ouSubject === undefined) {
// will result in a 502
throw new Error('Device certificate OU subject is not available');
}

return {
deviceTypeHashId: ouSubject.value.value,
deviceIdentifier: cnSubject.value.value, // should uniquely identify a device
};
}

Inspecting activity

You can inspect activity on this identifier function by going to "Activity" in the connectivity environment and filtering "Type" on "Handle incoming request" and "Certificate" on the subject CA chosen for the intermediate certificate.