SCEP Certificate Enrollment
SCEP (Simple Certificate Enrollment Protocol, RFC 8894) is a widely supported protocol that allows devices to automatically obtain and renew certificates from a certificate authority. Unlike manually provisioned certificates, SCEP lets each device enroll itself using a pre-shared challenge password, which makes it practical to deploy certificates to large fleets of devices without touching each one individually.
A common use case is operational technology (OT) and IoT deployments, where sites host many devices that need to authenticate to local services or encrypt traffic between each other. For example, a utility operator may have energy meters distributed across substations; with SCEP each meter can enroll a certificate from the local site's CA at first boot and renew it automatically before expiry, without any manual certificate distribution. Similarly, a retail or facilities operator running on-site hardware — shop gates, access control panels, cameras — can use SCEP to give every device a unique identity certificate tied to the site's CA, enabling mutual TLS between devices and management systems running in Avassa-managed containers on the same site.
In both cases, Avassa acts as the SCEP server: the TLS CA lives in Strongbox on the Control Tower, and the SCEP endpoint is exposed on each site so that devices can reach it locally even when connectivity to the cloud is intermittent.
When SCEP is enabled on a TLS CA, devices can request certificates over HTTP or HTTPS without requiring manual intervention.
For the background on device enrollment, and where SCEP sits relative to EST, see Device certificate enrollment.
SCEP enrollment requires an RSA-keyed CA. ECDSA-keyed CAs are not supported
because SCEP uses RSA key transport (PKCS#1 v1.5) to protect the enrollment
message. Use cert-key-type: rsa when creating the CA.
Enable SCEP on a TLS CA
Create a TLS CA with SCEP enabled and a challenge password that enrolling devices must present:
supctl create strongbox tls ca <<EOF
name: device-ca
cert-key-type: rsa
ttl: 2y
auto-renew:
renew-threshold: 90d
activate-threshold: 60d
scep:
enabled: true
challenge-password: my-secret-password
ttl: 365d
EOF
The challenge-password is a pre-shared secret that all enrolling devices
must include in their PKCS#10 CSR. The ttl under scep controls how long
the issued device certificates will be valid; if omitted it defaults to the
CA-level TTL.
Find the SCEP enrollment URLs
Show the CA to retrieve the SCEP endpoint URLs:
supctl show strongbox tls ca device-ca
The output includes the scep section with the computed URLs:
name: device-ca
scep:
enabled: true
ttl: 365d
http-url:
- http://control-tower.example.com:4664/scep/a1b2c3d4-.../device-ca
- http://api.internal:4664/scep/a1b2c3d4-.../device-ca
- http://192.168.1.10:4664/scep/a1b2c3d4-.../device-ca
https-url:
- https://api.internal:4646/scep/a1b2c3d4-.../device-ca
...
http-url and https-url are lists that include several variants:
- FQDN (
control-tower.example.com) — for devices that have DNS configured - Local IP (
192.168.1.10) — for devices without DNS, using the node's actual IP address on the local network api.internal— reachable from containers running on the site
The IP-based URLs reflect the addresses of the node that handles the request.
When the command is run against a specific site using --site, the IPs shown
are the site's local addresses, which is what devices on that site need.
All URLs embed the tenant UUID and CA name so each tenant and CA combination has its own isolated SCEP endpoint.
Restrict which names devices may request
By default, when allowed-domains is empty, any CN or DNS SAN is accepted.
Use allowed-domains together with allow-subdomains to limit what names
devices may request:
supctl update strongbox tls ca device-ca <<EOF
scep:
enabled: true
challenge-password: my-secret-password
allowed-domains:
- devices.example.com
allow-subdomains: true
allow-ip-sans: false
ttl: 365d
EOF
With this configuration:
- A CSR with CN
router1.devices.example.comis accepted because it is a subdomain ofdevices.example.comandallow-subdomainsis true. - A CSR with CN
router1.other.comis rejected. - A CSR with an IP address SAN is rejected because
allow-ip-sansis false (the default).
To allow any name (for example, during initial testing), set allow-any-name:
supctl update strongbox tls ca device-ca <<EOF
scep:
enabled: true
challenge-password: my-secret-password
allow-any-name: true
ttl: 365d
EOF
SCEP operations
The SCEP endpoint supports the following standard operations:
| Operation | Description |
|---|---|
GetCACert | Retrieve the CA certificate (or certificate chain for intermediate CAs) |
PKCSReq | Enroll a new certificate by submitting a PKCS#10 CSR |
GetCRL | Retrieve the current Certificate Revocation List |
GetCert | Not currently supported; returns a failure response |
Enroll a device
Any standard SCEP client works. With sscep, first retrieve the CA certificate:
sscep getca \
-u http://control-tower.example.com:4664/scep/a1b2c3d4-.../device-ca \
-c ca.crt
Generate a key and CSR for the device, then enroll:
openssl genrsa -out device.key 2048
openssl req -new -key device.key -out device.csr \
-subj "/CN=router1.devices.example.com"
sscep enroll \
-u http://control-tower.example.com:4664/scep/a1b2c3d4-.../device-ca \
-c ca.crt \
-k device.key \
-r device.csr \
-p my-secret-password \
-l device.crt
On success, device.crt contains the issued certificate signed by device-ca.
Some clients will not pass the challenge password on the command line and
expect it inside the request instead, as the PKCS#9 challengePassword
attribute:
cat > req.cnf <<'EOF'
[req]
prompt = no
distinguished_name = dn
attributes = attrs
[dn]
CN = router1.devices.example.com
[attrs]
challengePassword = my-secret-password
EOF
openssl req -new -key device.key -out device.csr -config req.cnf
With strongSwan's pki instead of sscep:
pki --scep --url http://control-tower.example.com:4664/scep/a1b2c3d4-.../device-ca \
--in device.key \
--dn "CN=router1.devices.example.com" \
--password my-secret-password \
--cacert-enc ca.crt --cacert-sig ca.crt \
--outform pem > device.crt
Confirm what was issued:
openssl x509 -in device.crt -noout -subject -issuer -dates
Renew a device certificate
SCEP has no separate renewal operation here: a device renews by enrolling
again before its certificate expires. Choose a ttl that leaves comfortable
margin for a device that has been unreachable for a while, and schedule
re-enrollment well inside it.
If your devices can speak EST instead, prefer it. EST renews against the device's current certificate, so renewal needs no shared secret at all.
Revoke an issued certificate
Certificates issued through SCEP are ordinary certificates of the CA, so they are revoked the same way as any other, and appear in the CA's CRL and OCSP responses. See Certificate revocation for the CA-level operations.
Troubleshooting
The request is refused. Check, in this order:
- Challenge password. It must match the CA's
challenge-passwordexactly, and must reach the server as the PKCS#9challengePasswordattribute. Some clients need it in the CSR rather than on the command line, as shown above. - Requested identity. The CN and any DNS SANs must satisfy
allowed-domainstogether withallow-subdomainsandallow-bare-domains. A request forrouter1.devices.example.comis refused if onlydevices.example.comis listed andallow-subdomainsis false. - IP SANs. Refused unless
allow-ip-sansis true. - Key strength. Requests with keys weaker than 2048-bit RSA are refused.
The CA will not serve SCEP at all. Confirm the CA has an RSA key
(cert-key-type: rsa). An ECDSA-keyed CA cannot serve SCEP enrollment.
The device cannot reach the endpoint. The enrollment port is reachable from the application network and from the configured management addresses. Verify the device is on a network permitted to reach it, and that you picked a URL form the device can resolve.
Disable SCEP
Remove the scep container to disable the SCEP endpoint for a CA:
supctl delete strongbox tls ca device-ca scep
After this, any SCEP request to the endpoint returns HTTP 404.