Lamassu IoT Docs

Device enrollment with EST

EST protocol guide for Lamassu IoT, with DMS configuration for the PKI administrator and the endpoint reference for device integrators.

EST: Enrollment over Secure Transport

Lamassu IoT implements the EST (Enrollment over Secure Transport) protocol described in RFC 7030 as its onlyone of its device certificate -enrollment mechanism for devices. Each DMS exposes the EST endpoints, mechanisms — the other is CMP (RFC 9483). Each DMS exposes one of the two protocols, never both at once. The DMS that exposes EST validates the device and applies its own authentication, registration and renewal policies. In doing so, it acts — acting as the Registration Authority (RA) facing the client.

The entity that issues and cryptographically signs the certificate is always the CA associated with the DMS, not the DMS. The DMS coordinates the process: it receives the CSR, authenticates the device and applies the issuance policy. This distinction matters for the PKI administrator when selecting the EnrollmentCA and for the integrator when interpreting the chain of trust received from /cacerts.

This guide is split in two parts:

If you only need a quick orientation, this is the recommended route:


DMS configuration

Prerequisites

Before configuring a DMS for EST, the following resources must exist on the platform:

  • An available CA: the CA acting as EnrollmentCA must be created and active in Lamassu. If the authentication mode is CLIENT_CERTIFICATE, the validation CAs for the bootstrap certificate must also have been created.
  • A created DMS: the DMS this configuration applies to must already exist. The EST configuration is applied over an existing DMS, it does not create one.

It is worth separating four configuration blocks:

  • How the initial enrollment will be performed, including the registration mode and client authentication.
  • How the re-enrollment of already-issued certificates will be handled.
  • How the cacerts endpoint will be configured to distribute the CAs devices use as trust anchors.
  • Whether server-side key generation through ServerKeyGen will be enabled.

Enrollment configuration

Enrollment Device Registration

This section defines how new devices are registered and with which credentials they authenticate.

  • Registration Mode: determines the behavior when receiving an initial enrollment. In JITP (Just-In-Time Provisioning) mode, the device is created automatically the first time it presents itself, applying the tags, metadata and icon defined in the provisioning profile. In PRE_REGISTRATION mode, the device must already exist on the platform; if it does not, the request is rejected with device not preregistered. JITP mode suits large fleets where the inventory is built during deployment; PRE_REGISTRATION fits high-security environments where each device is explicitly approved before receiving a certificate.
  • Tags: applied to each device automatically created by this DMS. They are optional and only take effect in JITP mode. Tags improve device search.
  • Icon: applied to each device automatically created by this DMS. It is optional and only takes effect in JITP mode. Purely visual.

