EST and SCEP are the two protocols most devices use to get an X.509 certificate from a private certificate authority. SCEP (RFC 8894) wraps a PKCS #10 request in CMS envelopes over plain HTTP and authenticates it with a shared challenge password. EST (RFC 7030) sends the same kind of request over TLS and can use the device's existing certificate, a password or both. To secure either one, make every enrollment credential single use, restrict what each profile can issue, keep lifetimes short and keep an inventory of everything issued.
ACME gets most of the attention because public web certificates run on it, and our ACME protocol explainer covers it in depth. But a managed laptop that needs a Wi-Fi certificate, a router that needs an IPsec identity or a sensor on a factory floor has no public DNS name and no web server to answer a challenge. Those devices enroll with SCEP or EST, and increasingly with CMP. This post explains how each protocol works on the wire, what changed in the RFCs between 2013 and 2026, where the weak points are, and how to run a safe lab deployment with open source software.
Key takeaways
- SCEP spent over twenty years as a draft before RFC 8894 (September 2020). It makes AES128-CBC and SHA-256 mandatory and forbids single DES and MD5, because the unauthenticated GetCACaps response allows downgrade.
- EST (RFC 7030, October 2013) runs over HTTPS and can bind each request to its TLS session.
- RFC 8951 (2020) and RFC 9908 (January 2026) clarified EST, the latter adding a server-supplied CSR template, and RFC 9148 (2022) carries EST over CoAP and DTLS.
- CMP is the third option. RFC 9810 (July 2025) obsoletes RFC 4210 and RFC 9480, adds support for KEM public keys, and RFC 9483 profiles it for industrial and IoT use.
- ACME is moving toward devices through the device attestation draft, now at revision 10, and Apple already documents an ACME payload that uses its
device-attest-01challenge. - Most real failures are operational: one challenge for the whole fleet, multi-year certificates, profiles that sign any requested name, and no inventory.
Why devices need something other than ACME
ACME, defined in RFC 8555 in March 2019, proves control of an identifier. For public web certificates that identifier is a DNS name, and the proof is an HTTP file, a DNS record or a TLS ALPN response that the CA can check from the internet. The challenge types post walks through them.
Device certificates break most of those assumptions:
- No public name. A laptop's Wi-Fi certificate identifies a device or a user, not a host name. Its subject might be a serial number, a directory identifier or an email address.
- No listening service. A phone or a battery-powered sensor does not run a web server the CA can reach, and it often sits behind NAT.
- The question is different. The CA does not need to know whether you control a name. It needs to know whether this request comes from a device the organisation owns or manages, and which identity that device should get. That is an authorisation decision, usually made by a management system or a registration authority (RA), not a domain validation.
- Constrained hardware. Some devices have tens of kilobytes of RAM, talk over lossy radio links and use CoAP rather than HTTP. They need small messages and DTLS.
- Installed base. Network gear, VPN clients and mobile device management (MDM) platforms have shipped SCEP clients for two decades. Apple documents a SCEP payload for iOS, macOS, tvOS, visionOS and watchOS, and Microsoft Intune supports SCEP certificate profiles alongside PKCS and imported PKCS certificates.
So device enrollment turns on one question: how does the RA know this request is legitimate? SCEP answers with a shared secret, EST with TLS client authentication or a password inside TLS, CMP with signatures or a MAC, and the ACME device attestation draft with a hardware attestation statement.
A short history and timeline
| Date | Document | What it did |
|---|---|---|
| September 2005 | RFC 4210 | Certificate Management Protocol (CMP) version 2, obsoleting RFC 2510 |
| October 2013 | RFC 7030 | Enrollment over Secure Transport (EST), a profile of CMC over HTTPS |
| 2015 | SCEP draft reboot | Per RFC 8894's background notes, the long-stalled SCEP draft was restarted and cleaned up |
| January 2018 | RFC 8295 | EST extensions: extra path components for firmware, trust anchors and keys, plus a Package Availability List |
| March 2019 | RFC 8555 | ACME |
| June 2020 | NIST SP 1800-16 | NCCoE practice guide on TLS server certificate management |
| September 2020 | RFC 8894 | SCEP published as an Informational RFC |
| November 2020 | RFC 8951 | EST clarification: deprecates Content-Transfer-Encoding, fixes ASN.1 |
| April 2022 | RFC 9148 | EST-coaps: EST over CoAP and DTLS |
| July 2022 | RFC 9266 | tls-exporter channel binding for TLS 1.3 |
| November 2023 | RFC 9480, 9482, 9483 | CMP updates, CMP over CoAP, Lightweight CMP Profile |
| July 2025 | RFC 9810, 9811 | CMP (version 3 syntax) obsoletes RFC 4210 and RFC 9480; new HTTP transfer document |
| January 2026 | RFC 9908 | EST CSR attributes clarification and CSR template |
| August 2026 | ACME device attestation draft, revision 10 | device-attest-01 challenge and hardware identifiers for ACME |
SCEP's history is unusual. RFC 8894's own background notes say the specification "spent over twenty years in the draft stage." It began as a network equipment vendor's protocol for provisioning IPsec routers, drifted into general device and IoT use, and accumulated conflicting appendices. Its original sponsors more or less abandoned it, and it was rebooted in 2015 because so much of the industry depended on it. The authors note that before the update, the lowest common denominator for interoperability was single DES and MD5.
How SCEP works
RFC 8894 lists five general operations: CA public key distribution, certificate enrolment and issue, certificate renewal, certificate query and CRL query. It builds them out of two older standards, CMS (formerly PKCS #7) for signing and encrypting, and PKCS #10 for the certificate request itself.
The transport
SCEP runs over HTTP, not HTTPS. Every message goes to one path with an operation query parameter. The RFC says clients should use the fixed path /cgi-bin/pkiclient.exe unless told otherwise, a relic of the protocol's early server software:
GET /cgi-bin/pkiclient.exe?operation=GetCACaps HTTP/1.1
GET /cgi-bin/pkiclient.exe?operation=GetCACert HTTP/1.1
POST /cgi-bin/pkiclient.exe?operation=PKIOperation HTTP/1.1
Early drafts sent everything with GET, base64 encoded into the URL. RFC 8894 makes POST mandatory to implement and keeps GET only "for historical reasons." Confidentiality and integrity come from the CMS layer, not from TLS.
Step 1: GetCACaps and GetCACert
The client first asks the server what it supports. GetCACaps returns a plain text list of keywords such as AES, SHA-256, POSTPKIOperation, Renewal, GetNextCACert and SCEPStandard. The SCEPStandard keyword is new in RFC 8894 and implies AES, POSTPKIOperation and SHA-256.
Then the client fetches the CA certificate (or the CA and RA certificates) with GetCACert. The response is not authenticated by anything the client already trusts, so the RFC requires the client to check it out of band. The usual method is a fingerprint: a SHA-256 hash over the whole CA certificate, configured on the device in advance. Apple's SCEP payload has a CAFingerprint key for exactly this purpose.
Step 2: build the request
The client generates a key pair and a PKCS #10 certificate signing request. If it has no certificate yet, it creates a temporary self-signed certificate with the same subject and key, and uses it to sign the SCEP message. That is a PKCSReq. If it already holds a certificate from this CA and the CA supports renewal, it signs with the existing certificate instead. That is a RenewalReq.
The authorisation secret goes inside the PKCS #10 request as the PKCS #9 challengePassword attribute. The RFC recommends including it, and recommends that it be a one-time value.
Step 3: wrap it twice
The request is placed in a CMS EnvelopedData structure encrypted to the CA (or RA) certificate, so the challenge password and the request are not readable in transit. That envelope is then placed inside a CMS SignedData structure signed with the client's self-signed or existing certificate. The signed attributes carry the SCEP fields:
| Attribute | Purpose |
|---|---|
transactionID | Unique ID for the transaction, used to match polls and responses |
messageType | 19 PKCSReq, 17 RenewalReq, 20 CertPoll, 21 GetCert, 22 GetCRL, 3 CertRep |
pkiStatus | In a response: success, failure or 3 for PENDING |
failInfo and failInfoText | Why a request failed; failInfoText is new in RFC 8894 |
senderNonce and recipientNonce | 16 byte random values to tie a response to its request |
Step 4: CertRep, or PENDING and polling
The CA answers with a CertRep. If it issues immediately, the response carries the certificate in an envelope encrypted to the client. If the request needs manual approval, the status is PENDING and the client repeats a CertPoll message (called GetCertInitial in older drafts) until the CA issues or rejects. Apple's payload exposes Retries and RetryDelay keys to control that polling.
Manual approval is the RFC's fallback when a request has neither a challenge password nor a trusted certificate. Most deployments rely on the challenge password instead.
What RFC 8894 actually standardised
RFC 8894 kept the wire format and consolidated fifteen years of appendices. The changes that matter for security are:
- Mandatory to implement: GetCACaps, GetCACert, PKCSReq with its responses, HTTP POST, AES128-CBC and SHA-256.
- Allowed for compatibility: HTTP GET, triple DES and SHA-1.
- Forbidden: single DES and MD5. The RFC explains that as of 2017 a DES key could be recovered with rainbow tables in under a minute because SCEP messages contain large amounts of predictable plaintext, and that MD5 chosen prefix collisions cost very little compute.
- Two optional additions: the
SCEPStandardcapability and thefailInfoTextattribute. Older implementations simply ignore both.
It is Informational, not Standards Track, reflecting its history as a documented industry protocol.
How EST works
RFC 7030 defines EST as a Standards Track profile of Certificate Management over CMS (CMC) messages over a secure transport, in practice HTTPS. Where SCEP builds security out of CMS envelopes, EST relies on TLS.
URIs and operations
All EST operations live under a well-known prefix, /.well-known/est, optionally followed by an arbitrary label so one server can host several CAs or profiles:
https://ca.example.internal/.well-known/est/cacerts
https://ca.example.internal/.well-known/est/wifi-devices/simpleenroll
| Operation | Path | Status in RFC 7030 | What it does |
|---|---|---|---|
| Distribution of CA certificates | /cacerts | MUST | Returns the current CA certificates as a certs-only CMS message |
| Enrollment | /simpleenroll | MUST | Client POSTs a PKCS #10 request, server returns the certificate |
| Re-enrollment | /simplereenroll | MUST | Renew or rekey, authenticated with the current certificate |
| Full CMC | /fullcmc | OPTIONAL | Full CMC requests and responses for advanced cases |
| Server-side key generation | /serverkeygen | OPTIONAL | Server generates the private key and returns it with the certificate |
| CSR attributes | /csrattrs | OPTIONAL | Server tells the client what to put in the CSR |
RFC 8295 (January 2018) added paths for firmware, trust anchors and keys, plus a Package Availability List. Check server support before relying on them.
Trust anchors and bootstrapping
RFC 7030 separates two trust anchor databases on the client. The Explicit TA database holds the CA certificates you configured for this PKI. The Implicit TA database is something like the operating system's public web roots. An Implicit TA lets the client confirm the server holds a certificate for the URL, but not that it is authorised to act as an RA for your PKI. The RFC requires that clients can disable the Implicit TA database, and recommends that clients using it rely only on TLS client certificates and channel binding so their passwords are not exposed to the wrong server.
When a device has no trust anchors at all, the RFC allows a bootstrap distribution of CA certificates. The client may complete an unauthenticated TLS handshake only to fetch /cacerts, and must then have a human approve the result using out-of-band data such as a SHA-256 fingerprint. It must not answer HTTP authentication requests on that unauthenticated connection, and must open a new, properly authenticated TLS session before doing anything else.
Client authentication
EST gives the server several ways to decide whether to issue:
- TLS client certificate. The device presents an existing certificate. That could be a manufacturer installed device certificate (often called an IDevID), a certificate from another PKI, or the certificate it is renewing.
- HTTP Basic or Digest authentication. RFC 7030 allows these only inside TLS. Its security considerations warn that Basic hands the server a cleartext password and recommend that clients use such credentials only once, to obtain a certificate they will use from then on.
- Certificate-less TLS. Cipher suites built on a shared secret are allowed, but the RFC requires that they resist offline guessing and that the secret is never shared beyond the client and server.
Deciding what an authenticated client may receive is left to local policy, which is where profiles and subject rules come in.
Linking identity and proof of possession
A signed PKCS #10 request already proves the client holds the private key. EST adds an optional link between that proof and the TLS session. The client takes the tls-unique channel binding value from its TLS stack, base64 encodes it and puts it in the request's challengePassword attribute before signing. The server checks that the value matches its own view of the session.
This stops an attacker relaying a request through a different authenticated session. Clients SHOULD do it, servers MUST verify it when present and MAY refuse requests without it.
One gap is worth knowing. tls-unique is defined for TLS 1.2 and earlier. RFC 9266 (July 2022) says it "is not defined for TLS 1.3" and defines a replacement binding called tls-exporter. If your EST server and clients negotiate TLS 1.3, test whether your implementations support channel binding at all before you make it a policy requirement.
Server-side key generation, carefully
/serverkeygen exists for devices that cannot generate good keys themselves. RFC 7030 is blunt about the cost: the private key travels to the client protected only by TLS unless the optional CMS encryption is used. It recommends that servers not offer the operation by default, that clients not request it without a compelling reason, that the Implicit TA database not be used with it, and that the encrypted CMS response be used. If you enable it, you have created a copy of every device key on the server side, which then needs the same protection as the CA.
RFC 8951 and RFC 9908: making EST interoperable
RFC 7030 told servers to send base64 bodies with a MIME style Content-Transfer-Encoding header, which HTTP does not use. Implementations disagreed, and RFC 8951 (November 2020) resolved it: payloads are base64 (RFC 4648) of the DER encoding, and the header is deprecated. It also fixed ASN.1 errors in the original.
The CSR attributes response was also ambiguous about whether it listed attribute types or values. RFC 9908 (January 2026) clarifies the encoding and adds a template: the server sends a partly filled CSR, including a subject name, for the client to complete. That lets the CA, not the device, decide the identity.
EST over CoAP: RFC 9148
RFC 9148 (April 2022) maps EST onto CoAP over DTLS for constrained devices. Our CoAP explainer covers the transport itself. The EST-specific changes are:
- Shorter paths. URIs are part of every packet, so EST-coaps uses short names under the same well-known prefix.
| EST over HTTPS | EST-coaps |
|---|---|
/cacerts | /crts |
/simpleenroll | /sen |
/simplereenroll | /sren |
/serverkeygen | /skg (PKCS #7) or /skc (single certificate) |
/csrattrs | /att |
- A fixed cipher suite. The mandatory DTLS cipher suite is TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 with curve secp256r1 (NIST P-256), following the IoT TLS profile in RFC 7925. DTLS 1.3 is also covered.
- Client certificates required. EST-coaps client authentication MUST use a client certificate. There is no password option, which suits devices that ship with a manufacturer certificate.
- Block-wise transfer. Certificates and CMS structures are larger than a single small packet, so EST-coaps uses CoAP block-wise transfer (RFC 7959).
- Connection reuse. Unlike the original EST, a
/crtsrequest followed by/sencan share one authenticated DTLS session, saving a handshake on a device that is bootstrapping. - A registrar at the edge. An HTTPS to CoAPS registrar at the network boundary relays to an ordinary EST server. It is effectively an RA and needs the same hardening.
Where CMP fits
CMP predates both EST and the SCEP RFC. RFC 4210 (September 2005) defined CMP version 2 for the full certificate lifecycle: initial registration, certification requests, key update, revocation requests, cross certification, key recovery and confirmation messages. It authenticates messages with signatures or with a MAC based on a shared secret, and it can run over HTTP or other transports.
RFC 9480 (November 2023) updated CMP to use EnvelopedData, clarified PKCS #10 handling and improved crypto agility. In July 2025, RFC 9810 obsoleted both RFC 4210 and RFC 9480. It adds support for certificates containing a Key Encapsulation Mechanism (KEM) public key, which matters for post-quantum algorithms, and uses protocol version cmp2021(3). RFC 9811 replaced the HTTP transfer document. RFC 9482 defined CMP over CoAP, and RFC 9483, the Lightweight CMP Profile narrows CMP to what industrial and IoT scenarios need.
CMP is heavier than EST but has an explicit certificate confirmation message and a richer revocation flow. For an industrial or automotive PKI, read RFC 9483 before defaulting to EST.
ACME for devices: the attestation draft
The IETF ACME working group's device attestation draft (revision 10, last revised 9 August 2026) adapts ACME for devices. The draft adds permanent-identifier and hardware-module identifier types, a device-attest-01 challenge whose response contains a WebAuthn attestation statement, and the use of ACME external account binding to pre-authenticate requests to an enterprise CA. It is still an Internet-Draft, not an RFC.
Apple already documents an ACME certificate payload (com.apple.security.acme) built on this approach. According to Apple's documentation, the device generates a key pair, requests an order using a ClientIdentifier as the permanent identifier, receives a device-attest-01 challenge, and if Attest is true answers with an attestation of the key and device properties. With HardwareBound set, the Secure Enclave generates the key and the system cannot export it; hardware bound keys must be P-256 or P-384. Apple lists attestation support for iPhone and iPad from A11 Bionic, Apple silicon Macs on macOS 14 and later, Apple TV from A12, Apple Watch from S4 and Vision Pro. Apple also notes that the ClientIdentifier alone is "a relatively weak indication" because it can be intercepted.
That is the argument for attestation: a secret proves the device saw it, while an attestation gives the CA evidence that the key lives in specific hardware.
Comparison: SCEP, EST, CMP and ACME
| SCEP (RFC 8894) | EST (RFC 7030 and updates) | CMP (RFC 9810, 9483) | ACME (RFC 8555, device draft) | |
|---|---|---|---|---|
| Status | Informational RFC, 2020 | Standards Track, 2013, clarified 2020 and 2026 | Standards Track, 2005, rewritten 2025 | Standards Track, 2019; device attestation is a draft |
| Transport | HTTP; security from CMS envelopes | HTTPS; CoAP and DTLS via RFC 9148 | HTTP, CoAP, others | HTTPS with JWS signed requests |
| Initial authentication | Challenge password in the CSR, or manual approval | TLS client certificate, HTTP Basic or Digest in TLS, or certificate-less TLS | Signature with an existing key, or MAC with a shared secret | Account key plus a challenge; external account binding; device attestation in the draft |
| Server authentication | CA fingerprint checked out of band | TLS with explicit trust anchors; fingerprint for bootstrap | Signed responses | TLS to the directory URL |
| Proof of possession | Signed CSR on first enrollment; not on renewal | Signed CSR, optionally bound to the TLS session | Signature or other POP methods | CSR signature at finalization |
| Renewal | RenewalReq signed with the current certificate | /simplereenroll with the current certificate | Key update request | New order, often with ACME Renewal Information |
| Server key generation | No | Optional /serverkeygen | Supported | No |
| Issue confirmation | None | None | Yes, certificate confirmation | Order status |
| Typical use | MDM profiles, network gear, legacy VPN clients | Network gear, IoT, newer MDM and industrial | Industrial, telecom, automotive PKI | Web servers; devices through attestation |
The weaknesses, at concept level
None of these protocols is broken when deployed as their RFCs recommend. The problems come from how they are commonly deployed.
Shared and static challenge passwords
In SCEP, the challenge password is the only thing standing between "anyone who can reach the URL" and "a certificate from your CA." RFC 8894 says it should not be a conventional password but a high-entropy value, ideally single use, with a limited lifetime, and stored on the CA as a salted iterated hash or a keyed MAC. It points out a useful side effect: if a single use value arrives twice, that is evidence of a breach.
Many deployments bake one static challenge into an MDM profile, image or router template. Anyone who extracts it can enroll as many devices as they like.
Intune's documentation shows the alternative. When it delivers a SCEP profile, Intune generates an encrypted and signed challenge blob per device that the device cannot read. A policy module on the SCEP server compares the device's CSR to the blob, including the expected subject and subject alternative name, and only issues to requests that match. A direct request to the published SCEP URL gets a 403.
No proof of who the device is
A challenge password proves the requester knows a secret, not which device is asking or that the key is in protected hardware. EST with a manufacturer certificate, CMP with an existing key, or ACME with attestation give stronger evidence.
Weak legacy cryptography and downgrade
SCEP's GetCACaps response is unauthenticated. RFC 8894 explains that if a server still supports single DES and MD5, an on-path attacker can strip the stronger keywords from the response and roll the exchange down. It suggests that a client which expects SCEPStandard and does not see it should treat that as a security issue. Client tools may also fall back on their own: the documentation of sscep, a long-standing open source SCEP client, says its default signature and encryption algorithms are "the best from getcacaps, or md5." Pin algorithms explicitly.
RFC 8894 lists other structural issues: the CA private key is used to sign and possibly decrypt transport messages, not just certificates; there is no issue confirmation; and renewal requests do not prove possession of the new key, because they are signed with the old one.
Unrestricted subject names and extensions
The most damaging misconfiguration is a profile that copies whatever subject and subject alternative names the CSR contains. A device enrolled for Wi-Fi could ask for a name that a VPN gateway or an internal web service trusts, or for a user principal name that maps to an administrator. The fix is to have the RA or CA decide the identity from its own records (or from the RFC 9908 template it hands the device), restrict which name types each profile accepts, and fix the extended key usage. Our post on name constraints for private CAs covers the CA-side limit that backs this up.
Intune's documentation gives a concrete example of why mapping matters: for certificate authentication against Active Directory, the KDC requires strong mapping, with a security identifier extension in the subject alternative name that matches the user or device.
The RA and the server key store
An RA that can approve requests is as sensitive as the CA for the profiles it serves. So is any server that keeps generated private keys. Intune's guidance not to host the SCEP service on the issuing CA server, because that is a risk when the CA serves internet requests, applies to any design: put the internet-facing enrollment front end on a separate host from the CA key.
Step by step: a lab EST and SCEP deployment with open source tools
This walkthrough uses OpenXPKI, an open source CA released under the Apache License whose project page lists SCEP, EST, SimpleCMC and ACME enrollment interfaces, with OpenSSL and curl as the EST client and sscep as the SCEP client. Dogtag PKI, another open source CA, also supports SCEP. Use a disposable lab machine. The OpenXPKI Docker repository states its images are for demonstration and testing and "should not be used in a production environment."
Step 1: start a lab CA
From the OpenXPKI Docker repository's README, clone the community configuration into the compose directory, prepare the CLI key and crypto secrets as the README describes, then start the stack and generate a demo two-tier hierarchy:
git clone https://github.com/openxpki/openxpki-config.git \
--single-branch --branch=community
# follow the README to create config/client.key and set secrets in
# openxpki-config/config.d/system/crypto.yaml before starting
docker compose up -d web
docker compose exec -u pkiadm server /bin/bash /etc/openxpki/contrib/sampleconfig.sh
The web UI comes up at https://localhost:8443/webui/index/. The project's sample Apache configuration exposes SCEP on port 80 and the RPC and EST APIs on port 443 over HTTPS. The README warns that if you do not supply real TLS certificates, dummy ones are generated and TLS client authentication, and therefore EST re-enrollment, will not work. Put a real server certificate from your lab CA in place before Step 5. For a production layout with an offline root, see our private CA explainer.
Step 2: fetch and verify the CA certificates over EST
The first /cacerts call is the bootstrap case from RFC 7030 section 4.1.1, so you must verify the result out of band before trusting it. Replace the host and label with the ones your configuration exposes.
EST=https://localhost:8443/.well-known/est
# Bootstrap only: -k because no trust anchor exists yet
curl -sk "$EST/cacerts" -o cacerts.b64
base64 -d cacerts.b64 > cacerts.p7b
openssl pkcs7 -inform DER -in cacerts.p7b -print_certs -out cacerts.pem
openssl x509 -in cacerts.pem -noout -subject -fingerprint -sha256
Compare the SHA-256 fingerprint with the one shown on the CA itself (from the web UI or openssl x509 -fingerprint -sha256 on the CA host). Only then keep cacerts.pem and drop -k from every later command.
Step 3: create a device key and CSR
Generate the key on the device. P-256 keeps the request small and matches the EST-coaps mandatory curve.
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc \
-keyout device.key -out device.csr \
-subj "/CN=lab-sensor-0042" \
-addext "subjectAltName=DNS:lab-sensor-0042.devices.example.internal"
openssl req -in device.csr -noout -text | grep -A1 "Subject Alternative"
-noenc is the OpenSSL 3 spelling of the older -nodes. On a real device the key would live in a TPM or secure element instead of a file.
Step 4: enroll with /simpleenroll
EST expects the CSR as base64 of the DER encoding (RFC 8951), with content type application/pkcs10. This example uses HTTP Basic inside TLS with a one-time enrollment secret, which is how RFC 7030 suggests passwords be used:
openssl req -in device.csr -outform DER | base64 > device.csr.b64
curl -s --cacert cacerts.pem \
--user "lab-sensor-0042:ONE-TIME-SECRET" \
-H "Content-Type: application/pkcs10" \
--data-binary @device.csr.b64 \
"$EST/simpleenroll" -o device.p7.b64
base64 -d device.p7.b64 > device.p7b
openssl pkcs7 -inform DER -in device.p7b -print_certs -out device.pem
openssl x509 -in device.pem -noout -subject -issuer -enddate -ext subjectAltName,extendedKeyUsage
Check that the subject alternative name is the one the CA should assign, the extended key usage is only client authentication, and the end date matches your short lifetime. If the CA accepted an extra name you added to the CSR, the profile is too permissive. The OpenSSL certificate reading guide explains each field.
Step 5: renew with /simplereenroll
Re-enrollment authenticates with the current certificate over TLS, so no password is needed:
curl -s --cacert cacerts.pem --cert device.pem --key device.key \
-H "Content-Type: application/pkcs10" \
--data-binary @device.csr.b64 \
"$EST/simplereenroll" -o renewed.p7.b64
The same CSR renews the same key. Rekeying with a fresh key on each renewal limits how long a leaked key stays useful.
Step 6: ask the server what it wants
curl -s --cacert cacerts.pem "$EST/csrattrs" | base64 -d | openssl asn1parse -inform DER
An empty response is allowed. A populated one lists attributes or, under RFC 9908, a template with the expected subject.
Step 7: enroll a second device over SCEP
SCEP puts the challenge password inside the CSR, so it needs an OpenSSL config section. Treat the file as a secret and delete it after use.
# scep-req.cnf
[req]
prompt = no
distinguished_name = dn
attributes = req_attributes
[dn]
CN = lab-laptop-0107
[req_attributes]
challengePassword = ONE-TIME-CHALLENGE
SCEP_URL="http://localhost/scep/your-endpoint" # from your server config
sscep getcaps -u "$SCEP_URL"
sscep getca -u "$SCEP_URL" -c ca.crt
openssl x509 -in ca.crt-0 -noout -fingerprint -sha256 # compare out of band
openssl req -new -newkey rsa:3072 -noenc -keyout laptop.key \
-config scep-req.cnf -out laptop.csr
sscep enroll -u "$SCEP_URL" -c ca.crt-0 -k laptop.key -r laptop.csr \
-l laptop.crt -E aes -S sha256
shred -u scep-req.cnf 2>/dev/null || rm -f scep-req.cnf
getca may write several files with -0, -1 suffixes when the server returns CA and RA certificates; pick the right one for -c. The -E aes -S sha256 flags pin the algorithms so the client does not fall back to its defaults if GetCACaps is missing or tampered with. Check the getcaps output for SCEPStandard or at least AES and SHA-256; if the server advertises DES without DES3, or MD5, fix the server.
Step 8: restrict profiles and subject names
CAs separate enrollment endpoints from certificate profiles; in OpenXPKI both live in YAML files in the configuration you cloned. Set these rules per endpoint:
- One profile per purpose. A Wi-Fi endpoint issues only client authentication certificates for the Wi-Fi profile. It cannot issue server certificates or anything with
CA:TRUE. - Server-chosen identity. The subject and subject alternative names come from your inventory or MDM record, or are checked against it, never accepted from the CSR as is.
- Allowed name types and patterns. For example, DNS names only under
devices.example.internal, or a UPN only for the device's own enrolled user. - Fixed key usage and extended key usage, with requested extensions ignored.
- Minimum key sizes and allowed algorithms, matching what your clients and RADIUS or VPN servers support.
- Authentication per endpoint. Single use secrets for initial SCEP enrollment; TLS client certificates for EST renewal; manual approval for anything unusual.
Step 9: short lifetimes and automatic renewal
Shorter lifetimes reduce how long a stolen key or a mistaken issue stays useful, and make revocation less critical. Intune allows a validity period of up to 24 months for SCEP certificates; nothing requires you to use it. For lab devices, try 30 to 90 days and renew at about two thirds of the lifetime. A minimal renewal check for a Linux device:
#!/bin/sh
# renew-if-due.sh: renew when fewer than 20 days remain
if ! openssl x509 -in /etc/device/device.pem -noout -checkend $((20*86400)); then
openssl req -new -key /etc/device/device.key -subj "/CN=$(hostname)" -outform DER \
| base64 > /tmp/renew.csr.b64
curl -sf --cacert /etc/device/cacerts.pem \
--cert /etc/device/device.pem --key /etc/device/device.key \
-H "Content-Type: application/pkcs10" --data-binary @/tmp/renew.csr.b64 \
"$EST/simplereenroll" | base64 -d \
| openssl pkcs7 -inform DER -print_certs -out /etc/device/device.new.pem \
&& mv /etc/device/device.new.pem /etc/device/device.pem
fi
Run it from a systemd timer or cron daily. Make sure the device alerts somewhere when renewal fails; a quiet failure becomes an outage on the expiry date. Our certificate lifecycle post covers renewal monitoring in more depth.
Step 10: keep an inventory
Export the issued certificate list from the CA regularly (OpenXPKI exposes search in the UI and API) and record serial, subject, names, profile, requester, issue and expiry dates. From any PEM file you can extract the same fields:
openssl x509 -in device.pem -noout -serial -subject -enddate -ext subjectAltName
Reconcile the list against your device inventory or MDM. A certificate with no matching device is either a decommissioned device you forgot to revoke or something you did not issue on purpose. NIST's NCCoE practice guide SP 1800-16 treats an inventory of certificates and their owners as the foundation of certificate management, and that advice applies to device certificates as much as to TLS server certificates.
How enrollment fits with 802.1X Wi-Fi and VPN
Most device certificates exist to get onto a network. In 802.1X with EAP-TLS, the device presents its certificate to a RADIUS server through the access point or switch, and the RADIUS server decides whether to let it in. Our 802.1X and EAP explainer and the post on Wi-Fi certificates versus shared passwords cover the network side. The enrollment side needs to line up with it:
- Deploy the trust chain first. Intune requires a trusted certificate profile with the root or issuing CA before SCEP or PKCS profiles. Devices should also validate the RADIUS server's certificate by name and issuer.
- Give network certificates their own issuing CA or profile, and have the RADIUS server trust only that, so certificates issued for other purposes cannot get on the network.
- Make identity mapping deliberate. Decide whether the RADIUS policy keys on the subject, a SAN value, a device ID extension or a directory lookup, and make the enrollment profile write exactly that field.
- Match key usage. Issue client authentication EKU for device and user certificates, and check that the RADIUS or VPN server enforces it.
- For VPN, the same rules apply: trust only the VPN issuing CA and map identities explicitly.
How to revoke at scale
Revocation is where device PKIs most often fail quietly. Some practical rules:
- Tie revocation to lifecycle events. When a device is retired, wiped, lost or removed from MDM, revoke its certificates by serial number from the inventory. Make that a step in your offboarding runbook.
- Make sure relying parties check. A RADIUS server or VPN gateway that never fetches the CRL or queries OCSP ignores revocation entirely. Configure and test it. Our post on why a revocation is not effective until the cache agrees explains cache timing.
- Plan for mass revocation. If a challenge password or an RA credential leaks, you need to find every certificate issued through that endpoint during the exposure window and revoke them. That requires an inventory that records the endpoint and authentication method for each issue.
- Lean on short lifetimes for devices you cannot reach. If a device cannot check revocation status, a 30 day certificate limits the damage more than any CRL.
- Rotate the CA without breaking the fleet. SCEP's GetNextCACert and EST's
/cacertsboth let you distribute the next CA certificate before the rollover. Push it well ahead of time.
Common mistakes
- One static challenge for the whole fleet. It ends up in profiles, scripts and backups, and anyone who finds it can enroll. Use single use, short-lived values, or a dynamic challenge that binds to the expected subject, as Intune's challenge blob does.
- Trusting the CSR's names. If the device chooses its own subject and SANs, a compromised or cloned device can ask for someone else's identity.
- Long-lived certificates. Multi-year device certificates make every leaked key and every mistaken issue last for years, and make revocation the only defence.
- No inventory of issued certificates. You cannot find what a leaked secret issued, spot orphans or predict expiries.
- Allowing legacy SCEP algorithms. Leaving single DES or MD5 enabled on the server invites downgrade. Remove them and pin AES and SHA-256 on clients.
- Skipping CA fingerprint checks. Without them, an on-path attacker can substitute their own CA during GetCACert or EST bootstrap.
- Running the enrollment front end on the CA host. Put the internet-facing SCEP or EST service on a separate machine from the CA key.
- Enabling server-side key generation by default. RFC 7030 recommends against it. If you need it, encrypt the key response and protect the server like the CA.
- One issuing CA for everything. A RADIUS server that trusts a general internal CA will accept certificates that were never meant for network access.
- Silent renewal failures. If renewal fails and nobody is told, the device drops off Wi-Fi or VPN on the expiry date.
FAQ
What is the difference between EST and SCEP?
SCEP sends CMS wrapped PKCS #10 requests over plain HTTP, authenticated by a challenge password. EST sends them over HTTPS, authenticated by a TLS client certificate or a password inside TLS, and can bind the request to the session. EST is Standards Track; SCEP is Informational.
Is SCEP still secure in 2026?
It can be, if run as RFC 8894 recommends: AES and SHA-256 only, single use challenges, CA fingerprint checks, strict profiles and short lifetimes. Its lack of issue confirmation and renewal proof of possession remain.
What does RFC 8894 change compared to older SCEP drafts?
The wire protocol is the same. RFC 8894 consolidates the appendices, makes AES128-CBC, SHA-256 and HTTP POST mandatory to implement, forbids single DES and MD5, and adds the optional SCEPStandard capability and failInfoText attribute.
Does EST work with TLS 1.3?
EST runs over any TLS version the client and server support; RFC 7030 required TLS 1.1 or later. The optional channel binding uses tls-unique, which RFC 9266 says is not defined for TLS 1.3 and replaces with tls-exporter. Check whether your EST implementations support binding under TLS 1.3.
What are /simpleenroll and /simplereenroll?
EST's enrollment and renewal operations. A device POSTs a base64 DER PKCS #10 request to the first, then to the second, authenticated by its current certificate, to renew.
What is EST over CoAP?
RFC 9148 defines EST-coaps, which runs EST over CoAP and DTLS for constrained devices. It uses short paths such as /crts and /sen, a mandatory ECDSA and AES-CCM cipher suite, block-wise transfer, and client certificate authentication only.
Should I use CMP instead of EST?
Consider CMP for explicit certificate confirmation, richer revocation or an industrial profile (RFC 9810, RFC 9483). EST is simpler and more widely implemented.
Can ACME issue certificates to devices?
Not with the standard DNS challenges. The ACME device attestation draft adds hardware identifier types and a device-attest-01 challenge using a WebAuthn attestation statement. It is still a draft, though Apple documents an ACME payload that uses it.
How do I stop SCEP issuing certificates for arbitrary names?
Do not let the profile copy names from the CSR. Derive or validate names against your MDM or inventory, restrict SAN types per endpoint and fix the extended key usage. A dynamic challenge that encodes the expected subject, as Intune's does, enforces this at the server.
How long should device certificates be valid?
As short as your renewal automation reliably handles. Intune allows up to 24 months for SCEP certificates, but shorter lifetimes limit leaked keys and reduce reliance on revocation.
Why software supply chain hygiene still matters here
Enrollment security is mostly PKI configuration, and the fix for a leaked challenge or a loose profile comes from your CA and MDM, not from a dependency update. But the software doing the enrolling is software you build and ship: the CA and RA containers, SCEP and EST client libraries, CoAP and DTLS stacks and the TLS libraries inside device firmware are all open source components that need tracking and patching like any other.
Safeguard does not issue certificates, manage devices or protect CA keys. Where it does help is inventory and patching of those components: safeguard scan --dir . on a firmware or agent repository, --image on the container image that runs your CA or registrar, or --sbom on an SBOM a device vendor gives you, then matching the components against vulnerability databases and showing which projects contain a given version.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session. It opens the interactive terminal, is asked to fix a lodash CVE in a repository, reads the finding and advisory, asks before any command that changes something, edits package.json, runs npm install and npm test with approval, prints a summary and pushes nothing. The same flow applies to the enrollment agent or RA service you maintain: find the vulnerable library, apply the upgrade and run the tests before anything ships.
Click through it at your own pace, or open it full screen.
The second demo sets up the local runner on your own machine, approves it with its device code and scans from the terminal. That suits device firmware and CA images that are built on internal machines and never leave the build network.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. A PKI team working in Claude could ask which of its projects carry a particular TLS or CoAP library version, with tool permissions the team sets.
Click through it at your own pace, or open it full screen.
To try the CLI, download Safeguard and follow the CLI documentation.
Quick check
Why does RFC 8894 forbid single DES and MD5 even though stronger algorithms are available?
Because GetCACaps is not authenticated. If a server still accepts single DES and MD5, an on-path attacker can remove the stronger options from the GetCACaps response and push the client down to them, and both are practical to break.
An EST client has no trust anchors yet. What must happen before it trusts the /cacerts response?
A human (or pre-configured data) must verify the CA certificate out of band, for example by its SHA-256 fingerprint. The client must not send HTTP credentials on the unauthenticated connection, and must open a new authenticated TLS session afterwards.
Sources
- RFC 7030: Enrollment over Secure Transport
- RFC 8894: Simple Certificate Enrolment Protocol
- RFC 8951: EST clarification
- RFC 9148: EST-coaps
- RFC 9908: EST CSR attributes
- RFC 8295: EST Extensions
- RFC 9266: Channel Bindings for TLS 1.3
- RFC 4210: CMP
- RFC 9480: CMP Updates
- RFC 9810: CMP
- RFC 9811: HTTP Transfer for CMP
- RFC 9482: CoAP Transfer for CMP
- RFC 9483: Lightweight CMP Profile
- RFC 8555: ACME
- IETF Datatracker: ACME device attestation draft
- Apple Developer: SCEP payload
- Apple Developer: ACMECertificate payload
- Microsoft Learn: SCEP infrastructure for Intune
- Microsoft Learn: Intune SCEP certificate profiles
- Microsoft Learn: Intune certificates overview
- NIST SP 1800-16
- OpenXPKI project
- OpenXPKI Docker README
- OpenXPKI configuration QUICKSTART
- Dogtag PKI
- sscep README
Last reviewed: 25 October 2026. Check the IETF Datatracker for the current status of the ACME device attestation draft and any further EST or CMP updates.