Skip to content

Windows Autoenrollment

NeySlim edited this page Oct 18, 2026 · 26 revisions

Windows Autoenrollment (MS-XCEP / MS-WSTEP)

Version: 2.242 (introduced in 2.207)
Status: ✅ Production Ready
Specs: MS-XCEP, MS-WSTEP


Overview

UCM natively serves the Active Directory Certificate Services enrollment protocols, so Windows clients can enroll certificates against UCM exactly as they would against a real AD CS deployment — without installing AD CS at all:

  • MS-XCEP (Certificate Enrollment Policy) — GetPolicies policy discovery: advertises the certificate templates, key requirements, EKUs and enrollment URIs Windows clients use to decide what to enroll and where.
  • MS-WSTEP (WS-Trust X.509v3 Token Enrollment) — certificate issuance and renewal: parses a RequestSecurityToken (RST) SOAP request and returns a RequestSecurityTokenResponseCollection with the issued certificate.

This is the protocol pair behind:

  • MMC → Request New Certificate (interactive enrollment)
  • certreq / certutil command-line enrollment
  • GPO machine and user autoenrollment — fully unattended certificate deployment at boot/logon, driven by Group Policy

Direction matters: this page covers UCM acting as the enrollment CA for Windows clients. For the opposite direction — UCM submitting CSRs to an existing Microsoft AD CS — see Microsoft CA Integration.


Requirements

Requirement Needed for Notes
A UCM CA with a private key Everything Selected in the XCEP/WSTEP configuration
pyspnego with the gssapi backend Kerberos binding only Optional dependency: pip install pyspnego[kerberos] (needs the system Kerberos libraries, e.g. libkrb5-dev, at build time). Username/Password and certificate-renewal bindings work without it
A Kerberos keytab + SPN Kerberos binding only Generated on the domain controller with ktpass (or ktutil), e.g. for HTTP/ucm.example.com@EXAMPLE.COM
AD Connector configured Unattended GPO autoenrollment Lets UCM derive the subject of the deliberately empty CSRs GPO submits — see AD Connector
At least one template with Allow autoenrollment Unattended GPO autoenrollment See Template flags

Since v2.208, the Kerberos availability check requires the actual gssapi backend, not just an importable spnego package (which ships transitively with other dependencies) — XCEP no longer advertises, and clients no longer attempt, a Kerberos binding that would fail at request time.


Configuration

Settings → Windows Autoenrollment

The tab starts with a Setup Checklist tracking what is configured versus what is still needed, separately for manual enrollment (MMC, certreq) and unattended GPO autoenrollment.

Policy Discovery (XCEP):

Setting Description Default
Enabled Serve MS-XCEP GetPolicies Off
Certificate Authority CA whose templates are advertised —
Username / Password Credential for the UsernamePassword binding —

Certificate Issuance (WSTEP):

Setting Description Default
Enabled Serve MS-WSTEP issuance/renewal Off
Certificate Authority CA that signs enrolled certificates —
Username / Password Credential for the UsernamePassword binding —
Validity (days) Default validity of issued certificates 365

Kerberos / SPNEGO:

Setting Description
Enabled Accept Authorization: Negotiate on the Kerberos endpoints
Service Principal Name (SPN) e.g. HTTP/ucm.example.com@EXAMPLE.COM
Keytab Uploaded keytab file for the SPN above. The tab re-inspects the stored keytab and shows its principal, whether it parses, and whether it matches the configured SPN

Settings → Active Directory (AD Connector)

See AD Connector below.


Endpoints

The URL shape follows Microsoft's own AD CS convention of encoding the authentication binding in the path. All endpoints are public protocol endpoints (no UCM session auth) and return 503 when the corresponding protocol is administratively disabled. GET/HEAD reachability probes return an empty 200 (they never trigger an HTTP Basic challenge, which would break real Windows clients expecting message-level auth).

Policy discovery (MS-XCEP, GetPolicies)

Endpoint Authentication
/ADPolicyProvider_CEP_UsernamePassword/service.svc WS-Security UsernameToken (HTTP Basic fallback)
/ADPolicyProvider_CEP_Kerberos/service.svc HTTP Negotiate (Kerberos/SPNEGO)

Certificate issuance & renewal (MS-WSTEP, RST → RSTRC)

Endpoint Authentication Purpose
/ADCertificateService_CES_UsernamePassword/service.svc WS-Security UsernameToken (HTTP Basic fallback) Initial enrollment
/ADCertificateService_CES_Certificate/service.svc XML-DSig-signed RST (existing certificate) Renewal only
/ADCertificateService_CES_Kerberos/service.svc HTTP Negotiate (Kerberos/SPNEGO) Initial enrollment and renewal — GPO autoenrollment's usual path

