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:
- DMS configuration, for the PKI administrator.
- Device integration, for anyone consuming the EST endpoints from firmware, scripts or provisioning services.
If you only need a quick orientation, this is the recommended route:
- PKI administrator: start with DMS configuration and finish with Diagnostics and compatibility.
- Device integrator: start with Before you begin and continue with
/cacerts,/simpleenroll,/simplereenrollor/serverkeygen, depending on the flow you will implement.
DMS configuration
Prerequisites
Before configuring a DMS for EST, the following resources must exist on the platform:
- An available CA: the CA acting as
EnrollmentCAmust be created and active in Lamassu. If the authentication mode isCLIENT_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
ServerKeyGenwill 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. InPRE_REGISTRATIONmode, the device must already exist on the platform; if it does not, the request is rejected withdevice not preregistered. JITP mode suits large fleets where the inventory is built during deployment;PRE_REGISTRATIONfits 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 reasonsupersededand the identity slot is updated. When inactive, a secondsimpleenrollattempt is rejected withforbiddenNewEnrollment. 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
-1validates 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) orPUT; 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,OIDCormTLS.
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:
HonorSubjectcontrols 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.HonorKeyUsagedecides whether the CSR'sKeyUsageis respected or replaced by the one defined in the template.HonorExtendedKeyUsagesapplies the same rule toExtKeyUsage.HonorExtensionscontrols 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
supersededonce 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
/cacertsendpoint from acurlclient with server trust. A200 OKresponse 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 100If 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_CERTIFICATEorCLIENT_CERTIFICATE_AND_EXTERNAL_WEBHOOK. This identity must come from one of the configuredValidationCAs, 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 byAuthModeto obtain the device certificate. - Renew with
/simplereenroll: authenticate with the device's current certificate to obtain a new one. - Use
/serverkeygenonly if you need server-side key generation: it applies the same authentication as/simpleenrolland 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) | Description | Expected credential | Applies in enroll | Applies in reenroll |
|---|---|---|---|---|
CLIENT_CERTIFICATE | mTLS with X.509 certificate | Client certificate and private key | Yes | Yes (with the active certificate) |
CLIENT_CERTIFICATE_AND_EXTERNAL_WEBHOOK | mTLS plus webhook validation | Client certificate and private key, plus webhook approval | Yes | Not as a combination; in reenroll both controls are not kept |
EXTERNAL_WEBHOOK | External authorization webhook | None on the TLS channel | Yes | No, degrades to NO_AUTH |
NO_AUTH | No authentication | None | Yes (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 inAuthOptionsMTLS.ValidationCAs. The process validates the certificate chain against those CAs through theValidationCAs,AllowExpiredandChainLevelValidationparameters. 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 withcertificate 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; andChainLevelValidation, 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 aPOSTrequest 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
csrfield is the base64 encoding of the CSR's PEM (not the DER, and not the plain PEM). The webhook must respond with2xxand a JSON body{"authorized": true}to continue; any other response aborts the enrollment withexternal webhook denied enrollment. A204 No Contentis not a valid response even though it is a2xxcode: Lamassu requires a JSON body with theauthorizedfield, 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 returnauthorized: true. In/simplereenrollthe 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 inNO_AUTHis 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:
IncludeLamassuSystemCAadds the downstream certificate of the Lamassu server. It is the TLS leaf the client can use to pin the connection, not a CA.IncludeEnrollmentCAadds the DMS's issuing CA, responsible for signing device certificates.ManagedCAspublishes 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.pemThe -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.b64The 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.crtSeveral DMS fields govern the behavior of /simpleenroll. The integrator does not configure them, but it helps to know them to interpret rejections:
EnrollmentSettings.RegistrationModedecides whether the device is created during the first enrollment (JITP) or must exist beforehand (PRE_REGISTRATION).EnrollmentSettings.EnableReplaceableEnrollmentallows repeating/simpleenrollfor an already-enrolled device. When doing so, it revokes the previous certificate assuperseded; if disabled, the server responds withforbiddenNewEnrollment.EnrollmentSettings.VerifyCSRSignaturevalidates 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.b64The behavior of reenroll is governed by several ReEnrollmentSettings fields:
-
ReEnrollmentDeltaopens the renewal window atNotAfter - ReEnrollmentDelta. Before that instant, the server responds withinvalid reenroll window. -
EnableExpiredRenewalallows using an already-expired certificate as proof of identity; if disabled, the server responds withexpired certificate. -
RevokeOnReEnrollmentrevokes the previous certificate assupersededafter issuing the new one. -
AdditionalValidationCAsadds 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
RawSubjectof the CSR with the certificate's (slices.Compare). If the bytes differ, the server falls back to a semantic comparison over theCommonName,OrganizationalUnit,Organization,Locality,ProvinceandCountryattributes. If all those attributes match, the reenroll continues despite the byte difference (rescuing cases where only the encoding changes,PrintableStringversusUTF8String). If any attribute differs, the reenroll aborts withinvalid 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 withcertificate 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 a500error. - 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:
- The private key in unencrypted PKCS#8 format, base64-encoded inside a part with
Content-Type: application/pkcs8. - 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.mimeTo 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.crtFull 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
/simpleenrollresponse 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
/simplereenrollwithin theReEnrollmentDeltawindow before the certificate expires. Do not wait for expiry unlessEnableExpiredRenewalis 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.
| HTTP | Message | Cause | Integrator action |
|---|---|---|---|
| 400 | Field validation for 'APS' failed | The {aps} segment is missing from the URL. | Include the DMS ID in the path. |
| 400 | content-type must be application/pkcs10 | Content-Type header missing or incorrect. | Set Content-Type: application/pkcs10. |
| 400 | body payload must be base64 encoded | The body is not valid base64. | Use base64 -w 0 to avoid line breaks. |
| 500 | DMS not found | The {aps} value does not match any DMS. | Verify the identifier with the PKI administrator. |
| 500 | invalid certificate | The client certificate is not valid against ValidationCAs. | Check the bootstrap certificate chain. |
| 500 | certificate is revoked | The certificate presented as the mTLS credential is revoked in Lamassu or via external OCSP/CRL. | Request a new bootstrap certificate or investigate the revocation. |
| 500 | revoked certificate | In 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. |
| 500 | expired certificate | The presented certificate has expired and the DMS does not allow AllowExpired/EnableExpiredRenewal. | Renew before expiration or request EnableExpiredRenewal to be enabled. |
| 500 | forbiddenNewEnrollment | The device is already enrolled and EnableReplaceableEnrollment is false. | Use /simplereenroll or ask the administrator to enable the flag. |
| 500 | device not preregistered | The DMS is in PRE_REGISTRATION and the device does not exist. | Register the device before enrolling it. |
| 500 | invalid reenroll window | Renewal attempted before NotAfter - ReEnrollmentDelta. | Wait for the window to open or request a larger ReEnrollmentDelta. |
| 500 | invalid RawSubject bytes | The renewal CSR's subject does not match the active certificate's. | Regenerate the CSR with the exact subject of the previous cert. |
| 500 | external webhook denied enrollment | The webhook returned a non-2xx code or authorized != true. | Review the webhook service logs. |
| 500 | server 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
/csrattrsand/fullcmcendpoints of RFC 7030 are not implemented in Lamassu. A client invoking/.well-known/est/<DMS_ID>/csrattrsreceives404 Not Foundinstead of the204 No Contentthe RFC suggests for "no attributes". Strict EST libraries must be configured not to attemptcsrattrs. - The base64 of the
/cacertsand/simpleenrollresponses is wrapped in lines of 76 characters terminated with CRLF (S/MIME compliant, RFC 5751). The parts of/serverkeygenapply no wrap. - The
multipart/mixedboundary in/serverkeygenis fixed and cannot be negotiated. - The
{aps}segment is mandatory in all EST routes. Routes registered without{aps}are in the code but respond400. - The body of
/cacerts,/simpleenrolland/simplereenrollcan be returned as concatenated PEM if the client sendsAccept: 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.