Documentation
Raft SCEP Bridge — Windows installation guide
Deploy the Raft SCEP Bridge on Windows in a production-hardened configuration, with no plaintext service-account passwords stored on disk.
Table of contents
This guide walks you through deploying the Raft SCEP Bridge on a Windows host in a production-grade configuration. By the end you will have a hardened bridge service that polls your Raft tenant for pending certificate requests, performs the SCEP exchange against your own AD CS / NDES infrastructure, and returns signed certificates back to Raft — with no plaintext service-account passwords stored on disk.
The guide is self-contained. Everything you need — including all PowerShell commands and complete configuration file examples — is embedded inline.
Quick reference for experienced administrators
This is a condensed checklist for administrators who have deployed a SCEP bridge against AD CS / NDES before and want a one-page summary of the Raft-specific bits. If a step is unfamiliar, jump to the corresponding detailed section below.
From your Raft tenant administrator
- Bridge token — 64-character hex string, generated in the Raft
web UI at
/admin/scep-bridge-keys. Sent asAuthorization: Bearer <token>. Revocable at any time. - Tenant API URL — e.g.
https://raft.example.com. - Certificate template name — agree on the name in advance; it must match what you create in AD, what you set in the NDES registry, and what the tenant admin enters in the Raft SCEP profile.
AD CS / NDES setup
-
Enterprise CA in your forest (offline-root + online-issuing also fine).
-
Custom template, cloned from Computer:
- Compatibility ≥ Windows Server 2008 (forces schema v2).
- Subject Name = Supply in the request (ENROLLEE_SUPPLIES_SUBJECT).
- EKU = Client Authentication only (drop Server Authentication).
- Enroll granted to the bridge service identity and to
ndes_svc.
-
Publish the template and
Restart-Service CertSvc -Force(an IIS reset alone is not enough). -
NDES installed (separate or collocated with CA),
ndes_svcas the IIS app-pool identity, SPNs registered (http/<host>,http/<fqdn>). -
NDES registry under
HKLM\SOFTWARE\Microsoft\Cryptography\MSCEP:SignatureTemplate = <your template name> EncryptionTemplate = <your template name> GeneralPurposeTemplate = <your template name> EnforcePassword = 1 (DWORD) UseSinglePassword = 0 (DWORD, OTP mode) PasswordMax = 999 (DWORD) PasswordValidity = 14400 (DWORD, minutes — 10 days) -
After registry/template changes:
iisreset /restart.
Bridge service identity
-
gMSA — e.g.
RAFTBRIDGE—New-ADServiceAccount … -PrincipalsAllowedToRetrieveManagedPassword "<bridge-host>$"; install on the bridge host withInstall-AdServiceAccount. -
Grant the gMSA Enroll on the cert template (Create the certificate template).
-
mscep_adminauthorization: a dedicated AD security group containing the gMSA, allowed via an explicit Allow rule onDefault Web Site/CertSrv/mscep_adminin IIS. Not local Administrators. -
Reconfigure the
RaftSCEPBridgeservice Log On to the gMSA with empty password:sc.exe config RaftSCEPBridge obj= "<NETBIOS>\RAFTBRIDGE$" password= ""
Bridge install
- Run
raft-scep-bridge-<ver>-x64.exe /quiet(bundle bootstraps VC++ 2015-2022 Redistributable) — ormsiexec /i …if you manage VC++ separately. - Service:
RaftSCEPBridge(“Raft SCEP Bridge”), Start Type Automatic, deliberately not started by the installer. - Diagnostics: ETW provider
Raft-SCEP-Bridge(read withGet-WinEvent -ProviderNameor an Event Viewer custom view) plus a daily-rolling file log inC:\ProgramData\RaftBridge\logs\. - Paths: binary at
C:\Program Files\Raft\Bridge\; config, state, and logs atC:\ProgramData\RaftBridge\. - Installer is not yet Authenticode-signed — expect a SmartScreen prompt on first run.
Minimal config.toml — OTP mode (recommended)
[server]
api_url = "https://raft.example.com"
api_key = "REPLACE-WITH-64-CHAR-BRIDGE-TOKEN"
[ndes]
url = "http://ndes.example.local/CertSrv/mscep/mscep.dll"
[challenge]
mode = "otp"
otp_url = "http://ndes.example.local/CertSrv/mscep_admin/"
[bridge]
poll_interval_secs = 30
key_algorithm = "RSA-2048"
Env-var overrides (machine scope): RAFT_BRIDGE_API_URL,
RAFT_BRIDGE_API_KEY, RAFT_BRIDGE_NDES_URL,
RAFT_BRIDGE_POLL_INTERVAL.
Verify
raft-scep-bridge.exe status # config validation
raft-scep-bridge.exe test-connection # Raft API + NDES GetCACert
Get-WinEvent -ProviderName Raft-SCEP-Bridge -MaxEvents 20
Get-ChildItem C:\ProgramData\RaftBridge\logs\ |
Sort-Object LastWriteTime -Descending | Select-Object -First 1 |
Get-Content -Tail 50
Gotchas at a glance
What the bridge does
The Raft SCEP Bridge is a Windows service that connects your Raft tenant to your on-premises Active Directory Certificate Services (AD CS) — specifically to the Network Device Enrollment Service (NDES) component.
It works by:
- Polling your Raft tenant over HTTPS for pending certificate requests (devices that need a cert issued).
- For each request, generating a key pair on the bridge host, building a CSR, fetching a one-time challenge password from NDES, and submitting the CSR to NDES over SCEP.
- Returning the signed certificate and the issuing CA chain back to Raft over the same HTTPS connection.
Raft delivers the signed certificate to the requesting device. AD CS remains the authoritative issuing CA — Raft never holds your private CA key.
Architecture
Recommended topology. Run the bridge on a separate Windows host from your AD/NDES server. This keeps the bridge’s service identity small in scope and matches the security posture most enterprises use for Windows services that talk to AD CS.
The bridge can run on the same host as AD/NDES if you are deploying a
small environment or a proof-of-concept. The configuration in this guide
works in either layout — the only difference is whether the [ndes].url
in config.toml points to localhost or to a remote hostname.
Prerequisites checklist
On the Raft side
- A bridge token issued to you by your Raft tenant admin (see Obtain a bridge token from your Raft tenant admin).
- The Raft tenant API URL — typically of the form
https://your-tenant.raft.example.comorhttps://raft.your-company.com. - The certificate template name your tenant admin wants the bridge to use (you will create this template in Create the certificate template; agree on a name beforehand so both sides match).
On the Windows side
- A Windows Server host (2019 or later) joined to the Active Directory forest that contains your AD CS infrastructure. This is the bridge host.
- An Active Directory forest with an Enterprise Root or Issuing CA (AD CS). If you do not have one, Prepare AD CS walks you through installing one.
- A Windows Server host (2019 or later) for NDES. NDES must be installed on a member server of the same forest as the CA. In a small environment this can be the same host as the CA; in production it is typically separate.
- Administrative access to the AD domain (a Domain Admin or delegated rights on the relevant containers).
- Local Administrator rights on the bridge host.
Network connectivity
The bridge host must reach:
- The Raft tenant URL outbound over HTTPS (TCP 443).
- The NDES host over HTTP (TCP 80) or HTTPS (TCP 443) depending on how
NDES is published. SCEP traffic itself is signed and encrypted at the
protocol layer, so plain HTTP for the SCEP endpoint is acceptable; the
/CertSrv/mscep_admin/endpoint is typically reached over the same scheme. - A domain controller for Kerberos, LDAP, and gMSA password retrieval (TCP/UDP 88, 389, 636, 464).
If your network uses an outbound proxy, ensure the bridge host trusts the proxy and that the proxy permits the destinations above.
Already running another SCEP integration?
TLS trust
The bridge uses the Windows system trust store. If your Raft tenant is served by an internal CA that is not in the public trust list, that CA certificate must be present in the Local Computer “Trusted Root Certification Authorities” store on the bridge host before the service will succeed at contacting Raft.
Via the Windows GUI:
- Press Win + R, type
certlm.msc, and click OK to open Certificates — Local Computer. (If a UAC prompt appears, click Yes.) - In the tree on the left, expand Trusted Root Certification Authorities, then click Certificates.
- From the menu bar choose Action → All Tasks → Import….
- In the Certificate Import Wizard:
- Click Next.
- On the File to Import page, click Browse…, navigate to your
internal root CA file (for example
C:\Temp\my-internal-root-ca.crt), and click Open, then Next. - On the Certificate Store page, make sure Place all certificates in the following store is selected and the store reads Trusted Root Certification Authorities. Click Next.
- Click Finish, then OK on the success message.
- The imported root should now appear in the certificate list. You may need to press F5 to refresh the view.
Via PowerShell:
# Import an internal root CA into the Local Machine trust store
Import-Certificate -FilePath "C:\Temp\my-internal-root-ca.crt" `
-CertStoreLocation Cert:\LocalMachine\Root
Obtain a bridge token from your Raft tenant admin
The bridge authenticates to your Raft tenant with a bridge token — a 64-character random hexadecimal string. The token is:
- Tenant-scoped: it only grants access to your own tenant.
- Single-purpose: it can only be used to poll for pending SCEP requests and submit results; it cannot perform any other tenant operation.
- Non-expiring by default, but revocable at any time by a tenant admin.
If you do not have Raft tenant-admin access
Ask the person who administers your Raft tenant to issue you a bridge token. Here is a template request you can send them:
Hi,
I am setting up the Raft SCEP Bridge on a Windows host so the tenant can issue certificates through our AD CS / NDES. Could you please:
- Sign in to the Raft web UI as a tenant administrator.
- Go to Bridge Keys in the admin navigation (URL:
/admin/scep-bridge-keys).- Enter a label that identifies this bridge instance — for example
<hostname>-bridge— and click Generate Bridge Key.- The page will display a 64-character token once. Copy it immediately; it cannot be retrieved later.
- Send me the token over an encrypted channel (Signal, an encrypted email, a password manager share, etc.). Do not put it in plaintext email or a chat that is logged unencrypted.
I will also need:
- The Raft tenant API URL (e.g.
https://raft.example.com).- The certificate template name you want the bridge to use. I will create this template in AD CS and will let you know the exact name so we can match it in the SCEP profile on the Raft side.
If the token is ever compromised, you can revoke it from the same page and issue me a new one.
Thanks!
If you have Raft tenant-admin access yourself
- Sign in to your Raft tenant’s web UI as a user with the Admin or SuperAdmin role.
- Open Bridge Keys in the admin navigation. The URL path is
/admin/scep-bridge-keys. - In the “Bridge key name” field, type a label that identifies this bridge — for example, the hostname of the bridge host. The label is only for your own bookkeeping.
- Click Generate Bridge Key.
- The page will display the new 64-character token in a one-time banner. Copy it immediately and store it in a password manager or secure note. Once you navigate away, the token cannot be retrieved. You can always revoke it and generate a new one if you lose it.
To revoke a token later (for example, if the bridge host is decommissioned or you suspect the token is compromised): return to the Bridge Keys page, find the row by its label, and click Revoke. The token is invalidated immediately on the server side; any bridge still using it will start receiving HTTP 401 responses on its next poll.
Receiving and handling the token
Whoever issues the token should deliver it over an encrypted channel.
On the bridge host, you will place the token directly into the
[server].api_key field of config.toml (see
Create config.toml). The configuration file is
protected by the standard NTFS ACLs on C:\ProgramData\RaftBridge,
which only grant access to Administrators and SYSTEM by default.
For an additional layer of separation, you can pass the token via an
environment variable instead of writing it into config.toml:
# Set as a machine-scoped environment variable (visible to the service)
[Environment]::SetEnvironmentVariable("RAFT_BRIDGE_API_KEY", "<token>", "Machine")
When RAFT_BRIDGE_API_KEY is set, it overrides any api_key value in
config.toml. The api_key field in config.toml is still required
by the parser, but you can put a placeholder there and rely on the
environment override at runtime.
Prepare AD CS (Certification Authority)
The following procedure installs an Enterprise Root CA on the current host. In production you would more typically have an offline Root CA and an online Enterprise Issuing CA; the SCEP bridge works with either layout. Adjust the role parameters to match your organisation’s naming conventions and key strength policy.
Sign in to the host that will be the CA as a Domain Admin.
Install the AD CS role
Via the Windows GUI:
- Open Server Manager (it normally launches at sign-in; otherwise Start → Server Manager).
- In the toolbar, click Manage → Add Roles and Features.
- Step through the Add Roles and Features Wizard:
- Before You Begin → Next.
- Installation Type → leave Role-based or feature-based installation selected → Next.
- Server Selection → leave the local server selected → Next.
- Server Roles → tick Active Directory Certificate Services. When prompted, click Add Features to include the management tools → Next.
- Features → Next.
- AD CS information page → Next.
- Role Services → tick Certification Authority. (Leave the other role services unticked for now; NDES will be added later on the NDES host.) → Next.
- Confirmation → Install.
- When installation completes, leave the wizard open.
Configure the CA
Via the Windows GUI:
- In Server Manager, click the yellow notifications flag in the top-right corner. A pane shows Post-deployment Configuration — Configure Active Directory Certificate Services on the destination server. Click that link.
- The AD CS Configuration wizard opens.
- Credentials — verify the user shown is a Domain Admin → Next.
- Role Services → tick Certification Authority → Next.
- Setup Type → select Enterprise CA → Next.
- CA Type → select Root CA → Next.
- Private Key → select Create a new private key → Next.
- Cryptography for CA:
- Select a cryptographic provider:
RSA#Microsoft Software Key Storage Provider - Key length:
4096 - Select the hash algorithm:
SHA256 - → Next.
- Select a cryptographic provider:
- CA Name → enter a Common Name such as
Contoso Issuing CA→ Next. - Validity Period → set
10years (or your organisation’s standard) → Next. - Certificate Database → accept defaults → Next.
- Confirmation → Configure.
- When the wizard reports success, click Close.
Via PowerShell:
Install-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools
Install-AdcsCertificationAuthority `
-CAType EnterpriseRootCA `
-CACommonName "Contoso Issuing CA" `
-KeyLength 4096 `
-HashAlgorithmName SHA256 `
-CryptoProviderName "RSA#Microsoft Software Key Storage Provider" `
-ValidityPeriod Years `
-ValidityPeriodUnits 10 `
-Force
Verify the CA is running
Via the Windows GUI:
- Open the Certification Authority MMC console: Start → Windows
Administrative Tools → Certification Authority, or press
Win + R and run
certsrv.msc. - Expand the server node in the tree. The CA you created (e.g. Contoso Issuing CA) appears with a green tick on its icon, indicating the service is running.
- Right-click the CA node → Properties. The General tab should show the certificate as valid with the date range you specified.
Via PowerShell:
certutil -CAInfo | Select-Object -First 6
# Expected: CA type: Enterprise Root CA, CA cert: Valid
Get-ADObject -SearchBase "CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,$((Get-ADRootDSE).rootDomainNamingContext)" -Filter *
# Expected: at least one CN matching your CA common name
Create the certificate template
The bridge issues certificates from a certificate template in AD CS.
You will create a custom template by cloning the built-in Computer
template and adjusting it for device enrollment.
Pick a template name that fits your organisation; this guide uses
RaftDeviceCertificate as a placeholder. Tell your Raft tenant admin
the exact name you choose — they will need to enter it in the SCEP
profile on the Raft side.
Why a custom template
NDES will not issue from the built-in Computer template against
externally-supplied subjects. We need a template that:
- Uses schema version 2 (cloned templates must be v2).
- Has the “Supply in the request” subject flag set so the bridge can put device-specific names on each CSR.
- Has
Client Authenticationas its sole EKU (notServer Authentication, which would let issued certs be used as TLS server certificates — usually undesirable for device-identity certs). - Grants Enroll permission to the bridge service account (you will set this up in Create the bridge service identity (gMSA) — return here after creating the service account, or grant permissions in advance to a known SID).
Clone the template
Sign in to the CA host (or any host with the AD CS RSAT tools) as a Domain Admin.
Via the Windows GUI:
- Open the Certification Authority MMC: Start → Windows
Administrative Tools → Certification Authority (or run
certsrv.msc). - Expand the server node, then right-click Certificate Templates
and choose Manage. The Certificate Templates Console
(
certtmpl.msc) opens, listing every template defined in the forest. - Scroll to the Computer template, right-click it, and choose Duplicate Template. The Properties of New Template dialog opens.
- Configure the new template across these tabs:
- Compatibility tab:
- Certification Authority: Windows Server 2008 (or higher).
- Certificate recipient: Windows 7 / Server 2008 R2 (or higher).
- These selections force the template into schema version 2, which is required for cloned templates.
- General tab:
- Template display name:
Raft Device Certificate(Windows fills in Template name automatically asRaftDeviceCertificate— make sure that matches what you tell your Raft tenant admin). - Validity period / Renewal period: keep your organisation’s defaults, or adjust to suit.
- Template display name:
- Subject Name tab:
- Select Supply in the request. Click OK to dismiss the security warning. (This is the ENROLLEE_SUPPLIES_SUBJECT behaviour — the bridge supplies the device-specific subject in each CSR.)
- Extensions tab:
- Select Application Policies in the list and click Edit….
- In the Edit Application Policies Extension dialog, select Server Authentication and click Remove. Make sure Client Authentication remains. Click OK.
- Security tab — leave for now; you will add the bridge service
account here in Create the bridge service identity (gMSA)
once it exists. NDES’s own service account (
ndes_svc) and theAuthenticated Usersgroup also need Enroll here once they are created.
- Compatibility tab:
- Click OK to create the template. It now appears in the Certificate Templates Console list.
Via PowerShell:
Import-Module ActiveDirectory
$domainDN = (Get-ADRootDSE).rootDomainNamingContext
$TemplatePath = "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,$domainDN"
$NewTemplate = "RaftDeviceCertificate" # <-- change to your chosen name
$DisplayName = "Raft Device Certificate" # <-- friendly display name
# Locate the built-in Computer template
$computerTemplate = Get-ADObject -SearchBase $TemplatePath -Filter * -Properties * |
Where-Object { $_.displayName -eq 'Computer' } |
Select-Object -First 1
# Generate a fresh OID for the new template under Microsoft's ADCS enterprise arc
function New-CertTemplateOID {
$r = 1..5 | ForEach-Object { Get-Random -Minimum 1000000 -Maximum 9999999 }
return "1.3.6.1.4.1.311.21.8.$($r[0]).$($r[1]).$($r[2]).$($r[3]).$($r[4]).1"
}
$newOID = New-CertTemplateOID
$cloneAttrs = @{
'displayName' = $DisplayName
'msPKI-Cert-Template-OID' = $newOID
'msPKI-Template-Schema-Version' = 2
'msPKI-Template-Minor-Revision' = 1
# Carry forward flags; OR in CT_FLAG_IS_MODIFIED (0x10000)
'flags' = ([int]$computerTemplate.flags) -bor 0x00010000
# ENROLLEE_SUPPLIES_SUBJECT — bridge supplies the device-specific subject
'msPKI-Certificate-Name-Flag' = 1
'msPKI-Enrollment-Flag' = [int]$computerTemplate.'msPKI-Enrollment-Flag'
'msPKI-Private-Key-Flag' = [int]$computerTemplate.'msPKI-Private-Key-Flag'
'msPKI-RA-Signature' = [int]$computerTemplate.'msPKI-RA-Signature'
'msPKI-Minimal-Key-Size' = [int]$computerTemplate.'msPKI-Minimal-Key-Size'
'pKIDefaultKeySpec' = [int]$computerTemplate.pKIDefaultKeySpec
'pKIMaxIssuingDepth' = [int]$computerTemplate.pKIMaxIssuingDepth
'revision' = [int]$computerTemplate.revision
'pKIExpirationPeriod' = [byte[]]$computerTemplate.pKIExpirationPeriod
'pKIOverlapPeriod' = [byte[]]$computerTemplate.pKIOverlapPeriod
'pKIKeyUsage' = [byte[]]$computerTemplate.pKIKeyUsage
# EKU: clientAuth only (drop serverAuth)
'pKIExtendedKeyUsage' = [string[]]@("1.3.6.1.5.5.7.3.2")
'msPKI-Certificate-Application-Policy' = [string[]]@("1.3.6.1.5.5.7.3.2")
}
New-ADObject `
-Name $NewTemplate `
-Path $TemplatePath `
-Type "pKICertificateTemplate" `
-OtherAttributes $cloneAttrs
Grant Enroll permission
The bridge service account (you will create it as a gMSA in Create the bridge service identity (gMSA)) needs Enroll permission on this template. You can grant the right at any time after the account exists.
Via the Windows GUI:
- In the Certificate Templates Console (
certtmpl.msc), locate Raft Device Certificate, right-click it, and choose Properties. - Switch to the Security tab.
- Click Add….
- In the Select Users, Computers, Service Accounts, or Groups
dialog:
- Click Object Types… and tick Service Accounts if you are selecting a gMSA (then click OK).
- In Enter the object names to select, type the account name
(for example
RAFTBRIDGE$for the gMSA, orndes_svcfor the NDES service account). - Click Check Names to resolve, then OK.
- With the new principal selected in the Group or user names list, tick Allow for Read and Enroll in the permissions box below. Leave Autoenroll unticked.
- Repeat for any other principals that need Enroll on this template
(typically: the bridge gMSA and
ndes_svc). - Click OK to close the dialog.
Via PowerShell:
$templateDN = "CN=$NewTemplate,$TemplatePath"
$enrollGuid = [Guid]"0e10c968-78fb-11d2-90d4-00c04f79dc55"
function Grant-Enroll {
param([string]$DN, [System.Security.Principal.IdentityReference]$SID)
$acl = Get-Acl "AD:\$DN"
$aceEnroll = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$SID,
[System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight,
[System.Security.AccessControl.AccessControlType]::Allow,
$enrollGuid)
$aceRead = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$SID,
[System.DirectoryServices.ActiveDirectoryRights]::GenericRead,
[System.Security.AccessControl.AccessControlType]::Allow)
$acl.AddAccessRule($aceEnroll)
$acl.AddAccessRule($aceRead)
Set-Acl "AD:\$DN" $acl
}
# Grant Enroll to the bridge gMSA (replace with your gMSA's sAMAccountName + $ suffix)
Grant-Enroll -DN $templateDN -SID (Get-ADServiceAccount "RAFTBRIDGE").SID
# NDES's own service account (configured in Step 7) also needs Enroll on this template
Grant-Enroll -DN $templateDN -SID (Get-ADUser "ndes_svc").SID
Publish the template on the CA
A newly-cloned template exists in AD but is not yet issued by the CA. You must explicitly publish it before NDES can request certificates against it.
Via the Windows GUI:
- Switch back to the Certification Authority MMC (
certsrv.msc). - Expand the server node, right-click Certificate Templates, and choose New → Certificate Template to Issue.
- In the Enable Certificate Templates dialog, scroll to and select Raft Device Certificate, then click OK.
- After publishing, restart the Active Directory Certificate
Services service so the CA fully reloads the new template
definition. In the Certification Authority MMC, right-click the
server node and choose All Tasks → Stop Service, then
All Tasks → Start Service.
(An IIS reset alone is not sufficient —
CertSvcitself must restart.) - Verify the template now shows in the Certificate Templates folder under the server node.
Via PowerShell:
certutil -setcatemplates "+$NewTemplate"
# Restart of CertSvc is required after publishing a new template.
# An IIS reset alone is NOT sufficient.
Restart-Service CertSvc -Force
# Verify the template is published:
certutil -CATemplates | Select-String $NewTemplate
# Expected: a line containing your template name
Install and configure NDES
NDES (Network Device Enrollment Service) is the SCEP front-end for AD CS. It runs as an IIS application that fronts the CA on behalf of network devices that speak SCEP.
NDES requires its own dedicated service account, separate from the bridge service account. This account is the IIS application pool identity for the NDES virtual application.
Create the NDES service account
Via the Windows GUI (on a Domain Controller or a host with the RSAT AD DS tools):
- Open Active Directory Users and Computers (ADUC): Start →
Windows Administrative Tools → Active Directory Users and
Computers, or run
dsa.msc. - In the tree, navigate to the OU where you keep service accounts (or the Users container if you do not have a dedicated OU).
- Right-click the OU and choose New → User.
- In the New Object — User wizard:
- Full name and User logon name:
ndes_svc. - User logon name (pre-Windows 2000):
ndes_svc. - Click Next.
- Enter a strong Password, confirm it, and clear User must change password at next logon. Tick Password never expires. Click Next, then Finish.
- Full name and User logon name:
- Right-click the new ndes_svc account and choose Properties. On the General tab, set Description to “NDES IIS application-pool identity”. Click OK.
- Add ndes_svc to the Cert Publishers group:
- In ADUC, find the Cert Publishers group (in the Users container by default).
- Right-click it → Properties → Members tab → Add….
- Type
ndes_svc, click Check Names, OK, OK.
Add the account to the local IIS_IUSRS group on the NDES host:
- Sign in to the NDES host as a local administrator.
- Open Computer Management (
compmgmt.msc). - Expand System Tools → Local Users and Groups → Groups.
- Double-click IIS_IUSRS, click Add…, type
<NETBIOS>\ndes_svc, Check Names, OK, OK.
Register the Service Principal Names (SPNs) so NDES can accept
Negotiate / NTLM authentication on mscep_admin. SPNs do not have a
first-class GUI in standard Windows tooling; Microsoft expects you to
use setspn.exe from a command prompt, or to edit the
servicePrincipalName attribute directly in ADSI Edit
(adsiedit.msc → connect to Default naming context → locate
ndes_svc → Properties → Attribute Editor tab →
servicePrincipalName → Edit). The PowerShell / command-line
approach below is the simpler path.
Via PowerShell:
Import-Module ActiveDirectory
$ndesPass = Read-Host -AsSecureString -Prompt "Password for ndes_svc"
New-ADUser `
-SamAccountName "ndes_svc" `
-Name "ndes_svc" `
-UserPrincipalName "ndes_svc@$((Get-ADDomain).DNSRoot)" `
-AccountPassword $ndesPass `
-Enabled $true `
-PasswordNeverExpires $true `
-Description "NDES IIS application-pool identity"
Add-ADGroupMember -Identity "Cert Publishers" -Members "ndes_svc"
# On the NDES host: add ndes_svc to the local IIS_IUSRS group
& net localgroup IIS_IUSRS "$((Get-ADDomain).NetBIOSName)\ndes_svc" /add
# Register SPNs so NDES can accept Negotiate / NTLM auth on mscep_admin
$ndesHost = "<ndes-hostname>" # e.g. ca01
$ndesFqdn = "<ndes-hostname>.<domain>" # e.g. ca01.contoso.local
setspn -S "http/$ndesFqdn" "$((Get-ADDomain).NetBIOSName)\ndes_svc"
setspn -S "http/$ndesHost" "$((Get-ADDomain).NetBIOSName)\ndes_svc"
Install NDES
Sign in to the host that will run NDES (a domain-joined member server, which may or may not also be the CA) as a Domain Admin.
Via the Windows GUI:
- Open Server Manager.
- Manage → Add Roles and Features.
- Step through the wizard:
- Before You Begin → Next.
- Installation Type → Role-based or feature-based → Next.
- Server Selection → Next.
- Server Roles → expand Active Directory Certificate Services and tick Network Device Enrollment Service. When prompted, click Add Features to include the IIS prerequisites and management tools. → Next.
- Features → Next.
- Web Server Role (IIS) information page → Next.
- Role Services (IIS) — accept the recommended defaults pre-selected by NDES dependencies. → Next.
- Confirmation → Install.
- Wait for installation to finish, then click the yellow notifications flag in the top-right corner of Server Manager and click Configure Active Directory Certificate Services on the destination server.
- The AD CS Configuration wizard opens.
- Credentials — Domain Admin → Next.
- Role Services → tick Network Device Enrollment Service → Next.
- Service Account for NDES → select Specify service account
(recommended), click Select…, enter
<NETBIOS>\ndes_svc, enter the password, and click OK. → Next. - CA for NDES:
- If NDES and CA are on the same host: select CA name and pick your CA from the dropdown.
- If NDES and CA are on different hosts: select CA name and click Select…, then choose the remote CA.
- → Next.
- RA Information — fill in registration authority details:
- RA name:
Contoso-NDES-RA - Country/region:
US - State/province:
Texas - City/locality:
Austin - → Next.
- RA name:
- Cryptography for NDES:
- Signature key provider:
Microsoft Strong Cryptographic Provider, key length2048. - Encryption key provider:
Microsoft Strong Cryptographic Provider, key length2048. - → Next.
- Signature key provider:
- Confirmation → Configure.
- When the wizard reports success, click Close.
Via PowerShell:
# Install NDES + IIS prerequisites
Install-WindowsFeature -Name ADCS-Device-Enrollment -IncludeManagementTools
Install-WindowsFeature -Name `
Web-Server, Web-Filtering, Web-ASP-Net45, Web-Net-Ext45, `
Web-ISAPI-Ext, Web-ISAPI-Filter, Web-WMI, `
RSAT-ADCS, RSAT-ADCS-Mgmt, Web-Metabase `
-IncludeManagementTools
# Configure NDES
$ndesCred = Get-Credential -UserName "<NETBIOS>\ndes_svc" `
-Message "Enter the ndes_svc password set earlier"
Install-AdcsNetworkDeviceEnrollmentService `
-ServiceAccountName "<NETBIOS>\ndes_svc" `
-ServiceAccountPassword $ndesCred.Password `
-RAName "Contoso-NDES-RA" `
-RACountry "US" `
-RAState "Texas" `
-RACity "Austin" `
-SigningProviderName "Microsoft Strong Cryptographic Provider" `
-SigningKeyLength 2048 `
-EncryptionProviderName "Microsoft Strong Cryptographic Provider" `
-EncryptionKeyLength 2048 `
-Force
NDES’s RA certificates require Enroll permission on the
CEPEncryption and EnrollmentAgentOffline built-in templates.
Via the Windows GUI:
- Open the Certificate Templates Console (
certtmpl.msc) — or right-click Certificate Templates in the Certification Authority MMC and choose Manage. - Right-click CEPEncryption → Properties → Security tab.
- Click Add…, type
ndes_svc, Check Names, OK. - With ndes_svc selected, tick Allow for Read and Enroll. Click OK.
- Repeat steps 2-4 for EnrollmentAgentOffline.
Via PowerShell:
$ConfigContext = ([ADSI]"LDAP://RootDSE").ConfigurationNamingContext
$TemplatePath = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext"
foreach ($tmplName in @("CEPEncryption", "EnrollmentAgentOffline")) {
$tmpl = Get-ADObject -SearchBase $TemplatePath -Filter "cn -eq '$tmplName'"
Grant-Enroll -DN $tmpl.DistinguishedName -SID (Get-ADUser "ndes_svc").SID
}
(The Grant-Enroll function is defined in Create the certificate template.)
Configure NDES registry settings
NDES reads its operating configuration from
HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP. The following settings
align NDES with the bridge’s expectations.
Via the Windows GUI:
-
Open Registry Editor: press Win + R, type
regedit, and click OK. Click Yes at the UAC prompt. -
Navigate to
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MSCEP. -
The three template values (
SignatureTemplate,EncryptionTemplate,GeneralPurposeTemplate) already exist asREG_SZ(string) values. For each one in turn:- Double-click the value.
- In Value data, replace the contents with
RaftDeviceCertificate(or your chosen template name from Create the certificate template). - Click OK.
-
Set the four challenge-password DWORD values. For each one, look for it under the MSCEP key. If the value exists, double-click and set its Value data; if it does not, right-click the empty space on the right pane → New → DWORD (32-bit) Value, type the name, and then double-click to set the value. Make sure Base is set to Decimal before entering numeric values.
Name Value Notes EnforcePassword1Always 1; keep enabled. UseSinglePassword0NDES generates a fresh OTP per request. PasswordMax999Maximum cached challenge passwords. PasswordValidity14400Minutes; equals 10 days. -
Close Registry Editor.
Restart so the IIS worker process re-reads the registry:
- Open the Certification Authority MMC, right-click the server node, choose All Tasks → Stop Service, then All Tasks → Start Service.
- Open IIS Manager (
inetmgr), select the server node in the left pane, and in the Actions pane on the right click Stop, then Start. (This is the GUI equivalent ofiisreset.)
Via PowerShell:
$regPath = "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP"
# Point all three template slots at your new template
Set-ItemProperty -Path $regPath -Name "SignatureTemplate" -Value "RaftDeviceCertificate"
Set-ItemProperty -Path $regPath -Name "EncryptionTemplate" -Value "RaftDeviceCertificate"
Set-ItemProperty -Path $regPath -Name "GeneralPurposeTemplate" -Value "RaftDeviceCertificate"
# Challenge-password behaviour
Set-ItemProperty -Path $regPath -Name "EnforcePassword" -Value 1 -Type DWord
Set-ItemProperty -Path $regPath -Name "UseSinglePassword" -Value 0 -Type DWord
Set-ItemProperty -Path $regPath -Name "PasswordMax" -Value 999 -Type DWord
Set-ItemProperty -Path $regPath -Name "PasswordValidity" -Value 14400 -Type DWord # 10 days in minutes
# Restart so the IIS worker process re-reads the registry
Restart-Service CertSvc -Force
iisreset /restart
Settings explained:
| Value name | Meaning |
|---|---|
SignatureTemplate |
Template NDES uses for signature-only CSRs. Set to your custom template name. |
EncryptionTemplate |
Template NDES uses for encryption-only CSRs. Set to your custom template name. |
GeneralPurposeTemplate |
Template NDES uses for general-purpose CSRs. Set to your custom template name. |
EnforcePassword |
1 = NDES requires a valid challenge password on every CSR. Keep this at 1. Setting to 0 disables challenge-password auth. |
UseSinglePassword |
0 = NDES generates a fresh one-time challenge per mscep_admin request. This is the recommended setting for production. |
PasswordMax |
Maximum number of unused challenge passwords NDES caches. The default (5) is too low for any non-trivial workload; raise it. |
PasswordValidity |
Validity window of an issued challenge, in minutes. 14400 = 10 days; the default of 60 minutes is too short for batch enrolment. |
Verify NDES is responding
Via the Windows GUI (in a browser on the NDES host):
-
Open Microsoft Edge (or any browser).
-
Navigate to
http://localhost/CertSrv/mscep/mscep.dll/pkiclient.exe?operation=GetCACaps&message=raft. -
The page should display a short plain-text list of capabilities, for example:
POSTPKIOperation Renewal SHA-512 SHA-256 SHA-1 DES3 -
To check the SCEP enrolment endpoint itself, navigate to
http://localhost/CertSrv/mscep/mscep.dll. You should see the built-in NDES landing page (“Network Device Enrollment Service”) describing how to obtain a challenge password.
If either URL returns an HTTP error page instead, confirm that the Default Web Site and the SCEP application pool are running in IIS Manager: in the left pane select Application Pools, find SCEP in the list, and check the Status column reads Started.
Via PowerShell:
# GetCACert returns a DER-encoded PKCS#7 containing the RA and CA certs
$base = "http://localhost/CertSrv/mscep/mscep.dll/pkiclient.exe"
$resp = Invoke-WebRequest -Uri "$base?operation=GetCACert&message=raft" -UseBasicParsing
"Status: $($resp.StatusCode), Bytes: $($resp.Content.Length)"
# GetCACaps lists supported SCEP capabilities
(Invoke-WebRequest -Uri "$base?operation=GetCACaps&message=raft" -UseBasicParsing).Content
# Expected lines: POSTPKIOperation, Renewal, SHA-512, SHA-256, SHA-1, DES3
Create the bridge service identity (gMSA)
A Group Managed Service Account (gMSA) is the recommended identity
for the bridge service. Active Directory manages the gMSA password
automatically, rotating it every 30 days by default. No plaintext
password is ever stored on the bridge host, in config.toml, or in
any script.
Pre-flight: KDS Root Key
A gMSA requires that your forest has a KDS Root Key. Most forests created after Windows Server 2012 already have one; check before creating:
Via PowerShell:
Get-KdsRootKey
If the result is empty, create one. In production, you would typically let it become effective after 10 hours of forest-wide replication; in a lab you can force it effective immediately:
# Production: defer activation so it replicates first
Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))
# Lab / single-DC environment only:
Add-KdsRootKey -EffectiveImmediately
Create the gMSA
Run on a Domain Controller (or any host with the AD PowerShell module and Domain Admin rights). No GUI equivalent exists in shipping Windows tooling.
Via PowerShell:
New-ADServiceAccount `
-Name "RAFTBRIDGE" `
-DNSHostName "raftbridge.<domain>" `
-PrincipalsAllowedToRetrieveManagedPassword "<bridge-host>$"
<bridge-host> is the short hostname (sAMAccountName) of the Windows
host where the bridge service will run. The $ suffix denotes the
computer account in AD.
If you want to allow multiple bridge hosts to use the same gMSA (for example, an active/passive pair), pass a comma-separated list:
-PrincipalsAllowedToRetrieveManagedPassword "<bridge-host-1>$","<bridge-host-2>$"
Install the gMSA on the bridge host
On the bridge host, sign in as a local administrator. As above, no GUI tool exists for this — use PowerShell.
Via PowerShell:
# Install the RSAT-AD-PowerShell module if it isn't already present
Install-WindowsFeature RSAT-AD-PowerShell
Install-AdServiceAccount -Identity "RAFTBRIDGE"
Test-AdServiceAccount -Identity "RAFTBRIDGE"
# Expected: True
Test-AdServiceAccount returning True confirms the bridge host can
retrieve the gMSA password from AD and will be able to log on as the
gMSA. If it returns False, double-check:
- The bridge host is joined to the same forest as the gMSA.
- The bridge host appears in
(Get-ADServiceAccount RAFTBRIDGE -Properties PrincipalsAllowedToRetrieveManagedPassword).PrincipalsAllowedToRetrieveManagedPassword. - The bridge host has rebooted at least once since the gMSA was created (Kerberos ticket cache).
Grant Enroll on the certificate template
Return to the Grant Enroll permission step
and run it now if you have not already done so. The bridge gMSA needs
Enroll permission on your RaftDeviceCertificate template; without
it, NDES will reject CSRs with a permissions error.
Fallback: domain user account
If your environment cannot use gMSAs (for example, the AD functional level predates Server 2012, or organisational policy forbids them), use a regular domain user account instead.
Via the Windows GUI (Active Directory Users and Computers):
- Open ADUC (
dsa.msc) on a Domain Controller or a host with the RSAT AD DS tools. - Navigate to the OU where you keep service accounts.
- Right-click the OU → New → User.
- Full name and User logon name:
bridge_svc. → Next. - Enter a strong Password, confirm it, clear User must change password at next logon, tick Password never expires. → Next → Finish.
- Right-click bridge_svc → Properties → General tab → set Description to “Raft SCEP bridge service identity”. → OK.
Via PowerShell:
$bridgePass = Read-Host -AsSecureString -Prompt "Password for bridge service account"
New-ADUser `
-SamAccountName "bridge_svc" `
-Name "bridge_svc" `
-UserPrincipalName "bridge_svc@<domain>" `
-AccountPassword $bridgePass `
-Enabled $true `
-PasswordNeverExpires $true `
-Description "Raft SCEP bridge service identity"
You can grant Enroll on the template using the same Grant-Enroll
helper as in Create the certificate template,
passing (Get-ADUser bridge_svc).SID.
Operational caveats for the domain-user fallback:
- The password is stored in the local Service Control Manager’s secret store on the bridge host. It is encrypted with the host’s machine key but is recoverable by local administrators.
- Password rotation requires re-running
sc.exe configon the bridge host (see Switch the bridge service to run as the gMSA) whenever you change the AD password. - Use a dedicated account for this purpose only — do not reuse a shared admin account.
Grant the bridge access to mscep_admin
NDES’s /CertSrv/mscep_admin/ virtual directory is the endpoint the
bridge calls to fetch one-time challenge passwords. It enforces
Windows Authentication (Negotiate / NTLM) by default, and by
default it only authorises members of the local Administrators group
on the NDES host.
Do not add the bridge gMSA to local Administrators on the NDES
host. That is wider access than the bridge needs and would let any
compromise of the gMSA tamper with the host’s services, registry,
and filesystem. Instead, add a scoped IIS authorisation rule that
grants access only to the mscep_admin virtual directory.
Create an AD group for bridge-token holders
Via the Windows GUI:
- On a Domain Controller (or any host with RSAT AD DS), open
Active Directory Users and Computers (
dsa.msc). - Navigate to the OU where you keep service-account groups.
- Right-click the OU → New → Group.
- In the New Object — Group dialog:
- Group name:
RAFT-NDES-Bridges - Group scope: Global
- Group type: Security
- Click OK.
- Group name:
- Right-click the new RAFT-NDES-Bridges group → Properties → General tab → set Description to “Service accounts permitted to fetch challenge passwords from NDES mscep_admin”. → Apply.
- Switch to the Members tab → Add….
- In the Select Users, Contacts, Computers, Service Accounts, or
Groups dialog:
- Click Object Types… and tick Service Accounts (for a gMSA) or Users (for a domain user). → OK.
- In Enter the object names to select, type
RAFTBRIDGE$for the gMSA (note the trailing$) orbridge_svcfor a domain user. Click Check Names, then OK.
- Click OK to close the group properties.
Via PowerShell:
New-ADGroup `
-Name "RAFT-NDES-Bridges" `
-GroupScope Global `
-GroupCategory Security `
-Path "OU=Service Accounts,DC=...,DC=..." `
-Description "Service accounts permitted to fetch challenge passwords from NDES mscep_admin"
# Add the bridge gMSA
Add-ADGroupMember -Identity "RAFT-NDES-Bridges" -Members "RAFTBRIDGE$"
Using an AD group (rather than naming each service account individually) means you can authorise additional bridge hosts later without changing IIS configuration.
Apply the scoped IIS authorisation rule
Sign in to the NDES host as a local administrator.
Via the Windows GUI:
- Open Internet Information Services (IIS) Manager: Start →
Windows Administrative Tools → Internet Information Services (IIS)
Manager, or run
inetmgr. - In the Connections pane on the left, expand the server node, then expand Sites → Default Web Site → CertSrv, and select the mscep_admin application.
- In the centre Features pane, double-click Authorization Rules.
- In the Actions pane on the right, click Add Allow Rule….
- In the Add Allow Authorization Rule dialog:
- Select Specified roles or user groups.
- In the text box, enter
<NETBIOS>\RAFT-NDES-Bridges(where<NETBIOS>is your domain NetBIOS name, e.g.CONTOSO). - Leave the Verbs field empty (which means “all verbs”).
- Click OK.
- The new rule should appear in the rule list with Mode = Allow
and Users/Roles =
<NETBIOS>\RAFT-NDES-Bridges.
Restart IIS so the worker process picks up the new authorisation rules:
- In IIS Manager, select the server node at the top of the Connections pane.
- In the Actions pane on the right, click Stop, then Start.
Via PowerShell:
Import-Module WebAdministration
$site = "Default Web Site/CertSrv/mscep_admin"
# Add an explicit <allow> rule for the AD group
Add-WebConfigurationProperty `
-PSPath "MACHINE/WEBROOT/APPHOST" `
-Location $site `
-Filter "system.webServer/security/authorization" `
-Name "." `
-Value @{accessType="Allow"; roles="<NETBIOS>\RAFT-NDES-Bridges"; verbs=""}
# (Optional) review the resulting authorization config:
Get-WebConfiguration `
-PSPath "MACHINE/WEBROOT/APPHOST" `
-Location $site `
-Filter "system.webServer/security/authorization" |
Select-Object -ExpandProperty Collection
# Restart IIS so the worker process picks up the new authz rules
iisreset /restart
Optional sanity check from the bridge host once everything is wired up (this will succeed only after the bridge service identity is set in Switch the bridge service to run as the gMSA and the bridge gMSA has Kerberos tickets).
Via the Windows GUI:
- On the bridge host, open Microsoft Edge.
- Navigate to
http://<ndes-host>/CertSrv/mscep_admin/. - The browser may prompt for Windows credentials — accept the default
if your interactive logon happens to be authorised on
mscep_admin, or cancel out (this URL is normally only consumed by the service). - If the page loads with a “Network Device Enrollment Service” body
containing a bolded challenge password, the URL and basic
authorisation are working. (Whether the bridge service can
reach it as its gMSA identity is a separate question, validated in
Start the service and verify via
test-connection.)
Via PowerShell:
Invoke-WebRequest `
-Uri "http://<ndes-host>/CertSrv/mscep_admin/" `
-UseDefaultCredentials -UseBasicParsing
# Expected: HTTP 200, HTML body.
Install the bridge — Path A: EXE bundle (recommended)
Two installer artifacts are shipped with every Raft SCEP Bridge release:
| Artifact | Best for |
|---|---|
raft-scep-bridge-<version>-x64.exe |
Standard Windows Server hosts. Wraps the MSI in a bootstrapper that also installs the Microsoft VC++ Redistributable if missing. |
raft-scep-bridge-<version>-x64.msi |
Environments that deploy MSIs via SCCM, Intune, or another endpoint-management tool that handles VC++ Redistributable centrally. |
If you have no preference, use the EXE bundle. It is self-contained and works on a fresh Windows Server install.
Install the EXE bundle
-
Copy
raft-scep-bridge-<version>-x64.exeto the bridge host (for example, toC:\Temp\).Via the Windows GUI: open File Explorer, navigate to the removable drive or download folder containing the installer, and copy / paste it to
C:\Temp\on the bridge host. -
Run the installer as Administrator.
Via the Windows GUI (interactive install with a wizard UI):
- In File Explorer, right-click
raft-scep-bridge-<version>-x64.exe→ Run as administrator. Click Yes at the UAC prompt. - The Raft SCEP Bridge Setup bootstrapper window opens.
- Tick the I agree to the license terms and conditions box (a “license” link beside it opens the AGPL-3.0 text).
- Click Install.
- The bootstrapper displays a single combined progress bar. Behind it, two packages run in sequence: the Visual C++ 2015-2022 Redistributable (x64) (skipped if it is already present on the host) followed by the inner Raft SCEP Bridge MSI.
- When the install completes and the success message appears, click Close.
Via PowerShell (unattended install):
Start-Process -FilePath "C:\Temp\raft-scep-bridge-<version>-x64.exe" ` -ArgumentList "/quiet","/log","$env:TEMP\raft-bridge-install.log" ` -Wait -NoNewWindow - In File Explorer, right-click
-
The bootstrapper does two things:
- Checks
HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64\Installed. If the VC++ 2015-2022 x64 redistributable is not already present, it installs the embedded copy (/install /quiet /norestart). - Runs the inner Raft SCEP Bridge MSI, which:
- Installs
raft-scep-bridge.exetoC:\Program Files\Raft\Bridge\. - Registers a Windows service named
RaftSCEPBridgewith the display name “Raft SCEP Bridge”, set to Automatic start and configured to run asLocalSystem(you will change this in Switch the bridge service to run as the gMSA). - Registers a classic Event Log source stub named
Raft-SCEP-Bridgeunder the Windows Application log (the runtime uses an ETW provider of the same name; see Check the bridge’s ETW events).
- Installs
- Checks
-
Verify the install.
Via the Windows GUI:
- Open Services (
services.msc). - Scroll the list to Raft SCEP Bridge. The columns should
read: Status = (blank, i.e. Stopped); Startup Type =
Automatic; Log On As = Local System. The service is
intentionally not started yet — it needs
config.tomlfirst (Createconfig.toml). - Open File Explorer and navigate to
C:\Program Files\Raft\Bridge\. The fileraft-scep-bridge.exeshould be present. - Open Settings → Apps → Installed apps (or Control Panel →
Programs and Features,
appwiz.cpl). Raft SCEP Bridge should appear in the list with the version you installed.
Via PowerShell:
Get-Service RaftSCEPBridge | Format-Table Name, DisplayName, StartType, Status # Expected: StartType=Automatic, Status=Stopped (it will start # after you drop config.toml — see Create `config.toml` below). Test-Path "C:\Program Files\Raft\Bridge\raft-scep-bridge.exe" # Expected: True - Open Services (
-
Note that the bridge service is deliberately not started by the installer. The service requires
config.tomlto exist before it will run; the next sections walk you through creating it.
The bundle’s UpgradeCode is stable across versions: when you later install a newer EXE bundle on top of the current one, Windows Installer replaces the existing install in place and preserves your configuration. See Upgrades.
SmartScreen warning on first install
The installer is not currently signed with an Authenticode certificate. Windows SmartScreen may display a warning the first time the EXE is run on a host.
Via the Windows GUI:
- When SmartScreen displays the Windows protected your PC dialog, click the More info link at the top of the message.
- The dialog expands to show App:
raft-scep-bridge-<version>-x64.exeand a Run anyway button. - Click Run anyway to continue with the install.
To pre-trust the file so SmartScreen does not interrupt the install:
- In File Explorer, right-click
raft-scep-bridge-<version>-x64.exe→ Properties. - On the General tab, near the bottom, look for a Security section with text like “This file came from another computer and might be blocked to help protect this computer.”
- Tick the Unblock checkbox next to it.
- Click OK.
Via PowerShell:
Unblock-File -Path "C:\Temp\raft-scep-bridge-<version>-x64.exe"
If SmartScreen is enforced via Group Policy or Defender for Endpoint, you may need to add an exclusion through your endpoint-management tool.
Install the bridge — Path B: standalone MSI
Use this path if your organisation deploys MSI packages via SCCM, Intune, or a similar tool that prefers raw MSIs.
-
Pre-install the Microsoft Visual C++ Redistributable for Visual Studio 2015-2022 (x64) if it is not already present on the host.
Via the Windows GUI:
- In a browser on the bridge host, navigate to
https://aka.ms/vs/17/release/vc_redist.x64.exe. Thevc_redist.x64.exeinstaller downloads. - In File Explorer, right-click the downloaded
vc_redist.x64.exe→ Run as administrator. - Tick the licence-agreement box and click Install.
- Click Close when the installer reports success.
Alternatively, deploy the redistributable through your endpoint-management tool (SCCM / Intune / similar).
Via PowerShell:
# vc_redist.x64.exe must be present on the host first. Start-Process -FilePath "C:\Temp\vc_redist.x64.exe" ` -ArgumentList "/install","/quiet","/norestart" ` -Wait -NoNewWindow - In a browser on the bridge host, navigate to
-
Copy
raft-scep-bridge-<version>-x64.msito the bridge host (for example, toC:\Temp\). -
Install the MSI.
Via the Windows GUI (interactive install):
- In File Explorer, right-click
raft-scep-bridge-<version>-x64.msi→ Install (or double-click the MSI; for plain Install you may not see a UAC prompt — Windows Installer elevates automatically for per-machine MSIs). - The Raft SCEP Bridge Setup wizard opens.
- On the Welcome page, click Next.
- On the End-User License Agreement page, tick I accept the terms in the License Agreement and click Next.
- Optionally adjust the install location on the Destination
Folder page (default:
C:\Program Files\Raft\Bridge\) and click Next. - Click Install.
- When the installer completes, click Finish.
Via PowerShell (unattended install):
$msi = "C:\Temp\raft-scep-bridge-<version>-x64.msi" Start-Process -FilePath "msiexec.exe" ` -ArgumentList "/i","`"$msi`"","/qn","/l*v","$env:TEMP\raft-bridge-install.log" ` -Wait -NoNewWindow - In File Explorer, right-click
-
The verification steps from Path A (Services snap-in / File Explorer / Apps list) apply identically.
Switch the bridge service to run as the gMSA
Both installer paths configure the RaftSCEPBridge service to run as
LocalSystem by default, which cannot authenticate to mscep_admin
in OTP mode. Reconfigure the service to run as the gMSA from
Create the bridge service identity (gMSA).
Via the Windows GUI:
- Open Services (
services.msc). - Scroll to Raft SCEP Bridge, right-click it, and choose Properties.
- Switch to the Log On tab.
- Select This account, then click Browse….
- In the Select User dialog:
- Click Object Types… and tick Service Accounts, then OK.
- Click Locations…, choose your AD domain (not the local machine), then OK.
- In Enter the object name to select, type
RAFTBRIDGE$(note the trailing$— that is how AD denotes the managed-service account computer principal). - Click Check Names. The dialog should resolve the entry to
RAFTBRIDGE (Windows hides the
$once resolved). - Click OK.
- Back on the Log On tab, the This account field should now
read
<NETBIOS>\RAFTBRIDGE$. - Leave both the Password and Confirm password boxes empty. This is the documented signal to the Service Control Manager that this is a managed service account whose password lives in AD.
- Click OK. Windows displays a message confirming that the account has been granted the Log on as a service right.
- Right-click Raft SCEP Bridge in the Services list and choose Restart (if it was running) or Start (if it was stopped).
To verify the change took effect, look at the Log On As column in the Services snap-in, or open the service’s properties and confirm the Log On tab still shows the gMSA.
Via PowerShell:
# Note the trailing $ on the gMSA name and the deliberately empty password.
# An empty password tells the Service Control Manager to fetch the
# AD-managed password automatically (and to keep refreshing it on rotation).
sc.exe config RaftSCEPBridge `
obj= "<NETBIOS>\RAFTBRIDGE$" `
password= ""
# Verify the change took effect:
Get-WmiObject Win32_Service -Filter "Name='RaftSCEPBridge'" |
Select-Object Name, StartName, State
# Expected StartName: <NETBIOS>\RAFTBRIDGE$
Either path automatically grants the Log on as a service
privilege (SeServiceLogonRight) to the new identity, so no
additional Local Security Policy step is needed.
Fallback: domain user identity
If you are using a domain user account instead of a gMSA, supply the password explicitly.
Via the Windows GUI:
- Open Services (
services.msc), right-click Raft SCEP Bridge → Properties → Log On tab. - Select This account, click Browse…, leave Object Types
at the default (Users), enter
bridge_svc, click Check Names, then OK. - In Password and Confirm password, enter the AD password for the account. Click OK.
- Acknowledge the Log on as a service confirmation message.
- Right-click Raft SCEP Bridge → Restart.
Via PowerShell:
sc.exe config RaftSCEPBridge `
obj= "<NETBIOS>\bridge_svc" `
password= "<password>"
Store the password in a password manager. Whenever you rotate it in
AD, re-open the service’s Log On tab (or re-run sc.exe config)
to update the local SCM secret store.
Create config.toml
The bridge reads its configuration from
C:\ProgramData\RaftBridge\config.toml. The installer does not
create this file — that way, an admin-managed configuration is never
overwritten by an install or upgrade.
Create the directory and the file.
Via the Windows GUI:
- Open File Explorer. In the address bar, type
C:\ProgramDataand press Enter. (TheProgramDatafolder is hidden by default; typing the path directly works regardless of the Hidden items view setting.) - If a folder named RaftBridge does not already exist, right-click
the empty file-list area → New → Folder, name it
RaftBridge, and press Enter. - Open the RaftBridge folder. Right-click the empty area → New → Text Document.
- Windows creates New Text Document.txt. Rename it to
config.toml(delete the.txtextension). If File Explorer warns about changing the file extension, click Yes. - Right-click config.toml → Open with → Notepad (or your preferred editor). Leave the editor open — you will paste a configuration template into it in the next step.
Via PowerShell:
New-Item -ItemType Directory -Force -Path "C:\ProgramData\RaftBridge" | Out-Null
New-Item -ItemType File -Force -Path "C:\ProgramData\RaftBridge\config.toml" | Out-Null
notepad "C:\ProgramData\RaftBridge\config.toml"
Use one of the two templates below. OTP mode is the recommended production configuration because it does not store the NDES challenge password on the bridge host.
Recommended: OTP mode
In OTP mode, the bridge fetches a fresh one-time challenge password
from mscep_admin for every CSR, authenticating as its service
identity (the gMSA from Create the bridge service identity (gMSA))
via Kerberos/NTLM.
Save the following as C:\ProgramData\RaftBridge\config.toml:
# Raft SCEP Bridge configuration -- OTP mode (recommended).
# The bridge fetches a fresh one-time challenge password from NDES per
# CSR; no challenge password is stored in this file.
[server]
# Your Raft tenant URL (HTTPS).
api_url = "https://raft.example.com"
# Bridge token obtained from your Raft tenant admin.
# A 64-character hexadecimal string.
api_key = "REPLACE-WITH-YOUR-64-CHAR-BRIDGE-TOKEN"
[ndes]
# SCEP enrollment endpoint -- the mscep.dll URL on the NDES host.
# Use http:// or https:// depending on your NDES IIS bindings.
# If the bridge runs on the SAME host as NDES, use localhost.
# If the bridge runs on a SEPARATE host, use the NDES host's FQDN.
url = "http://ndes.example.local/CertSrv/mscep/mscep.dll"
[challenge]
mode = "otp"
# Challenge-password fetch endpoint. Must point at the
# /CertSrv/mscep_admin/ virtual directory on the NDES host.
# The bridge authenticates here with Windows Integrated Auth as the
# service identity, so its credentials are never stored in this file.
otp_url = "http://ndes.example.local/CertSrv/mscep_admin/"
[bridge]
# How often the bridge polls the Raft tenant for pending requests.
# 30 seconds is the default; lower values trade off Raft API load
# against enrolment latency.
poll_interval_secs = 30
# RSA-2048 or RSA-4096. RSA-2048 is the default.
key_algorithm = "RSA-2048"
[logging]
# Directory for daily-rolling log files (bridge.log).
# Default if omitted: C:\ProgramData\RaftBridge\logs
file_log_dir = "C:\\ProgramData\\RaftBridge\\logs"
# Log level: trace, debug, info, warn, error.
file_log_level = "info"
Alternative: static mode
In static mode, you (or an automation tool) fetch a challenge password
from mscep_admin once at install time and embed it in config.toml.
The bridge sends the same cached password on every CSR.
Static mode is simpler to bring up — the bridge service does not need
to authenticate to mscep_admin itself — but the cached password sits
in config.toml until you rotate it. Consider it for proof-of-concept
deployments or when your environment cannot wire up
Windows Authentication on mscep_admin.
To fetch a challenge password manually, run this on the NDES host as
a user authorised on mscep_admin (for example, a local
administrator).
Via the Windows GUI:
- Sign in to the NDES host as a user that is a member of the
RAFT-NDES-Bridgesgroup from Grant the bridge access tomscep_admin, or local Administrators (which is the NDES default). - Open Microsoft Edge.
- Navigate to
http://localhost/CertSrv/mscep_admin/. - If Edge prompts for credentials, leave Use my current Windows account ticked (or supply the credentials of an authorised account) and click OK.
- The NDES landing page displays a heading “Network Device Enrollment Service” followed by an Enrollment challenge password in bold. The password is a long uppercase-hex string.
- Triple-click the password to select it, then Ctrl + C to copy.
You will paste it into
config.tomlin the next step.
Via PowerShell:
$url = "http://localhost/CertSrv/mscep_admin/"
$resp = Invoke-WebRequest -Uri $url -UseDefaultCredentials -UseBasicParsing
# mscep_admin's response body is UTF-16 LE despite advertising UTF-8.
# Strip the high bytes to read the page reliably:
$rawBytes = [System.Text.Encoding]::Default.GetBytes($resp.Content)
$text = [System.Text.Encoding]::ASCII.GetString($rawBytes) -replace '[^\x20-\x7E]', ''
if ($text -match '<B>\s*([0-9A-F]{6,})\s*</B>') {
Write-Host "Challenge password: $($Matches[1])"
} else {
Write-Host "No challenge password found in the response. Raw text:"
$text
}
Save the following as C:\ProgramData\RaftBridge\config.toml, with
<challenge-password> replaced by the value printed above:
# Raft SCEP Bridge configuration -- static mode.
# A challenge password fetched from NDES once at install time is
# embedded below and reused on every CSR.
[server]
api_url = "https://raft.example.com"
api_key = "REPLACE-WITH-YOUR-64-CHAR-BRIDGE-TOKEN"
[ndes]
url = "http://ndes.example.local/CertSrv/mscep/mscep.dll"
[challenge]
mode = "static"
password = "<challenge-password>"
[bridge]
poll_interval_secs = 30
key_algorithm = "RSA-2048"
[logging]
file_log_dir = "C:\\ProgramData\\RaftBridge\\logs"
file_log_level = "info"
Static-mode challenge passwords are valid for the duration set in
NDES’s PasswordValidity registry value
(Install and configure NDES; 10 days at the
recommended setting). If the password expires you will need to fetch
a new one and update config.toml. OTP mode avoids this maintenance
loop entirely.
Securing config.toml
The default ACL on C:\ProgramData\RaftBridge already restricts read
access to Administrators and SYSTEM. You can verify and tighten it.
Via the Windows GUI:
- In File Explorer, navigate to
C:\ProgramData\RaftBridge\. - Right-click config.toml → Properties.
- Switch to the Security tab.
- Confirm the Group or user names list shows only SYSTEM,
Administrators, and (typically) Users. To tighten further:
- Click Advanced.
- Click Disable inheritance → Convert inherited permissions into explicit permissions on this object.
- Back on the main Security tab, select Users and click Edit… → Remove.
- Click OK on each dialog until you return to the file properties.
- Verify that SYSTEM and Administrators retain Read and Write (the bridge service inherits SYSTEM-level access via the computer account; configuration edits happen as an Administrator).
Via PowerShell:
Get-Acl "C:\ProgramData\RaftBridge\config.toml" | Format-List
# Optional: explicitly deny non-administrators
icacls "C:\ProgramData\RaftBridge\config.toml" `
/inheritance:r `
/grant:r "Administrators:(R,W)" `
/grant:r "SYSTEM:(R,W)"
Environment-variable overrides
The following environment variables (machine-scope) override their
counterparts in config.toml at service startup. They are useful for
keeping credentials out of files entirely, or for differentiating
config-version-controlled defaults from per-host secrets:
| Variable | Overrides |
|---|---|
RAFT_BRIDGE_API_URL |
[server].api_url |
RAFT_BRIDGE_API_KEY |
[server].api_key |
RAFT_BRIDGE_NDES_URL |
[ndes].url |
RAFT_BRIDGE_POLL_INTERVAL |
[bridge].poll_interval_secs |
Example: keep the bridge token out of config.toml entirely.
Via the Windows GUI:
- Right-click Start → System, or run
sysdm.cpl. - In the About page, click Advanced system settings.
- In the System Properties dialog, on the Advanced tab, click Environment Variables….
- In the System variables section (the lower of the two lists), click New….
- Set:
- Variable name:
RAFT_BRIDGE_API_KEY - Variable value: paste your bridge token
- Click OK.
- Variable name:
- Click OK twice more to close the Environment Variables and System Properties dialogs.
- Open Services (
services.msc), right-click Raft SCEP Bridge → Restart so the service inherits the new environment.
Via PowerShell:
# Set machine-wide so the service inherits it
[Environment]::SetEnvironmentVariable("RAFT_BRIDGE_API_KEY", "<token>", "Machine")
# Restart the service so it picks up the new environment
Restart-Service RaftSCEPBridge
The api_key line in config.toml is still required by the parser;
put a placeholder like api_key = "placeholder" in the file and let
the environment variable override it at runtime.
Start the service and verify
Start the service
Via the Windows GUI:
- Open Services (
services.msc). - Scroll to Raft SCEP Bridge.
- Right-click it and choose Start.
- Wait a few seconds. The Status column should now read Running. If it returns to blank (Stopped) almost immediately, the service has crashed during startup — see Troubleshooting.
Via PowerShell:
Start-Service RaftSCEPBridge
Get-Service RaftSCEPBridge | Format-Table Name, Status, StartType
# Expected: Status=Running
Validate configuration and connectivity (optional, recommended)
The bridge binary supports several diagnostic subcommands you can run on the bridge host before or after the service is up. These are command-line only — there is no equivalent GUI dialog for the self-test, since the diagnostic output is produced by the bridge binary running interactively in a console.
To open an elevated PowerShell or Command Prompt session from the
GUI: press Start, type powershell, right-click Windows
PowerShell in the search results, and choose Run as
administrator.
# Validate config.toml parses and meets the schema requirements
& "C:\Program Files\Raft\Bridge\raft-scep-bridge.exe" status
Expected output (values reflect what you put in config.toml):
Config: OK
Server: https://raft.example.com
NDES URL: http://ndes.example.local/CertSrv/mscep/mscep.dll
Challenge mode: Otp
Key algorithm: RSA-2048
Poll interval: 30s
Pending transactions: none
# Probe the Raft tenant API and NDES GetCACert endpoint
& "C:\Program Files\Raft\Bridge\raft-scep-bridge.exe" test-connection
Expected output:
Raft API: OK
NDES GetCACert: OK (2 certificate(s))
If Raft API fails, check the api_url, the bridge token, your
outbound firewall, and your TLS trust (the bridge host must trust the
TLS issuer of api_url).
If NDES GetCACert fails, check the [ndes].url, that NDES is
running (Get-Service CertSvc; Get-WebAppPoolState SCEP), and that
the bridge host can reach the NDES endpoint
(Invoke-WebRequest "<ndes-url>?operation=GetCACert&message=test" -UseBasicParsing).
Check the bridge’s ETW events
The bridge emits structured events through an ETW (Event Tracing for
Windows) provider named Raft-SCEP-Bridge, not through the classic
Windows Application event log. The installer registers an Application-log
source of the same name as a placeholder, but the bridge itself writes
exclusively to ETW — so filtering Event Viewer’s Windows Logs →
Application by source Raft-SCEP-Bridge will show no events.
The two practical ways to read the events are PowerShell
(Get-WinEvent -ProviderName) and an Event Viewer custom view.
Via the Windows GUI (Event Viewer custom view):
- Open Event Viewer: Start → Windows Administrative Tools →
Event Viewer, or press Win + R and run
eventvwr.msc. - In the Actions pane on the right, click Create Custom View….
- In the Create Custom View dialog:
- Logged: Last hour (or whatever window matches when you started the service).
- Select By source rather than By log.
- In the Event sources dropdown, scroll to find Raft-SCEP-Bridge under the ETW providers (the list is long; the dropdown is searchable as you type). If the provider does not appear, the bridge has not been started yet on this host — start the service once and reopen the dialog.
- Click OK.
- Give the view a name such as
Raft SCEP Bridgeand click OK. It now appears under Custom Views in the left tree. - Selecting the view shows all ETW events from the provider. Click a row to read the structured payload in the General tab.
Via PowerShell:
Get-WinEvent -ProviderName Raft-SCEP-Bridge -MaxEvents 20 |
Format-List TimeCreated, LevelDisplayName, Message
Check the rolling log file
The bridge also writes a daily-rolling log file to
C:\ProgramData\RaftBridge\logs\ (configurable via
[logging].file_log_dir). Each day’s file is named
bridge.log.YYYY-MM-DD — today’s file uses today’s date, and there is
no stable bridge.log file without a date suffix.
Via the Windows GUI:
- Open File Explorer and navigate to
C:\ProgramData\RaftBridge\logs\. You should see one file per day the bridge has run, namedbridge.log.YYYY-MM-DD(for examplebridge.log.2026-05-12for today,bridge.log.2026-05-11for yesterday). - Double-click today’s file (or right-click → Open with → Notepad). Scroll to the bottom to see the most recent entries.
- For a live tail equivalent in the GUI, install Notepad++ or Visual Studio Code and use their “tail” / auto-reload features; stock Notepad does not refresh open files.
The file log is the authoritative diagnostic source — it captures everything the bridge produces.
Via PowerShell:
Get-ChildItem "C:\ProgramData\RaftBridge\logs\"
# Tail the most recent log file (today's, or the latest if the service
# hasn't logged yet today)
Get-ChildItem "C:\ProgramData\RaftBridge\logs\" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1 |
Get-Content -Tail 50 -Wait
Confirm the polling loop
On the Raft side, your tenant admin should see the bridge token’s
“last used” timestamp updating every poll_interval_secs seconds.
On the Bridge Keys page in the Raft web UI, that timestamp is shown
next to each key.
Running alongside an existing SCEP integration
If this Windows host already hosts another SCEP bridge or
certificate-issuance integration that uses your AD CS / NDES
infrastructure, the two integrations are not isolated from each
other by default. They share AD, the CA, the cert template, the NDES
IIS instance, the MSCEP registry, and the mscep_admin challenge
cache. Most of these can be safely shared with planning; a few
must be partitioned or sized correctly to avoid the two
integrations interfering with each other.
This section enumerates the shared surfaces and the right way to operate Raft alongside another working SCEP integration.
What is safely partitioned by default
These do not require any extra coordination — Raft picks its own unique identifiers throughout:
- Windows service name. Raft installs as
RaftSCEPBridge; other SCEP services have different names. - Filesystem paths. Raft uses
C:\Program Files\Raft\Bridge\andC:\ProgramData\RaftBridge\. - Diagnostics surface. Raft emits ETW events from the
Raft-SCEP-Bridgeprovider; other SCEP integrations have their own provider names and Application-log sources. - Installer ProductCode / UpgradeCode. Raft’s MSI is independent of any other vendor’s installer; Add or Remove Programs lists the two side by side.
What is shared and must be planned
These touch infrastructure that NDES, AD CS, or AD treats as global. Plan how each is handled before bringing Raft live.
NDES MSCEP template slots
NDES exposes three template “slots” in the registry —
SignatureTemplate, EncryptionTemplate, and
GeneralPurposeTemplate
(Install and configure NDES). A given NDES instance
serves SCEP requests through whichever template the slot points at.
If the other integration already owns these slots with its own template, you have three choices:
- Share the template. Acceptable only if both integrations need certificates with identical EKUs, subject-name policy, validity, and key-usage — which is usually not the case across different certificate-issuance integrations.
- Reassign one slot to Raft’s template. NDES can route different
SCEP “Purpose” requests to different templates:
Signature,Encryption, andGeneralPurposecan each point at a different template name. Raft’s CSRs are all “general-purpose”, so reassigningGeneralPurposeTemplateto Raft’s template while leaving the other slots untouched is a common solution. Coordinate with the other integration’s owner before changing any registry value. - Stand up a second NDES instance on a separate Windows Server
host, dedicated to Raft. Each NDES instance has its own MSCEP
registry, its own
mscep_admincache, and its own app pool, and can therefore be configured independently. This is the cleanest separation and is the right call if the two integrations have meaningfully different policy requirements.
mscep_admin challenge-password cache
NDES caches issued challenge passwords up to PasswordMax entries
(Install and configure NDES). The cache is shared
across every caller of that NDES instance’s mscep_admin. If
PasswordMax is sized for a single bridge’s enrolment rate, doubling
the fetch rate with a second bridge can fill the cache and produce
The password cache is full responses from mscep_admin.
Mitigation:
- Raise
PasswordMaxto cover the aggregate rate of both integrations. For high-activity environments, values in the thousands are reasonable. There is no hard upper bound documented by Microsoft, but each cached entry consumes memory in the NDES worker process; budget accordingly. - Keep
PasswordValidity(the per-challenge TTL) wide enough that in-flight enrolments complete before their challenge expires.
mscep_admin IIS authorization
Grant the bridge access to mscep_admin walks you
through creating a dedicated AD group (RAFT-NDES-Bridges) and an
Allow rule on the mscep_admin virtual directory.
When another integration already has its own allow rule on the same virtual directory, leave that rule alone and add a second Allow rule for your Raft group. IIS evaluates allow rules as a union, so multiple rules coexist without conflict. Do not add the Raft service identity to the other integration’s existing allow group — sharing groups makes it impossible to revoke or audit one integration’s access independently of the other.
Active Directory service accounts
Use a separate gMSA (or domain user) for Raft, even if the other integration has a perfectly good service account.
- Distinct identities produce distinct audit trails in AD, NDES, and the CA. Revoking, rotating, or disabling one identity does not affect the other.
- gMSA password rotation is per-account, so independent identities rotate on independent schedules.
Certificate template ACLs
Both service identities must have Enroll on whichever templates they use:
- If both integrations issue from the same template, grant Enroll to both gMSAs on that template (their respective sections in this guide already cover the Raft side — Create the certificate template).
- If the integrations issue from different templates, each gMSA only needs Enroll on its own template.
NDES IIS worker process
A single NDES IIS application pool serves every caller of that NDES
instance. Restarting it via iisreset interrupts all integrations
that depend on it, not only Raft. When the NDES host needs a
maintenance restart, coordinate the window with the other
integration’s owner.
AD CS log volume
Both integrations submit CSRs through the same NDES RA, and AD CS records every issuance in its database and event log. Combined volume roughly doubles compared to a single integration; size AD CS database retention and audit-log rotation policy with that in mind.
Subject-name collisions
If both integrations attempt to issue a certificate for the same subject (CN or SAN), the outcome depends on your CA’s policy:
- Default (
Don't reissue if same subject existspolicy NOT set). AD CS happily issues both certificates. You end up with two unrelated valid certs for the same identity — usually harmless but occasionally confusing in cert-pinning or trust-anchor scenarios. - With reissue-suppression policy enabled. AD CS rejects the second CSR for the same subject. The integration that loses the race will report an enrolment failure.
If both integrations target the same device pool, coordinate the
subject-name convention so the two integrations issue distinct
identities (for example: per-purpose CN prefixes such as wifi- and
vpn-).
Decommissioning the other integration after migration
If you are running Raft alongside an existing integration during a migration window and plan to retire the other integration after cutover, do the cleanup in this order to avoid orphaning a Raft certificate flow:
- Confirm Raft is enrolling successfully end-to-end against your
device pool (status events in
Raft-SCEP-Bridge, devices showing certificates from the Raft pipeline). - Stop the other integration’s bridge service.
- Verify Raft continues to enrol new devices and renew existing ones for one or two device cohorts.
- Reclaim the other integration’s NDES template slots (return them
to the Raft template if not already) and the other integration’s
mscep_adminallow rule. - Remove the other integration’s AD service account, security group, and any orphan templates.
- Document the cutover date in your AD CS event log retention so future audits can trace which integration issued historical certificates.
Upgrades
Upgrading from an earlier release
Both installer paths perform an in-place major upgrade when run on top of an existing install: Windows Installer locates the prior install via its UpgradeCode, uninstalls it, then installs the new version. Configuration and state files outside the install directory are preserved.
To upgrade:
-
Stop the service.
Via the Windows GUI: open
services.msc, right-click Raft SCEP Bridge → Stop.Via PowerShell:
Stop-Service RaftSCEPBridge -
Run the new EXE or MSI installer the same way as the original install (Install the bridge — Path A: EXE bundle or Install the bridge — Path B: standalone MSI). The new version is installed; the old version is removed automatically.
-
Verify the service identity is still your gMSA — service-identity settings persist across upgrades.
Via the Windows GUI: open
services.msc, find Raft SCEP Bridge, and confirm the Log On As column still shows your gMSA / service account. (Right-click → Properties → Log On tab for a full view.)Via PowerShell:
Get-WmiObject Win32_Service -Filter "Name='RaftSCEPBridge'" | Select-Object Name, StartName, State -
Start the service.
Via the Windows GUI: in
services.msc, right-click Raft SCEP Bridge → Start.Via PowerShell:
Start-Service RaftSCEPBridge
What is preserved across upgrades
C:\ProgramData\RaftBridge\config.toml— untouched.C:\ProgramData\RaftBridge\state.json(in-flight SCEP transactions) — untouched.C:\ProgramData\RaftBridge\logs\— untouched.- Service identity (
sc.exe config obj=setting) — preserved. - Any environment variables you set (
RAFT_BRIDGE_*) — preserved.
What changes across upgrades
- The bridge binary at
C:\Program Files\Raft\Bridge\raft-scep-bridge.exeis replaced. - The Event Log source registration is refreshed (the source name stays the same).
- The Add / Remove Programs entry collapses to a single row for the new version.
Switching from MSI to EXE bundle (or vice versa)
The EXE bundle wraps the same inner MSI, so upgrading from a standalone-MSI install to an EXE bundle install (or back) is treated as a normal in-place upgrade. Your configuration is preserved; the service is left in the state you stopped it in. After the upgrade, start the service again.
Uninstall
Via the Windows GUI:
- Open Settings → Apps → Installed apps (Windows Server 2019 and
later), or Control Panel → Programs and Features (
appwiz.cpl). - Locate Raft SCEP Bridge in the list.
- In Settings → Installed apps: click the ⋮ (three-dot) menu on the right of the row → Uninstall → confirm. In Programs and Features: select the entry → Uninstall in the top toolbar → confirm.
- Wait for the uninstaller to complete. The entry disappears from the list when done.
Via PowerShell:
# Locate the bundle's QuietUninstallString and run it
$uninst = Get-ItemProperty `
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Where-Object DisplayName -eq "Raft SCEP Bridge"
Start-Process -FilePath "cmd.exe" `
-ArgumentList "/c $($uninst.QuietUninstallString)" `
-Wait -NoNewWindow
Or for the standalone MSI path:
msiexec /x raft-scep-bridge-<version>-x64.msi /qn
Uninstall behaviour:
-
The
RaftSCEPBridgeservice is stopped and removed. -
The classic Event Log source stub
Raft-SCEP-Bridgeis unregistered (the ETW provider of the same name is process-scoped and goes away when the service is removed). -
Files under
C:\Program Files\Raft\Bridge\are removed. -
C:\ProgramData\RaftBridge\is preserved — configuration, state, and logs remain on disk. Delete it manually if you want to remove every trace of the bridge.Via the Windows GUI: open File Explorer, type
C:\ProgramDatain the address bar, right-click the RaftBridge folder → Delete, and confirm.Via PowerShell:
Remove-Item -Recurse -Force "C:\ProgramData\RaftBridge" -
The gMSA, NDES service account, certificate template, NDES registry settings, and IIS authorisation rule are not touched by the uninstaller. If you are decommissioning the bridge permanently, remove them manually as well.
Troubleshooting
The service starts and immediately stops
Most often this is a config.toml problem. The fastest way to see
the precise error is to run the bridge interactively from a console
and read the message it prints.
Via the Windows GUI:
-
Press Start, type
powershell, right-click Windows PowerShell, and choose Run as administrator. -
In the elevated PowerShell window, run:
& "C:\Program Files\Raft\Bridge\raft-scep-bridge.exe" status -
If
statusreports an error, run the bridge interactively to see the full startup trace:& "C:\Program Files\Raft\Bridge\raft-scep-bridge.exe" run --interactivePress Ctrl + C in the console to stop it.
For background detail, also look at the ETW events from the
Raft-SCEP-Bridge provider (see
Check the bridge’s ETW events) and at the
most recent file in C:\ProgramData\RaftBridge\logs\ (named
bridge.log.YYYY-MM-DD).
Via PowerShell:
& "C:\Program Files\Raft\Bridge\raft-scep-bridge.exe" status
Common config errors and their meaning:
| Error | Cause / fix |
|---|---|
server.api_url is required |
[server].api_url is empty. Set it to your Raft tenant URL. |
server.api_key is required |
[server].api_key is empty and RAFT_BRIDGE_API_KEY is not set. |
ndes.url is required |
[ndes].url is empty. Set it to your NDES SCEP endpoint. |
challenge.password is required when mode = static |
[challenge].mode = "static" but no password = "..." line. Either add the password or switch to mode = "otp" with otp_url. |
challenge.otp_url is required when mode = otp |
[challenge].mode = "otp" but no otp_url = "..." line. Add the mscep_admin URL. |
bridge.key_algorithm must be RSA-2048 or RSA-4096 |
[bridge].key_algorithm set to an unsupported value. Choose RSA-2048 or RSA-4096. |
Also inspect the bridge’s ETW events (Check the bridge’s ETW events) and the rolling log file (Check the rolling log file).
Via PowerShell:
Get-WinEvent -ProviderName Raft-SCEP-Bridge -MaxEvents 20 |
Format-List TimeCreated, LevelDisplayName, Message
Get-ChildItem "C:\ProgramData\RaftBridge\logs\" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1 |
Get-Content -Tail 50
Raft API: FAILED on test-connection
Causes, in order of likelihood:
- Bridge token is wrong or revoked. Compare
[server].api_key(orRAFT_BRIDGE_API_KEY) byte-for-byte against the value your tenant admin issued. Note that the token is case-sensitive hexadecimal — copy / paste rather than retyping. api_urlis wrong. Should be the Raft tenant URL with no trailing slash and no path (e.g.https://raft.example.com, nothttps://raft.example.com/v1/scep/).- TLS trust failure. The bridge host must trust the issuer of
the TLS certificate served at
api_url. For an internal CA, import the root intoCert:\LocalMachine\Root. For a public CA (Let’s Encrypt, DigiCert, etc.) this should already work. - Outbound firewall is blocking TCP 443 to the Raft host.
Test-NetConnection -ComputerName raft.example.com -Port 443.
NDES GetCACert: FAILED on test-connection
Causes:
- NDES is not running.
Get-Service CertSvcshould beRunning;Get-WebAppPoolState SCEPshould beStarted. ndes.urlis wrong. It must point at themscep.dllpath, not the bare IIS host. The canonical form ishttp(s)://<host>/CertSrv/mscep/mscep.dll.- No bridge-host → NDES-host connectivity. Verify with
Test-NetConnection -ComputerName <ndes-host> -Port 80(or 443 for HTTPS). - NDES IIS application is in a bad state. Restart:
Restart-WebAppPool SCEP; iisreset /restart.
mscep_admin returns HTTP 401 to the bridge
The bridge logged a 401 Unauthorized while fetching a challenge
password. In order of likelihood:
-
Service identity is wrong. Confirm the service is running as your gMSA, not
LocalSystem.Via the Windows GUI: open
services.msc, find Raft SCEP Bridge, and inspect the Log On As column (or right-click → Properties → Log On tab).Via PowerShell:
Get-WmiObject Win32_Service -Filter "Name='RaftSCEPBridge'" | Select-Object StartName # Expected: <NETBIOS>\RAFTBRIDGE$ (or your domain user account)If it shows
Local System, revisit Switch the bridge service to run as the gMSA. -
The gMSA is not authorised on
mscep_admin. Confirm the AD group containing the gMSA is in the IIS authorisation rule.Via the Windows GUI: open IIS Manager on the NDES host, navigate to Default Web Site → CertSrv → mscep_admin, double-click Authorization Rules, and confirm an Allow rule exists for
<NETBIOS>\RAFT-NDES-Bridges(or whichever group contains your bridge’s identity).Via PowerShell:
Get-WebConfiguration ` -PSPath "MACHINE/WEBROOT/APPHOST" ` -Location "Default Web Site/CertSrv/mscep_admin" ` -Filter "system.webServer/security/authorization" | Select-Object -ExpandProperty CollectionIf your group is not listed, return to Grant the bridge access to
mscep_admin. -
IIS hasn’t picked up authz changes.
Via the Windows GUI: in IIS Manager, select the server node and click Stop then Start in the Actions pane.
Via PowerShell:
iisreset /restart -
Long-idle authentication state. On hosts that have been idle for an extended period (hours to days), NDES occasionally returns 401 to authenticated requests until IIS is restarted. If you see this pattern, run
iisreset /restarton the NDES host. If it recurs frequently, investigate Application-event-log entries from the W3SVC / IIS-W3SVC-WP sources around the failure time.
NDES returns badRequest / disposition=incomplete on PKCSReq
The challenge password the bridge presented to NDES was rejected. Causes:
EnforcePassword=0was used at some point. Confirm it is1now (see Install and configure NDES) and that the bridge has been restarted since.- Challenge has expired or was reused (static mode). Static
mode uses one cached challenge for many CSRs; the cached value
is only valid for the duration of NDES’s
PasswordValiditysetting (10 days at the recommended value). If you re-installed NDES, reset registry settings, or restarted the NDES host’s W3WP, the cached challenges in the bridge’sconfig.tomlmay no longer be valid. Fetch a new one and updateconfig.toml.
Element not found / Event 77 in the CA event log
The certificate template referenced in the SCEP profile and / or
the NDES registry does not exist in AD, or its OID does not match
the AD-published template object. Most often this happens after
re-importing a template from another forest. Re-create the template
by cloning the built-in Computer template fresh
(Create the certificate template) rather than
importing from a dump.
After-config-change restart sequence
If you change something in this configuration after the fact, the relevant services need restarting.
| What you changed | Service to restart | GUI | PowerShell |
|---|---|---|---|
| Certificate template / publish new template | CertSvc (CA host) |
certsrv.msc → right-click server → All Tasks → Stop Service → Start Service |
Restart-Service CertSvc -Force |
| MSCEP registry values / IIS authz rules | IIS (NDES host) | inetmgr → select server node → Stop then Start in the Actions pane |
iisreset /restart |
config.toml or RAFT_BRIDGE_* env vars |
RaftSCEPBridge (bridge host) |
services.msc → right-click Raft SCEP Bridge → Restart |
Restart-Service RaftSCEPBridge |
Capturing diagnostics for a support ticket
If you need to send diagnostics to support, collect a useful baseline. Sanitise the bridge token from any output before sharing.
Via the Windows GUI:
- In Event Viewer (
eventvwr.msc), open the Raft SCEP Bridge custom view you created in Check the bridge’s ETW events (or create one now). With the view selected, click Save All Events in Custom View As… in the Actions pane and save asbridge-events.evtx. Send that file. - In File Explorer, copy the most recent files from
C:\ProgramData\RaftBridge\logs\(each is namedbridge.log.YYYY-MM-DD) to a scratch folder and send those. - Open
services.msc, right-click Raft SCEP Bridge → Properties, and screenshot the General and Log On tabs. - On the NDES host, open
regedit, navigate toHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MSCEP, and right-click the MSCEP key → Export… to save the relevant keys as a.regfile.
Via PowerShell:
# Service state and identity
Get-WmiObject Win32_Service -Filter "Name='RaftSCEPBridge'" |
Select-Object Name, StartName, State, StartMode | Format-List
# Last 200 lines of the most recent bridge log file
Get-ChildItem "C:\ProgramData\RaftBridge\logs\" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1 |
Get-Content -Tail 200
# Last 30 minutes of bridge ETW events
Get-WinEvent -ProviderName Raft-SCEP-Bridge -MaxEvents 200 |
Where-Object TimeCreated -gt (Get-Date).AddMinutes(-30) |
Format-List TimeCreated, LevelDisplayName, Message
# Config + connectivity self-test
& "C:\Program Files\Raft\Bridge\raft-scep-bridge.exe" status
& "C:\Program Files\Raft\Bridge\raft-scep-bridge.exe" test-connection
# NDES side
Get-Service CertSvc
Get-WebAppPoolState SCEP
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" |
Select-Object SignatureTemplate, EncryptionTemplate, GeneralPurposeTemplate,
EnforcePassword, UseSinglePassword, PasswordMax, PasswordValidity
Operational reference
File and registry locations
| Path | Purpose |
|---|---|
C:\Program Files\Raft\Bridge\raft-scep-bridge.exe |
Bridge binary (installed by MSI; do not edit). |
C:\ProgramData\RaftBridge\config.toml |
Bridge configuration. Admin-managed; not touched by installer/upgrader/uninstaller. |
C:\ProgramData\RaftBridge\state.json |
In-flight SCEP transaction state (CSRs awaiting NDES poll). Managed automatically by the bridge. |
C:\ProgramData\RaftBridge\logs\bridge.log.YYYY-MM-DD |
Daily-rolling log file (one per day, dated). Default path; override the directory with [logging].file_log_dir. |
HKLM:\SYSTEM\CurrentControlSet\Services\RaftSCEPBridge |
Windows service registration (managed by installer + sc.exe config). |
HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\Application\Raft-SCEP-Bridge |
Classic Event Log source stub. Registered by the MSI but unused at runtime — the bridge writes events to the ETW provider of the same name instead. |
Bridge command-line subcommands
The installed binary supports the following subcommands. All accept
an optional --config <path> flag pointing at an alternate
config.toml.
| Command | Purpose |
|---|---|
raft-scep-bridge.exe run |
The service entry point. Service Control Manager invokes this automatically; you do not run it manually. |
raft-scep-bridge.exe run --interactive |
Run in the foreground for live troubleshooting (Ctrl+C to stop). Useful for diagnosing config issues. |
raft-scep-bridge.exe status |
Validate config.toml, print effective settings, and show any in-flight SCEP transactions. |
raft-scep-bridge.exe test-connection |
Probe the Raft API and NDES GetCACert and print the results. |
raft-scep-bridge.exe version |
Print the installed bridge version. |
raft-scep-bridge.exe install / uninstall |
Manage the Windows service entry. The MSI already installs the service; these subcommands are for advanced repair scenarios only. |
NDES endpoints reference
| URL | Purpose | Auth |
|---|---|---|
http(s)://<ndes-host>/CertSrv/mscep/mscep.dll |
SCEP enrolment (GetCACert, GetCACaps, PKCSReq, …) | Anonymous (NDES default) |
http(s)://<ndes-host>/CertSrv/mscep_admin/ |
Challenge-password retrieval | Windows Integrated Auth (Negotiate / NTLM) |
Bridge token lifecycle
| Action | Where |
|---|---|
| Generate | Raft web UI → Bridge Keys (/admin/scep-bridge-keys) → Generate Bridge Key. Reveals the token once. |
| Use | Place in config.toml [server].api_key, or set RAFT_BRIDGE_API_KEY env var. |
| Inspect | Raft web UI → Bridge Keys lists labels, creation date, last-used timestamp. |
| Rename | Raft web UI → Bridge Keys → Rename button on the row → edit the label inline. |
| Revoke | Raft web UI → Bridge Keys → Revoke. Effect is immediate; bridge using the revoked token will start receiving HTTP 401. |
| Rotate | Generate a new token, update config.toml (or env var), Restart-Service RaftSCEPBridge, then revoke the old token. |
What does NOT survive uninstall
Things you set up that the uninstaller leaves in place — clean these up manually if you are removing the bridge permanently:
- The
RAFTBRIDGEgMSA in AD. - The NDES service account (
ndes_svc). - The certificate template (
RaftDeviceCertificate). - The NDES installation itself.
- NDES registry settings under
HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP. - The scoped IIS authorisation rule on
mscep_admin. - The bridge token in your Raft tenant (revoke it from the Bridge Keys page).
C:\ProgramData\RaftBridge\(config, state, logs).
License and signing
Software license
The Raft SCEP Bridge — both the EXE bundle and the standalone MSI — is distributed under the GNU AGPL-3.0, the same license as the rest of Raft Management.
The EXE bundle additionally embeds a copy of Microsoft’s Visual C++ Redistributable for Visual Studio 2015-2022 (x64). The Microsoft component is distributed under Microsoft’s Redistributable License (not AGPL-3.0). If you need to keep your install strictly free-software-licensed, use the standalone MSI (Install the bridge — Path B: standalone MSI) and install the VC++ Redistributable yourself directly from Microsoft.
Authenticode signing
The EXE bundle and MSI are not currently Authenticode-signed; SmartScreen will warn on first install (see the SmartScreen warning on first install note). Authenticode signing is on the roadmap for a future release.
If you run into anything not covered above, please contact Raft Management support with the diagnostics described in Capturing diagnostics for a support ticket.