The XCEP policy response advertises the UsernamePassword and Certificate CES URIs unconditionally, but the Kerberos CES URI only when Kerberos is actually configured (enabled + SPN + keytab) and the gssapi backend is installed — a client is never pointed at a binding guaranteed to fail.


Authentication Bindings

Three bindings, mirroring real AD CS CES endpoints:

1. WS-Security UsernameToken (+ HTTP Basic fallback)

Credentials arrive as a wsse:UsernameToken inside the SOAP message header — not as an HTTP Authorization header. This matches real Windows client behavior: a client configured for the UsernamePassword binding refuses to answer an HTTP Basic challenge (WS_E_SERVER_REQUIRES_BASIC_AUTH). HTTP Basic is still accepted as a fallback (convenient for curl/testing), but the endpoints never challenge for it; authentication failures come back as SOAP faults. The username/password is the single shared credential configured in Settings (stored hashed).

2. Client-certificate renewal (XML-DSig)

For renewal, the RST itself is WS-Security signed (XML-DSig) with the private key of the client's current certificate — there is no HTTP-level authentication. UCM verifies that:

  • the signature covers the embedded CSR,
  • the signing certificate matches byte-for-byte a certificate UCM itself issued from the configured CA — matching on serial/subject alone is deliberately not enough, since both are public and could be forged onto a self-signed lookalike,
  • the stored certificate is neither revoked nor expired,
  • the CSR's subject (and SAN, if present) match the signing certificate,
  • the WS-Security signature uses RSA or ECDSA with SHA-256, SHA-384 or SHA-512; other algorithms are refused since v2.241.

3. Kerberos/SPNEGO (Negotiate)

Transport-level HTTP authentication per RFC 4559, exactly like IIS Windows Integrated Authentication: Authorization: Negotiate <SPNEGO token>, verified against the uploaded keytab via GSSAPI. This is what unattended GPO autoenrollment uses — the machine (or user) authenticates with its own Kerberos ticket, no credential prompt. Multi-leg SPNEGO exchanges (e.g. a request-MIC round) are supported. Requires the optional pyspnego[kerberos] dependency.


AD Connector

Settings → Active Directory configures a single stored LDAP connection UCM uses to query Active Directory directly. It is deliberately separate from any LDAP provider under SSO — that one is for logging into UCM; this one is for certificate-related AD lookups, and works whether or not SSO is configured.

Setting Description
Domain Controllers / Port One domain controller per row, each by its own hostname, tried in order with failover (since v2.233); default port 389
LDAPS / Verify SSL LDAP over SSL/TLS, with optional custom CA bundle. Naming each DC by its own hostname is what makes Verify SSL usable on a multi-DC domain: a DC certificate names only its own host
Base DN Search base, e.g. DC=example,DC=com
Bind DN / Password Service account for lookups — the bind password is encrypted at rest. Both are required while the connector is enabled (since v2.233)
Health Check Interval Seconds between real binds to each DC (default 120). An unreachable DC is skipped at enrollment time; the verdict shows on the settings row and against each DC, expires, and never rules out every DC
Test Connection Verifies connectivity and credentials before saving

Upgrade note (v2.233): an enabled connector saved without a bind password stops answering lookups until credentials are entered in the settings dialog, and an API client must send both when it enables one.

Why it exists: subject derivation for empty CSRs

Windows GPO machine autoenrollment deliberately submits a subject-less ("naked") CSR — no CN, no SAN — trusting the CA to build the subject from the directory, the way a real AD CS template configured with "Build from this Active Directory information" does. When a naked CSR arrives on the Kerberos-bound WSTEP endpoint, UCM resolves the authenticated Kerberos principal in AD:

  • Machine principal (e.g. WIN11$@EXAMPLE.COM) → subject becomes CN=<dNSHostName> of the computer object. Always on (no template opt-in needed).
  • User principal → opt-in per template via the Build subject from Active Directory flag: the subject becomes the AD user object's own directory-path DN (e.g. CN=Jane Doe, CN=Users, DC=example, DC=com), plus the userPrincipalName as a UPN otherName SAN — matching the shape of a real AD CS-issued User certificate. If the AD mail attribute is set, it is added opportunistically as an E= RDN and an rfc822Name SAN; a missing email never blocks enrollment.

