SCEP (Simple Certificate Enrolment Protocol, RFC 8894) is how network devices, appliances and MDM-managed handsets enrol for a certificate without running an agent. In Skyrsoft CertOps a SCEP endpoint is a named object rather than a server-wide setting: each one maps a URL slug to a certificate template and an issuing CA. A managed mobile fleet, a rack of switches and a set of repointed legacy clients can therefore each have their own endpoint, their own CA and their own challenge mode. The slug is fixed at creation and immutable, so renaming a template or a CA never breaks a URL already configured on a device. SCEP is a Pro feature; on a Free licence the endpoints answer HTTP 402.
How it works
A device proves it is allowed to enrol by putting a challenge password in the
challengePassword attribute of its PKCS#10 request. Where that password comes from is the main
decision, and there are three answers. A device renewing a certificate it already holds is the
exception, and takes a fourth path described below. An MDM usually fetches its challenge itself,
per device, from a challenge URL protected by HTTP Basic:
# the MDM mints a fresh single-use challenge for one device
$ curl -u mdm-android:•••••• \
'https://certops.example.com/scep/mdm-android/challenge?format=json'
{
"challengePassword": "XbQ7pKmSvD2h…R4mU9fTyaCeJ",
"expiresAt": "<issued + expiryMinutes, ISO 8601 UTC>",
"expiryMinutes": <the endpoint’s token lifetime>,
"caThumbprint": "9F2C…A417"
}
# the device redeems it on the endpoint’s own SCEP URL
$ curl 'https://certops.example.com/scep/mdm-android?operation=GetCACaps'
POSTPKIOperation
GetNextCACert
SHA-256
SHA-512
RenewalTokens are 32 characters drawn from A–Z a–z 0–9. That alphabet is deliberate: a Windows SCEP
client encodes the challenge as an ASN.1 PrintableString, whose permitted set excludes the
underscore, so a base64url token fails on Windows roughly two times in five. The default response
format is Microsoft NDES’s own page, because that is what an MDM configured for a Microsoft AD CS
certificate authority expects to parse; ?format=json is there for your own tooling.
Challenge modes
Static challenge
One shared password, set once on the endpoint, which an MDM pushes into a single SCEP profile applied to every device it targets. This matches the shape of a SCEP profile — one challenge field, many devices — and it adds no per-device admin work as a fleet grows. Anyone who extracts the password from one device can reuse it, so the real authorisation boundary in this mode is the template’s request validation rules plus whatever restricts the endpoint’s reachability to the MDM’s own egress addresses.
Admin-minted one-time tokens
Single-use tokens, generated one or a batch at a time from the endpoint’s Tokens panel and
handed to a specific device out of band. Each can carry a device hint for your own reference and an
identity binding, described below. The panel lists every token minted for the endpoint — pending,
consumed with the serial it produced, expired or revoked — and lets you revoke an unused one early.
GetCACert never hands out a token: it is unauthenticated by RFC 8894, so a token only ever exists
because an admin or an authenticated automation minted it.
The MDM challenge URL
Most MDMs will not accept a challenge you minted by hand. They expect to fetch one themselves, per
device, from a challenge URL with a username and password, which is how Microsoft NDES has always
worked and how Okta’s own hosted CA exposes the same thing. Every endpoint in OTP mode serves one,
so an OTP endpoint asks for a challenge username and password and will not save without them. The
canonical path is the SCEP URL with /challenge appended; the NDES spellings /mscep_admin and
/mscep_admin/mscep.dll map to the same handler for admins working from documentation that shows
one of those. Authentication is HTTP Basic only. A client written narrowly against real NDES may
try NTLM or Negotiate, and when it does the server log names the scheme it sent rather than
reporting a bad password.
Certificate-authenticated renewal
A device that already holds a certificate this endpoint issued can renew it by signing the request with that certificate, and sends no challenge password at all. This is RFC 8894 §3.3.1, it is a per-endpoint setting, and it is on by default. Three gates apply. The signer has to be a certificate CertOps issued and still considers Active, which is what stops a revoked or suspended certificate renewing itself back into a working one. The signature has to verify against the presented certificate, which proves the caller holds its private key rather than a copy of the public certificate. And the new CSR has to preserve the old certificate’s identity — both the subject CN and any UPN SAN — so a device certificate can only ever renew into itself.
This is what makes a one-time challenge genuinely one-time. A device provisioned with a single-use
token consumes it at first issuance and never needs another secret, because every subsequent
renewal authenticates with the certificate. Anything that is not a valid renewal falls back to the
ordinary challenge path unchanged, so a self-signed signer, a foreign certificate or a revoked one
simply enrols the normal way. A default endpoint advertises Renewal in its GetCACaps response;
turning the setting off removes it.
Binding a request to an identity
A valid challenge proves that the caller holds a credential an admin authorised to exist. On its own it says nothing about which identity the resulting certificate may claim. Unless something else constrains it, a device holding a valid challenge can put any subject or SAN the template’s CSR policy allows into its request, including another person’s UPN. Two independent levers close that, and they suit different deployments.
Identity binding on a one-time token
When generating a token you can set an expected value for one field — UPN, DNS name, email address
or subject CN. At enrolment the CSR’s actual value in that field is compared against it, and a
mismatch is refused outright with IdentityMismatch even though the token itself was valid. The
binding is set by an admin at mint time, before the device ever connects, which makes it the
strongest guarantee available here. It is inherently per-device, so it fits individually
provisioned machines rather than a fleet-wide static rollout.
Request validation on the UPN SAN
A request validation rule with match field CSR Subject SAN (UPN) and object type User checks the CSR’s UPN SAN against Active Directory directly, and rejects a request unless it names a real, enabled account. This is the practical floor for a static-mode rollout, where there is no per-device token to bind. It confirms the claimed UPN exists and is enabled; it does not prove the device presenting the CSR is entitled to that identity, so a device holding a shared fleet password could still claim a different real colleague’s UPN and pass the check.
Approval gating
Two settings decide whether an endpoint ever queues a request for approval, and they do not combine the way you might expect. With Enforce certificate template approval rules off — the default — every enrolment issues immediately once the challenge is verified, and the template’s own “requires approval” flag has no effect. Turn it on and that flag is honoured; a template that does not itself require approval then runs through the approval rules engine as an agent enrolment would.
Think carefully before turning it on. SCEP has no push notification, so the only way a client learns a queued request was approved is to resubmit the identical request and see whether the answer changed. Real clients do retry, but only a few times and close together: Apple’s SCEP payload defaults to three attempts ten seconds apart. That is not long enough for a human to open the console. A queued request does not consume its token, so a client’s own retry can pick the certificate up once approval lands and reconnects to the same request an admin reviewed — but the token still expires on its own schedule, so set a generous expiry on an endpoint with approval enabled.
Client URLs
| Method | Path | Purpose |
|---|---|---|
| GET | /scep/{slug}?operation=GetCACaps |
Advertised capabilities |
| GET | /scep/{slug}?operation=GetCACert |
CA/RA certificates, PKCS#7 |
| POST | /scep/{slug}?operation=PKIOperation |
Enrolment, DER PKCS#7 CMS body |
| GET/POST | /scep/{slug}/pkiclient.exe |
Windows NDES-client compatible alias |
| GET | /scep/{slug}/challenge |
Mint one challenge, HTTP Basic |
A challenge is validated against the specific endpoint that issued it, so a device has to be
configured with the exact /scep/{slug} URL. The pkiclient.exe alias exists because Windows’
native SCEP client appends that path unconditionally to whatever URL it is given. The same client
sends its initial capabilities probe as plain HTTP even when the configured URL is https://, so
CertOps answers /scep/* in the clear on port 80 as well.
Limits
A challenge authenticates the requester, not the device identity it names. That distinction is the most important thing on this page. A valid static or one-time challenge tells CertOps that something holding an authorised secret is asking; it does not establish that the machine asking is the one entitled to the subject or SAN in the request. A dynamic challenge fetched from the challenge URL carries no binding at all, because the protocol gives the MDM no way to say which device a challenge is for. What a certificate may end up containing is therefore governed by the template’s request validation rules, by an identity binding on an admin-minted token, and by who can reach the endpoint.
Where SCEP certificates are used for NPS and 802.1X, the containment is an OU and a group. CertOps can resolve or create an Active Directory object per enrolment so that NPS has something to strong-map the certificate to. The requester supplies the UPN in look-up mode, so a leaked challenge could name a different account. Confining the look-up to a dedicated OU is what bounds that: put only unprivileged resource accounts in it, target a deny-logon GPO at them — deny local, RDP, network, service and batch — and the blast radius of a leaked challenge is exactly those accounts. Nominate a group as well and use it as the NPS network-policy group condition, which must be a Machine Groups condition rather than User Groups when the objects are computer accounts. These are real AD credentials. No EKU changes that: a certificate with Client Authentication, or none at all, is accepted for Kerberos PKINIT, which was tested against a real KDC rather than assumed.
Request validation rules cannot gate create-on-demand provisioning, and are not offered as though they could. Validation checks a claimed name against the directory, and in create mode CertOps is the thing creating the object, so the check would be circular. Set the endpoint’s Max objects cap instead, which is what stops a shared challenge becoming an object-creation denial of service against your directory.
AES is deliberately not advertised. A default endpoint’s GetCACaps returns
POSTPKIOperation, GetNextCACert, SHA-256, SHA-512 and Renewal. AES is absent because the
issued-certificate response is always enveloped with 3DES regardless of what the client used. 3DES
is Apple’s own documented SCEP-payload fallback, so the omission costs interoperability nothing.
A create-mode certificate cannot use certificate-authenticated renewal. Where the endpoint
mints an AD object per enrolment, the issued subject is rewritten to the UPN built from that
object’s generated sAMAccountName,
so a device presenting its old certificate alongside a CSR carrying its own device name fails the
identity-preservation check. Those devices fall back to challenge-based re-enrolment. CertOps
generates a fresh object and a fresh name on each enrolment, so there is no stable identity for a
renewal to preserve.
Apple’s native SCEP payload accepts RSA 1024 or 2048 only. No larger RSA key size, and no ECDSA. Point Apple-managed devices at a template using RSA 2048; a template they cannot handle fails the enrolment outright.
The challenge URL speaks HTTP Basic and nothing else. Real NDES sits behind IIS Windows Integrated Authentication, so an MDM written narrowly against it may only offer NTLM or Negotiate, and such a client cannot use this endpoint. Okta’s hosted CA has the same constraint for the same reason.