Enrollment Settings

  • Enrollment CA: identifies the CA that will sign the identity certificates issued through this DMS. Its choice determines the chain of trust the device will receive and the root against which backend systems will validate fleet certificates.
  • Allow Override Enrollment (enable_replaceable_enrollment): allows an already-enrolled device to enroll again through /simpleenroll. When active, the previous certificate is revoked with the reason superseded and the identity slot is updated. When inactive, a second simpleenroll attempt is rejected with forbiddenNewEnrollment. This option must be enabled with care in production environments, since it lets you replace a device's identity without going through the re-enrollment flow.
  • Verify CSR Signature (verify_csr_signature): controls whether the server validates the CSR signature against the public key declared in it (PKCS#10 proof of possession). When inactive, a requester could submit a CSR with a public key it does not control and obtain a certificate bound to it. For IoT fleets on open networks it must be enabled.
  • Authentication Mode: defines how the DMS validates the device identity during enrollment. It can require an mTLS certificate (CLIENT_CERTIFICATE), delegate authorization to a webhook (EXTERNAL_WEBHOOK), combine both controls (CLIENT_CERTIFICATE_AND_EXTERNAL_WEBHOOK) or accept unauthenticated requests (NO_AUTH). The latter must be limited to isolated labs or provisioning networks.

When the mode is CLIENT_CERTIFICATE or CLIENT_CERTIFICATE_AND_EXTERNAL_WEBHOOK, these parameters are also configured:

  • Validation CAs: list of trusted CAs to validate the bootstrap certificate presented by the device.
  • Chain Validation Level: maximum depth of the validation chain. The value -1 validates the full chain up to the root CA.
  • Allow Authenticating Expired Certificates: if active, accepts expired client certificates. Useful in recovery scenarios where the device could not renew its certificate in time.

When the mode includes EXTERNAL_WEBHOOK, these parameters are also configured:

  • Webhook URL: address of the external service that will decide authorization.
  • HTTP Method: HTTP method used to invoke the webhook. Supports POST (default) or PUT; both are equivalent for the webhook, only the verb Lamassu uses changes.
  • Timeout: maximum wait time for the webhook call. If not specified, the default is 10 seconds; after that without a response, Lamassu denies the operation.
  • Webhook Authentication Mode: authentication mode of the webhook toward the external service, for example ApiKey, OIDC or mTLS.

The exact contract the webhook service must implement (request and response schema, examples) is published as an OpenAPI specification: authorization webhook reference.

Issuance profile

The behavior of the issued certificate is not determined solely by the device's CSR, but by the issuance profile (IssuanceProfile) associated with the DMS. The integrator does not configure this profile, but its rules affect the content of the final certificate:

  • HonorSubject controls whether the CSR's subject is copied verbatim into the certificate. If disabled, the CA applies the profile's template and ignores the received subject.
  • HonorKeyUsage decides whether the CSR's KeyUsage is respected or replaced by the one defined in the template.
  • HonorExtendedKeyUsages applies the same rule to ExtKeyUsage.
  • HonorExtensions controls whether the rest of the CSR's extensions, such as SAN or CDP, are included in the certificate.

This matters for the integrator: the KeyUsage, ExtKeyUsage and SANs declared in the CSR may not appear as-is in the final certificate if the profile decides otherwise. Check with the PKI administrator if the issued certificate does not contain the expected extensions.

Re-enrollment configuration

The Re-Enrollment Settings section controls the certificate renewal process. Re-enrollment can only be performed while the certificate is within the configured window, and optionally also when the certificate has already expired.

  • Revoke On Re-Enroll: if active, the previous certificate is automatically revoked with the reason superseded once the new one is issued.
  • Allow Expired Renewal: allows renewing an already-expired certificate. Useful when a device has been offline and could not renew within the window.
  • Allowed Renewal Delta: time window before expiration during which re-enrollment is allowed. Before this window opens, the request is rejected with invalid reenroll window.
  • Preventive Renewal Delta: time before expiration from which preventive renewal alerts are raised.
  • Critical Renewal Delta: threshold closest to expiration that triggers critical status alerts.
  • Additional Validation CAs: additional CAs accepted as issuers of the active certificate during re-enrollment. It allows migrating to a new enrollment CA without interrupting the renewal of devices already enrolled with the previous CA.

ServerKeyGen configuration

The Server Key Generation section lets Lamassu generate the device's key pair on the server and return it together with the issued certificate. It is useful for devices with limited cryptographic capability.

The Enable Server-Side Key Generation option activates the /serverkeygen endpoint. When inactive, any call to that endpoint is rejected with server key generation not enabled.

Key Type selects the algorithm: RSA or ECDSA. The Key Size (bits) field determines the key size. For ECDSA, the size in bits also determines the curve: 256 corresponds to P-256, 384 to P-384 and 521 to P-521.

The key is generated in the server's software engine, not in a hardware Crypto Engine such as AWS KMS or PKCS#11. The only protection of the key in transit is the TLS channel; no additional encryption (CMS EnvelopedData) is applied to the key before sending it to the device. See the /serverkeygen section of the integrator reference for the exact response format and security considerations.

Verify the configuration

Once the enrollment, re-enrollment and ServerKeyGen settings are applied, you can confirm the DMS is operational as follows:

  • From the console: open the DMS detail and check that the EST section shows the assigned enrollment CA and the configured authentication mode.
  • With a test request: invoke the DMS's /cacerts endpoint from a curl client with server trust. A 200 OK response with base64 content confirms the DMS is active and the enrollment CA is reachable.
curl -s --cacert lamassu-trust.pem \
  "https://<EST_HOST>/.well-known/est/<DMS_ID>/cacerts" | head -c 100

If the DMS does not exist or the CA is not available, the server responds with DMS not found or another error message. See the Diagnostics and compatibility section to interpret error messages.


Device integration

This section is aimed at device integrators.

Lamassu's EST endpoints are published under the /.well-known/est prefix and always require the presence of the {aps} path segment, interpreted as the identifier of the DMS the operation targets. The association is direct: the {aps} value received in the URL is used as the DMS ID when querying the internal store. Variants registered without {aps} (for example /.well-known/est/cacerts) are routed in the code, but their handler requires the APS field and returns 400 Bad Request with the message Field validation for 'APS' failed when the segment is not provided.

This part follows the integrator's usual working order: first the prerequisites and the authentication model, then the reference for each endpoint and, finally, complete examples and diagnostic material.

Before you begin

The PKI administrator must have configured a DMS with the EST_RFC7030 protocol, an assigned enrollment CA and at least one authentication mode. Without that prior configuration, Lamassu rejects any enrollment request. The details of that configuration are in the DMS configuration section of this same page.

Before making the first call, make sure you have:

  • The EST host URL, for example https://est.lamassu.example, provided by the PKI administrator.
  • The server trust anchor (lamassu-trust.pem), root or intermediate, with which the client will validate Lamassu's TLS.
  • The bootstrap certificate and its private key if the DMS uses CLIENT_CERTIFICATE or CLIENT_CERTIFICATE_AND_EXTERNAL_WEBHOOK. This identity must come from one of the configured ValidationCAs, usually a provisioning CA different from the enrollment CA.
  • The DMS ID ({aps}), which must appear in every EST route.

Flow overview

  • Download trust with /cacerts: the client validates the server's TLS and receives the certificates in PKCS#7 or PEM.
  • Perform initial enrollment with /simpleenroll: present the bootstrap certificate or the credential defined by AuthMode to obtain the device certificate.
  • Renew with /simplereenroll: authenticate with the device's current certificate to obtain a new one.
  • Use /serverkeygen only if you need server-side key generation: it applies the same authentication as /simpleenroll and returns the PKCS#8 private key together with the certificate.

Client authentication model

Lamassu's implementation recognizes four authentication configurations for /simpleenroll, expressed through the DMS's EnrollmentOptionsESTRFC7030.AuthMode field. Three are simple modes (CLIENT_CERTIFICATE, EXTERNAL_WEBHOOK, NO_AUTH) and one combines two controls (CLIENT_CERTIFICATE_AND_EXTERNAL_WEBHOOK). The client must present itself in the mode the DMS expects; any other combination fails the enrollment.

Mode (AuthMode)DescriptionExpected credentialApplies in enrollApplies in reenroll
CLIENT_CERTIFICATEmTLS with X.509 certificateClient certificate and private keyYesYes (with the active certificate)
CLIENT_CERTIFICATE_AND_EXTERNAL_WEBHOOKmTLS plus webhook validationClient certificate and private key, plus webhook approvalYesNot as a combination; in reenroll both controls are not kept
EXTERNAL_WEBHOOKExternal authorization webhookNone on the TLS channelYesNo, degrades to NO_AUTH
NO_AUTHNo authenticationNoneYes (restricted use)Yes (restricted use)

JWT is not a valid authentication mode in EST. The JWT identity extractor exists in the code but is only evaluated in Lamassu's administration API. Any Bearer token sent to /.well-known/est/... is silently ignored. When the DMS is configured with AuthMode: CLIENT_CERTIFICATE, a request carrying only a JWT is rejected for lack of a client certificate. When the DMS is in AuthMode: NO_AUTH, the request is processed as such and the JWT neither adds nor removes authorization.

  • CLIENT_CERTIFICATE: the client presents an X.509 certificate issued by one of the CAs declared in AuthOptionsMTLS.ValidationCAs. The process validates the certificate chain against those CAs through the ValidationCAs, AllowExpired and ChainLevelValidation parameters. In addition, the service checks the revocation status in three steps: first it looks up the cert in Lamassu's local database; if not found, it queries the OCSP responders listed in the AIA extension; if OCSP fails, it tries the CRL Distribution Points (CDP). A revoked certificate interrupts the enrollment with certificate is revoked.

    Its main parameters are ValidationCAs, the list of CAs that can validate the bootstrap certificate or the active certificate; AllowExpired, which allows accepting expired client certificates; and ChainLevelValidation, which limits the chain depth when its value is greater than zero.

  • EXTERNAL_WEBHOOK: Lamassu delegates the authorization decision to an external HTTP service. The EST server builds a POST request to the configured webhook with a JSON body reproducing the enrollment context.

    {
      "csr": "<base64 of the CSR PEM, e.g. 'LS0tLS1CRUdJTi...'>",
      "aps": "<DMS_ID>",
      "device_cn": "<CommonName of the CSR>",
      "http_request": {
        "headers": { "Header-Name": "value" },
        "url": "/.well-known/est/<DMS_ID>/simpleenroll"
      }
    }

    The value of the csr field is the base64 encoding of the CSR's PEM (not the DER, and not the plain PEM). The webhook must respond with 2xx and a JSON body {"authorized": true} to continue; any other response aborts the enrollment with external webhook denied enrollment. A 204 No Content is not a valid response even though it is a 2xx code: Lamassu requires a JSON body with the authorized field, so a webhook responding without a body denies the operation just like an error.

  • CLIENT_CERTIFICATE_AND_EXTERNAL_WEBHOOK: combines the two previous modes during /simpleenroll. The client must present a valid mTLS certificate and the webhook must return authorized: true. In /simplereenroll the combination is not preserved: the joint mTLS + webhook check is not maintained, so the integrator must not assume the same guarantees of the initial enrollment apply in reenroll.

  • NO_AUTH: the DMS accepts any CSR without validating the client's identity. Lamassu logs a warning (DMS is configured with NoAuth, allowing enrollment) and proceeds with the registration. It must be restricted to controlled environments (an isolated provisioning network); any network exposed to the internet with a DMS in NO_AUTH is open to certificate issuance by unauthorized third parties.

Endpoint reference

The following sections describe the endpoints in the usual order of use within the device lifecycle:

  • /cacerts: download of the trust chain.
  • /simpleenroll: initial certificate enrollment.
  • /simplereenroll: renewal of an already-issued certificate.
  • /serverkeygen: enrollment with server-side key generation.

/cacerts

The endpoint GET /.well-known/est/{aps}/cacerts distributes the certificates the device needs to validate the EST server and the rest of the CAs managed by the DMS. Lamassu inserts the certificates in this order: first the downstream certificate of the Lamassu server, which is the server's TLS leaf and not a CA, useful for pinning if IncludeLamassuSystemCA is enabled; then the ManagedCAs list declared in the DMS; and last the enrollment CA, if IncludeEnrollmentCA is enabled.

CADistributionSettings lets you decide what is published:

  • IncludeLamassuSystemCA adds the downstream certificate of the Lamassu server. It is the TLS leaf the client can use to pin the connection, not a CA.
  • IncludeEnrollmentCA adds the DMS's issuing CA, responsible for signing device certificates.
  • ManagedCAs publishes additional CAs as trust anchors.

The default format is degenerate PKCS#7 (certificates only) base64-encoded. The response declares Content-Type: application/pkcs7-mime; smime-type=certs-only and Content-Transfer-Encoding: base64. The base64 is wrapped in lines of 76 characters terminated with CRLF, a width compliant with S/MIME (RFC 5751), not the 64 that some RFC 7030 guides suggest. This wrapping applies only to /cacerts and /simpleenroll; the internal parts of /serverkeygen carry no wrap, as explained below.

Degenerate PKCS#7 is a set of certificates with no defined semantic order. Although Lamassu inserts them in the order stated, the integrator must not assume that order when validating chains: the chain must be built by Subject/Issuer matching (or by AKI/SKI), not by position in the response.

curl -s --cacert lamassu-trust.pem \
  "https://<EST_HOST>/.well-known/est/<DMS_ID>/cacerts" \
  -o cacerts.b64

base64 -d -i cacerts.b64 > cacerts.p7b
openssl pkcs7 -inform DER -in cacerts.p7b -print_certs -out cacerts.pem

The -i flag (ignore garbage) helps tolerate the CRLF Lamassu adds and is necessary on some base64 implementations (BusyBox, BSD); on GNU coreutils it is ignored.

Lamassu additionally adds an extension over the RFC: if the client sends the Accept: application/x-pem-file header, the server responds with the PEM concatenation of the same certificates, without the PKCS#7 wrapper. This makes consumption from scripts easier. The extension also works on /simpleenroll and /simplereenroll.

curl -s --cacert lamassu-trust.pem \
  -H "Accept: application/x-pem-file" \
  "https://<EST_HOST>/.well-known/est/<DMS_ID>/cacerts" \
  -o cacerts.pem

/simpleenroll

The endpoint POST /.well-known/est/{aps}/simpleenroll receives a CSR and returns the issued certificate. The request body must be the PKCS#10 (CSR) in DER, base64-encoded without line breaks. The base64 wraps the DER directly, not a PEM. The Content-Type header must be application/pkcs10. If the Content-Type is different, the server responds 400 with content-type must be application/pkcs10; if the body is not valid base64, it responds 400 with body payload must be base64 encoded.

openssl req -in device.csr -outform DER | base64 -w 0 > device.csr.b64

curl -s --cacert lamassu-trust.pem \
  --cert bootstrap.crt --key bootstrap.key \
  -H "Content-Type: application/pkcs10" \
  --data-binary "@device.csr.b64" \
  "https://<EST_HOST>/.well-known/est/<DMS_ID>/simpleenroll" \
  -o enroll.b64

The response follows the same wrapper as /cacerts: degenerate PKCS#7 in base64, with Content-Type: application/pkcs7-mime; smime-type=certs-only. The PKCS#7 contains only the newly issued leaf certificate; it does not include the issuing CA or intermediates. This complies with RFC 7030 §4.2.3 (where the chain is optional) but implies the integrator must combine this certificate with the chain obtained from /cacerts before presenting it to any relying party that validates the full chain (for example, an MQTT broker with strict mTLS).

base64 -d -i enroll.b64 > enroll.p7b
openssl pkcs7 -inform DER -in enroll.p7b -print_certs -out device.crt
openssl verify -CAfile cacerts.pem device.crt

Several DMS fields govern the behavior of /simpleenroll. The integrator does not configure them, but it helps to know them to interpret rejections:

  • EnrollmentSettings.RegistrationMode decides whether the device is created during the first enrollment (JITP) or must exist beforehand (PRE_REGISTRATION).
  • EnrollmentSettings.EnableReplaceableEnrollment allows repeating /simpleenroll for an already-enrolled device. When doing so, it revokes the previous certificate as superseded; if disabled, the server responds with forbiddenNewEnrollment.
  • EnrollmentSettings.VerifyCSRSignature validates that whoever submits the CSR controls the declared private key. For IoT fleets on open networks it must be enabled.

In JITP mode, the first simpleenroll with a CommonName that does not yet exist in Lamassu creates the device automatically. In PRE_REGISTRATION mode, the device must exist beforehand; an unknown CommonName is rejected with device not preregistered. See Enrollment configuration for more details on both modes and the behavior of the issuance profile.

/simplereenroll

The endpoint POST /.well-known/est/{aps}/simplereenroll renews the certificate of an already-enrolled device. The body format is identical to /simpleenroll (DER base64 without line breaks, Content-Type: application/pkcs10). The difference lies in authentication: the client must present the device's active certificate (not the bootstrap) as the mTLS credential.

EXTERNAL_WEBHOOK mode: degradation to NO_AUTH in reenroll

If the DMS is configured with AuthMode: EXTERNAL_WEBHOOK, the webhook is not invoked during /simplereenroll. The renewal flow silently degrades to NO_AUTH: any client that knows a valid CommonName can request reenroll for that device without authentication (allowing reenroll: using NO AUTH mode in the log). A DMS with EXTERNAL_WEBHOOK must not be used in production for reenroll flows unless access to the endpoint is network-controlled.

openssl req -in device-renewal.csr -outform DER | base64 -w 0 > device-renewal.csr.b64

curl -s --cacert lamassu-trust.pem \
  --cert device.crt --key device.key \
  -H "Content-Type: application/pkcs10" \
  --data-binary "@device-renewal.csr.b64" \
  "https://<EST_HOST>/.well-known/est/<DMS_ID>/simplereenroll" \
  -o reenroll.b64

The behavior of reenroll is governed by several ReEnrollmentSettings fields:

  • ReEnrollmentDelta opens the renewal window at NotAfter - ReEnrollmentDelta. Before that instant, the server responds with invalid reenroll window.

  • EnableExpiredRenewal allows using an already-expired certificate as proof of identity; if disabled, the server responds with expired certificate.

  • RevokeOnReEnrollment revokes the previous certificate as superseded after issuing the new one.

  • AdditionalValidationCAs adds valid issuers for the current certificate, in addition to the DMS's enrollment CA.

  • Subject immutability: the renewal CSR must have exactly the same subject as the active certificate. Lamassu compares, byte by byte, the RawSubject of the CSR with the certificate's (slices.Compare). If the bytes differ, the server falls back to a semantic comparison over the CommonName, OrganizationalUnit, Organization, Locality, Province and Country attributes. If all those attributes match, the reenroll continues despite the byte difference (rescuing cases where only the encoding changes, PrintableString versus UTF8String). If any attribute differs, the reenroll aborts with invalid RawSubject bytes. The integrator must regenerate the CSR with exactly the same subject as in the initial enrollment to avoid depending on fallback behavior.

  • Validation of the presented certificate's chain: validation is first attempted against the DMS's enrollment CA; if it fails, it walks the AdditionalValidationCAs. Validation is "shallow": each CA is tested as an anchor, no multi-level chain is composed. The client must present the complete chain in the mTLS handshake. A revoked certificate aborts the reenroll with certificate is revoked.

/serverkeygen

The endpoint POST /.well-known/est/{aps}/serverkeygen lets Lamassu generate the device's key pair on the server and return, in the same response, the private key and the issued certificate. It is useful for devices with limited cryptographic capability or flows where the key is provisioned during manufacturing.

This endpoint requires ServerKeyGen.Enabled to be true in the DMS. If it is not enabled, the server responds with server key generation not enabled. Authentication applies the same way as in /simpleenroll: the integrator presents the credential matching the DMS's AuthMode.

The algorithm and size of the generated key are decided by the DMS, not by the client.

  • RSA supports any size accepted by crypto/rsa; common values are 2048, 3072 and 4096 bits. An invalid value fails generation with a 500 error.
  • ECDSA supports 224, 256, 384 and 521 bits, equivalent to P-224, P-256 (prime256v1), P-384 and P-521. Any other value silently degrades to P-256 and only leaves a warning in the log: the client receives no error and may obtain a curve different from the expected one.

The CSR sent by the client serves only to carry the subject and extensions; the PKCS#10 proof of possession does not apply here because the client is not the one who generated the key. Lamassu discards the original CSR's public key, generates a new key pair with its internal entropy, in a software engine and not in a hardware Crypto Engine such as AWS KMS or PKCS#11, builds a new CSR with that key and proceeds with an internal enrollment equivalent to /simpleenroll.

The response is a multipart/mixed with the literal boundary estServerLamassuBoundary. It contains two parts:

  1. The private key in unencrypted PKCS#8 format, base64-encoded inside a part with Content-Type: application/pkcs8.
  2. The issued certificate in degenerate PKCS#7 base64, inside a part with Content-Type: application/pkcs7-mime; smime-type=certs-only.

Unlike /cacerts and /simpleenroll, where the base64 is wrapped in 76-character lines, each part of /serverkeygen travels as a single base64 line without breaks. Any reverse proxy that rewrites the body (for example, normalizing line endings) can break the parsing.

Critical security warning: private key in transit

This endpoint reverses the PKI principle whereby the subscriber controls its own private key. The key is serialized as unencrypted PKCS#8 (PrivateKeyInfo, without EncryptedPrivateKeyInfo). The CMS EnvelopedData wrapper contemplated in RFC 7030 §4.4.2 is not applied; no AES key wrap or password-based symmetric encryption is used. The only protection is the channel's TLS. The integrator must guarantee that: (a) no reverse proxy terminates TLS and logs the response body; (b) the key is encrypted at rest immediately after receipt; (c) the audit account on the Lamassu server remains the single point of origin of the key.

curl -s --cacert lamassu-trust.pem \
  --cert bootstrap.crt --key bootstrap.key \
  -H "Content-Type: application/pkcs10" \
  --data-binary "@device.csr.b64" \
  "https://<EST_HOST>/.well-known/est/<DMS_ID>/serverkeygen" \
  -o response.mime

To separate the two parts, the following snippet uses awk with the literal boundary. It works with Lamassu's exact structure (each part = headers + blank line + a single base64 line); any intermediate proxy rewriting the body will break it.

# Extract the first part (PKCS#8 key) - plain base64 in one line
awk '/--estServerLamassuBoundary/{i++; next} i==1 && NF{print}' response.mime \
  | sed -n '/^$/,$p' | tail -n +2 | base64 -d -i > device.key.der

# Extract the second part (PKCS#7 certificate) - plain base64 in one line
awk '/--estServerLamassuBoundary/{i++; next} i==2 && NF{print}' response.mime \
  | sed -n '/^$/,$p' | tail -n +2 | base64 -d -i > device.p7b

# Convert the key to PEM (unencrypted PKCS#8)
openssl pkcs8 -inform DER -in device.key.der -nocrypt -out device.key

# Convert the certificate to PEM
openssl pkcs7 -inform DER -in device.p7b -print_certs -out device.crt

Full flows

Once the endpoints have been reviewed individually, these examples show how to chain them in two common scenarios: the initial enrollment of a device and the renewal of an already-issued certificate.

End-to-end example: enroll

The following script illustrates the complete process of enrolling a device with a locally generated EC P-256 key and mTLS authentication against a bootstrap certificate.

#!/usr/bin/env bash
set -euo pipefail

EST_HOST="https://est.lamassu.example"
DMS_ID="iot-fleet-01"
TRUST="lamassu-trust.pem"
BOOTSTRAP_CRT="bootstrap.crt"
BOOTSTRAP_KEY="bootstrap.key"

DEVICE_CN="device-00001"
DEVICE_KEY="device.key"
DEVICE_CSR="device.csr"
DEVICE_CRT="device.crt"
CACERTS_PEM="cacerts.pem"

# 1. Generate the device's private key (EC P-256)
openssl ecparam -name prime256v1 -genkey -noout -out "$DEVICE_KEY"

# 2. Build the CSR with the final subject (signed with SHA-256)
openssl req -new -key "$DEVICE_KEY" -sha256 -out "$DEVICE_CSR" \
  -subj "/CN=$DEVICE_CN/O=Acme/OU=Fleet"

# 3. Download the DMS trust chain
curl -s --cacert "$TRUST" \
  -H "Accept: application/x-pem-file" \
  "$EST_HOST/.well-known/est/$DMS_ID/cacerts" \
  -o "$CACERTS_PEM"

# 4. Convert the CSR to base64 DER without line breaks
openssl req -in "$DEVICE_CSR" -outform DER | base64 -w 0 > device.csr.b64

# 5. Request the certificate from /simpleenroll using mTLS
curl -s --cacert "$TRUST" \
  --cert "$BOOTSTRAP_CRT" --key "$BOOTSTRAP_KEY" \
  -H "Content-Type: application/pkcs10" \
  --data-binary "@device.csr.b64" \
  "$EST_HOST/.well-known/est/$DMS_ID/simpleenroll" \
  -o enroll.b64

# 6. Unwrap the base64 PKCS#7 and persist the certificate
base64 -d -i enroll.b64 \
  | openssl pkcs7 -inform DER -print_certs -out "$DEVICE_CRT"

# 7. Validate the certificate against the downloaded chain
openssl verify -CAfile "$CACERTS_PEM" "$DEVICE_CRT"

End-to-end example: reenroll

The following script reproduces a renewal process: it detects the expiration of the current certificate, regenerates the CSR with the same subject and authenticates with the active certificate.

#!/usr/bin/env bash
set -euo pipefail

EST_HOST="https://est.lamassu.example"
DMS_ID="iot-fleet-01"
TRUST="lamassu-trust.pem"

DEVICE_KEY="device.key"
DEVICE_CRT="device.crt"
DEVICE_CRT_NEW="device.new.crt"

# 1. Inspect the current subject and expiration date
CURRENT_SUBJECT=$(openssl x509 -in "$DEVICE_CRT" -noout -subject -nameopt RFC2253 \
  | sed 's/^subject=//')
NOT_AFTER=$(openssl x509 -in "$DEVICE_CRT" -noout -enddate | cut -d= -f2)
echo "Current subject: $CURRENT_SUBJECT"
echo "Expires: $NOT_AFTER"

# 2. Build the new CSR with the SAME subject as the active certificate.
#    Any difference in attributes causes "invalid RawSubject bytes".
openssl req -new -key "$DEVICE_KEY" -sha256 -out device-renewal.csr \
  -subj "/CN=device-00001/O=Acme/OU=Fleet"

# 3. Convert to base64 DER without line breaks
openssl req -in device-renewal.csr -outform DER | base64 -w 0 > device-renewal.csr.b64

# 4. Invoke /simplereenroll with the active certificate as the mTLS credential
curl -s --cacert "$TRUST" \
  --cert "$DEVICE_CRT" --key "$DEVICE_KEY" \
  -H "Content-Type: application/pkcs10" \
  --data-binary "@device-renewal.csr.b64" \
  "$EST_HOST/.well-known/est/$DMS_ID/simplereenroll" \
  -o reenroll.b64

# 5. Unwrap the new certificate and persist it
base64 -d -i reenroll.b64 \
  | openssl pkcs7 -inform DER -print_certs -out "$DEVICE_CRT_NEW"

echo "New certificate saved to $DEVICE_CRT_NEW"

Next steps after enrollment

Once the device has obtained its identity certificate, the usual steps are:

  • Install the trust chain (cacerts.pem) into the device's trust store. The MQTT broker, the HTTPS server or any relying party validating mTLS will need this chain.
  • Combine the leaf certificate with the chain before presenting it in mTLS connections. The /simpleenroll response contains only the leaf certificate; the relying party may reject the connection if it does not receive the full chain.
  • Plan renewal: configure a timer on the device that invokes /simplereenroll within the ReEnrollmentDelta window before the certificate expires. Do not wait for expiry unless EnableExpiredRenewal is enabled.

Diagnostics and compatibility

Error codes and diagnostics

HTTP 500 for business errors

Lamassu maps almost every business error to 500 Internal Server Error, not to 4xx. The HTTP status code is not enough to distinguish a network error from a policy error. The integrator must inspect the err field of the JSON body ({"err": "<message>"}) to determine the exact cause of the failure.

HTTPMessageCauseIntegrator action
400Field validation for 'APS' failedThe {aps} segment is missing from the URL.Include the DMS ID in the path.
400content-type must be application/pkcs10Content-Type header missing or incorrect.Set Content-Type: application/pkcs10.
400body payload must be base64 encodedThe body is not valid base64.Use base64 -w 0 to avoid line breaks.
500DMS not foundThe {aps} value does not match any DMS.Verify the identifier with the PKI administrator.
500invalid certificateThe client certificate is not valid against ValidationCAs.Check the bootstrap certificate chain.
500certificate is revokedThe certificate presented as the mTLS credential is revoked in Lamassu or via external OCSP/CRL.Request a new bootstrap certificate or investigate the revocation.
500revoked certificateIn reenroll, the device's active certificate in the database is listed as revoked.Review the identity slot status; if the revocation was intentional, issue a new enroll instead of reenroll.
500expired certificateThe presented certificate has expired and the DMS does not allow AllowExpired/EnableExpiredRenewal.Renew before expiration or request EnableExpiredRenewal to be enabled.
500forbiddenNewEnrollmentThe device is already enrolled and EnableReplaceableEnrollment is false.Use /simplereenroll or ask the administrator to enable the flag.
500device not preregisteredThe DMS is in PRE_REGISTRATION and the device does not exist.Register the device before enrolling it.
500invalid reenroll windowRenewal attempted before NotAfter - ReEnrollmentDelta.Wait for the window to open or request a larger ReEnrollmentDelta.
500invalid RawSubject bytesThe renewal CSR's subject does not match the active certificate's.Regenerate the CSR with the exact subject of the previous cert.
500external webhook denied enrollmentThe webhook returned a non-2xx code or authorized != true.Review the webhook service logs.
500server key generation not enabled/serverkeygen is disabled on the DMS.Ask the administrator to enable ServerKeyGen.Enabled.

For messages not listed in this table, the Lamassu server logs (with the fields func, dms, device-cn, step, auth-method, auth-status) are the first place to look for the cause.

Compatibility notes

Lamassu's implementation follows RFC 7030 in its essentials but introduces several quirks that can affect strict EST clients or third-party libraries:

  • The /csrattrs and /fullcmc endpoints of RFC 7030 are not implemented in Lamassu. A client invoking /.well-known/est/<DMS_ID>/csrattrs receives 404 Not Found instead of the 204 No Content the RFC suggests for "no attributes". Strict EST libraries must be configured not to attempt csrattrs.
  • The base64 of the /cacerts and /simpleenroll responses is wrapped in lines of 76 characters terminated with CRLF (S/MIME compliant, RFC 5751). The parts of /serverkeygen apply no wrap.
  • The multipart/mixed boundary in /serverkeygen is fixed and cannot be negotiated.
  • The {aps} segment is mandatory in all EST routes. Routes registered without {aps} are in the code but respond 400.
  • The body of /cacerts, /simpleenroll and /simplereenroll can be returned as concatenated PEM if the client sends Accept: application/x-pem-file. This is a Lamassu-specific extension and does not appear in the RFC.
  • The server does not explicitly reject CSRs signed with SHA-1 or MD5; the integrator must generate the CSR with SHA-256 or higher.

On this page