If the connector is not configured, no template opted in, or the lookup fails, the naked CSR is rejected with the normal "subject required" error — derivation never fails open. Kerberos principals from a realm other than the connector's own domain are refused (cross-realm tickets must not be resolved against a same-named account in the wrong domain).


Template Flags

Three opt-in flags on certificate templates (Templates → Enrollment) control autoenrollment behavior:

Flag Effect
Allow autoenrollment (autoenroll_enabled) Advertises the template with autoEnroll=true in the XCEP policy — the Enroll vs. Autoenroll permission split real AD CS enforces. Without it, the template is offered for manual enrollment (enroll=true) but unattended GPO autoenrollment ignores it
Build subject from Active Directory (ad_derived_subject) Opts user enrollments into AD-derived subjects (see above). Machine subject derivation is always available
Allowed AD group (allowed_ad_group) Per-template Enroll ACL: Kerberos-authenticated requesters must be a member of this AD group — nested group membership counts (resolved via the AD LDAP_MATCHING_RULE_IN_CHAIN matching rule). Non-members are refused with a fault

Which templates are advertised at all follows the CA's template pinning: templates pinned to the configured CA, or every active template when none are pinned.

Note: MS-WSTEP carries no explicit "which template did you pick" signal, so UCM matches the request to a template by its requested EKUs. A naked GPO CSR (no EKU extension) matches no template — in that case the Enroll ACL fails closed: the requester must satisfy at least one restricted template's group, rather than none of them.


Pinned Subject Fields (v2.208)

A template can pin the C, ST, L, O and OU subject fields to fixed values (Templates → edit → Pinned subject fields). On every WSTEP issuance, pinned values override whatever the client's CSR (or the AD derivation) supplied for those fields — while CN and SAN always stay dynamic. CN can never be pinned.

This gives a "hybrid subject template": organizational identity controlled centrally, host/user identity still per-request. If the request could not be matched to a specific template and several CA-resolvable templates pin the same field to different values, that field is skipped (with a warning in the logs) rather than silently picking a winner.


Strong Certificate Mapping (v2.208, KB5014754)

On Kerberos-bound issuance, when the AD Connector is enabled, UCM embeds the authenticated requester's AD SID in the issued certificate as the SID security extension (szOID_NTDS_CA_SECURITY_EXT, OID 1.3.6.1.4.1.311.25.2) — the same extension real AD CS adds automatically, and what domain controllers use for strong certificate mapping under KB5014754 (certificate-based authentication enforcement).

Unlike the other AD Connector features this is unconditional (no per-template opt-in) and applies to naked and fully-populated Kerberos-bound CSRs alike. Fail-closed: if the connector is enabled but the SID lookup fails for a request, issuance is refused rather than issuing a weaker-mapped certificate. If the connector is not configured at all, the extension simply is not added. UsernamePassword-bound requests never get it — there is no per-request AD principal to resolve.


Windows Interop Notes

Details verified against real Windows clients:

  • TLS 1.2 cap while WSTEP is enabled — some schannel-based TLS 1.2-only clients abort the handshake against a TLS 1.3-capable server. UCM caps its TLS at 1.2 only while WSTEP is enabled; installs that never use Windows autoenrollment keep TLS 1.3. Toggling WSTEP automatically restarts the service so the cap tracks the setting (on Docker, restart the container when prompted).
  • Microsoft Certificate Template extension — issued certificates carry the Certificate Template extension with a stable per-(CA, template) policy OID; without it, CX509Enrollment::Enroll fails with CERTSRV_E_PROPERTY_EMPTY.
  • CMC / full PKCS#7 requests — Windows "Full PKCS#7" requests are unwrapped (CMC envelope → inner PKCS#10) and answered with a CMC full PKI response referencing the request's CMC body part.
  • Proof of possession — always enforced. For plain PKCS#10 requests, via the CSR's self-signature; for PKCS#7/CMC-wrapped requests (whose inner self-signature is not reliable from real clients), via the outer CMS SignedData signature, which must verify against — and identify itself by the subject key identifier of — the CSR's own public key.
  • SHA-1 CSRs are rejected — certreq defaults to SHA-1 unless the INF sets HashAlgorithm=sha256; UCM refuses MD5/SHA-1-signed CSRs with an explicit message.

GPO Client Setup

On the Windows side (Group Policy Management, Computer Configuration and/or User Configuration → Policies → Windows Settings → Security Settings → Public Key Policies):

  1. Certificate Services Client – Certificate Enrollment Policy
    • Set Configuration Model to Enabled.
    • Add a policy server: URL https://ucm.example.com:8443/ADPolicyProvider_CEP_Kerberos/service.svc, authentication type Windows integrated (Kerberos) — required for silent autoenrollment. The Username/password URL (.../ADPolicyProvider_CEP_UsernamePassword/service.svc) is for interactive "Request New Certificate" enrollment only.
    • Validate the server, then save. (The UCM server's TLS certificate must be trusted by the client.)
  2. Certificate Services Client – Auto-Enrollment
    • Set to Enabled, and check Renew expired certificates… and Update certificates that use certificate templates.
  3. Ensure at least one UCM template has Allow autoenrollment set, and (for user certificates with AD-derived subjects) Build subject from Active Directory.

At the next policy refresh (or gpupdate /force + certutil -pulse), eligible machines and users enroll silently against UCM. Interactive enrollment is available immediately in certlm.msc / certmgr.msc → Request New Certificate.


Monitoring

Audit Logs

Action Operation
certificate.issued WSTEP enrollment (UsernamePassword or Kerberos binding)
certificate.renewed WSTEP certificate-bound renewal

XCEP GetPolicies policy discovery is not audited (read-only).

Logs

sudo journalctl -u ucm | grep -i -E "wstep|xcep|kerberos"
tail -f /var/log/ucm/ucm.log | grep -i wstep

Troubleshooting

Kerberos endpoints return 503 "Kerberos binding not configured"

Causes:

  • The gssapi backend is not installed — pip install pyspnego[kerberos] (needs libkrb5-dev or equivalent to build). Since v2.208 the Kerberos binding is also no longer advertised in the XCEP policy in this case, so clients stop attempting it.
  • Kerberos is not enabled, no SPN is set, or no keytab has been uploaded.

Fix: check Settings → Windows Autoenrollment → Kerberos/SPNEGO — the tab shows whether the library is available, and whether the uploaded keytab parses and matches the configured SPN.

Autoenrollment silently enrolls nothing

  • The template must have Allow autoenrollment set — enroll=true alone is ignored by the unattended autoenrollment engine.
  • The GPO policy server must use Kerberos authentication; the Username/Password URL cannot be used unattended.
  • If an Allowed AD group is set, the machine/user must be a (possibly nested) member.

Naked CSR rejected: "CSR subject must include a non-empty CN or a SubjectAltName"

GPO submitted a subject-less CSR but UCM could not derive a subject from AD. Check that the AD Connector (Settings → Active Directory) is enabled and its connection test passes, and — for user enrollment — that a template has Build subject from Active Directory set. Cross-realm Kerberos principals are refused by design.

"Could not resolve requester identity for strong certificate mapping"

The AD Connector is enabled but the SID lookup for the authenticated principal failed (account not found, LDAP unreachable). Issuance fails closed — fix connectivity or the account, or disable the connector if AD lookups are not wanted.

Enrollment fails with CERTSRV_E_PROPERTY_EMPTY

The issued certificate reached the client but carried no Certificate Template extension — in practice this means the request did not match any advertised template. Check the CA's template pinning and the template's EKU configuration.

CSR rejected: "signed with an unsupported hash algorithm (sha1)"

certreq INF files default to SHA-1. Add HashAlgorithm=sha256 to the [NewRequest] section.


Security Considerations

  • Renewal identity is byte-exact — the certificate signing a renewal RST must match a UCM-issued certificate byte-for-byte; serial/subject lookalikes are refused.
  • Enroll ACL fails closed — unresolvable principals, cross-realm tickets and failed group lookups deny enrollment rather than skipping the check.
  • AD Connector bind password is encrypted at rest; saving fails closed if encryption is unavailable.
  • The UsernamePassword binding authenticates a single shared credential — prefer the Kerberos binding for per-machine/per-user identity, group ACLs and strong certificate mapping.
  • Request bodies are size-capped (64 KB XCEP / 128 KB WSTEP) and disabled protocols answer 503 on every endpoint.

Related Documentation


Standards Compliance

  • ✅ MS-XCEP — X.509 Certificate Enrollment Policy Protocol (GetPolicies)
  • ✅ MS-WSTEP — WS-Trust X.509v3 Token Enrollment Extensions (RST/RSTRC)
  • ✅ WS-Security / WS-Trust 1.3 — UsernameToken and XML-DSig message security
  • ✅ RFC 4559 — SPNEGO-based Kerberos HTTP authentication
  • ✅ RFC 5272 — CMC (full PKCS#7 request/response)
  • ✅ RFC 2986 — PKCS#10 Certificate Request Syntax

Need Help? See Troubleshooting or GitHub Issues

Clone this wiki locally