|
| 1 | +# SCEP Platform Research |
| 2 | + |
| 3 | +PINT already implements SCEP end-to-end for iOS/macOS using Apple's native mobileconfig SCEP payload and automatic renewal. This document covers research into SCEP enrollment for other platforms and the PINT endpoints available for manual enrollment. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## PINT SCEP Endpoints |
| 8 | + |
| 9 | +### `GET /scep` / `POST /scep` |
| 10 | + |
| 11 | +The SCEP RA endpoint. Handles `GetCACaps`, `GetCACert`, and `PKIOperation`. No authentication required — the one-time challenge password in the enrollment request is the auth. |
| 12 | + |
| 13 | +### `GET /profile/scep-challenge` |
| 14 | + |
| 15 | +Requires an active PINT session. Issues a one-time challenge token (15-minute TTL) and returns it as JSON: |
| 16 | + |
| 17 | +```json |
| 18 | +{"challenge": "a3f8b1c2d4e5f6a7b8c9d0e1f2a3b4c5"} |
| 19 | +``` |
| 20 | + |
| 21 | +Use this token as the challenge password in any SCEP client. A new token is required for each enrollment — tokens are consumed on use. |
| 22 | + |
| 23 | +```bash |
| 24 | +# Fetch a token (substitute your session cookie) |
| 25 | +TOKEN=$(curl -s -b <session-cookie> https://pint.csh.rit.edu/profile/scep-challenge | jq -r .challenge) |
| 26 | +``` |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## Windows |
| 31 | + |
| 32 | +### Current state |
| 33 | + |
| 34 | +Windows has no built-in SCEP client that works against a generic SCEP server. The native tooling (`Get-Certificate`, `certmgr.msc`) only speaks Microsoft's own XCEP/WSTEP protocols. However, the PSCertificateEnrollment PowerShell module wraps Windows's `IX509SCEPEnrollment` COM interface and is documented as compatible with any RFC 8894 server. It is the most practical path today. |
| 35 | + |
| 36 | +### Enrollment with PSCertificateEnrollment |
| 37 | + |
| 38 | +```powershell |
| 39 | +# One-time: install the module |
| 40 | +Install-Module PSCertificateEnrollment |
| 41 | +
|
| 42 | +# Fetch a challenge token from PINT (requires browser session cookie) |
| 43 | +$token = (Invoke-RestMethod -Uri https://pint.csh.rit.edu/profile/scep-challenge ` |
| 44 | + -Headers @{ Cookie = "<session-cookie>" }).challenge |
| 45 | +
|
| 46 | +# Enroll |
| 47 | +Get-SCEPCertificate ` |
| 48 | + -SCEPServerURL https://pint.csh.rit.edu/scep ` |
| 49 | + -ChallengePassword $token ` |
| 50 | + -Subject "CN=$env:USERNAME" |
| 51 | +``` |
| 52 | + |
| 53 | +The certificate is installed directly into the Windows certificate store (Personal). The WLAN XML profile (`/profile/generate?platform=windows`) can then reference it for EAP-TLS. |
| 54 | + |
| 55 | +### Automatic renewal |
| 56 | + |
| 57 | +Renewal is scriptable using the existing certificate as authentication: |
| 58 | + |
| 59 | +```powershell |
| 60 | +# Renew using the expiring cert (no new challenge needed) |
| 61 | +Get-SCEPCertificate ` |
| 62 | + -SCEPServerURL https://pint.csh.rit.edu/scep ` |
| 63 | + -SigningCertificate (Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.Subject -like "CN=$env:USERNAME*" }) |
| 64 | +``` |
| 65 | + |
| 66 | +Wrap this in a Task Scheduler job at ~50% of the 1-year cert lifetime (around 6 months) for fully automatic renewal. |
| 67 | + |
| 68 | +### Future: MS-XCEP + MS-WSTEP (native GUI enrollment) |
| 69 | + |
| 70 | +The only path to a true `certmgr.msc` GUI experience on non-domain Windows machines without MDM. The flow: |
| 71 | + |
| 72 | +1. User adds the PINT XCEP URL to `certmgr.msc` under Manage Enrollment Policies (one-time setup). |
| 73 | +2. Right-click Personal > Request New Certificate > select the PINT template. |
| 74 | +3. Certificate is issued and installed. |
| 75 | + |
| 76 | +Authentication would use FreeIPA username/password via WS-Security UsernameToken — no Active Directory required. The specs (MS-XCEP and MS-WSTEP) are fully published open specifications. No production Go library exists for this; it would need to be implemented from scratch. Estimated effort: 1-3 weeks. Renewal from `certmgr.msc` would also work (right-click the cert > Renew), but automatic background renewal would require additionally implementing MS-CEAS (autoenrollment trigger). |
| 77 | + |
| 78 | +--- |
| 79 | + |
| 80 | +## Linux |
| 81 | + |
| 82 | +### Current state |
| 83 | + |
| 84 | +No distro ships a SCEP client by default, but two well-maintained options cover the use case well. Both are packaged for Debian/Ubuntu. |
| 85 | + |
| 86 | +### Option 1: strongSwan `pki --scep` (recommended) |
| 87 | + |
| 88 | +Implements RFC 8894 (the current standard). Ships AES + SHA-256 by default. Better long-term interoperability with PINT's modern SCEP server. |
| 89 | + |
| 90 | +```bash |
| 91 | +# Install |
| 92 | +apt install strongswan-pki |
| 93 | + |
| 94 | +# Fetch a challenge token |
| 95 | +TOKEN=$(curl -s -b <session-cookie> https://pint.csh.rit.edu/profile/scep-challenge | jq -r .challenge) |
| 96 | + |
| 97 | +# Generate a private key |
| 98 | +pki --gen --type rsa --size 2048 --outform pem > wifi-client.key |
| 99 | + |
| 100 | +# Enroll |
| 101 | +pki --scep \ |
| 102 | + --url https://pint.csh.rit.edu/scep \ |
| 103 | + --in wifi-client.key \ |
| 104 | + --dn "CN=$(whoami),O=CSH.RIT.EDU" \ |
| 105 | + --password "$TOKEN" \ |
| 106 | + > wifi-client.crt |
| 107 | + |
| 108 | +# Configure wpa_supplicant or NetworkManager to use wifi-client.key + wifi-client.crt |
| 109 | +``` |
| 110 | + |
| 111 | +Renewal uses the existing cert to authenticate (no new challenge required): |
| 112 | + |
| 113 | +```bash |
| 114 | +pki --scep \ |
| 115 | + --url https://pint.csh.rit.edu/scep \ |
| 116 | + --in wifi-client.key \ |
| 117 | + --cert wifi-client.crt \ |
| 118 | + --dn "CN=$(whoami),O=CSH.RIT.EDU" \ |
| 119 | + > wifi-client.crt.new && mv wifi-client.crt.new wifi-client.crt |
| 120 | +``` |
| 121 | + |
| 122 | +### Option 2: sscep |
| 123 | + |
| 124 | +Implements an older SCEP draft but works against most servers. Debian-packaged (`apt install sscep`). Useful if strongSwan is not available. |
| 125 | + |
| 126 | +```bash |
| 127 | +apt install sscep openssl |
| 128 | + |
| 129 | +TOKEN=$(curl -s -b <session-cookie> https://pint.csh.rit.edu/profile/scep-challenge | jq -r .challenge) |
| 130 | + |
| 131 | +# Generate key and CSR |
| 132 | +openssl genrsa -out wifi-client.key 2048 |
| 133 | +openssl req -new -key wifi-client.key -out wifi-client.csr \ |
| 134 | + -subj "/CN=$(whoami)/O=CSH.RIT.EDU" |
| 135 | + |
| 136 | +# Fetch the SCEP CA cert |
| 137 | +sscep getca -u https://pint.csh.rit.edu/scep -c scep-ca.crt |
| 138 | + |
| 139 | +# Enroll |
| 140 | +sscep enroll \ |
| 141 | + -u https://pint.csh.rit.edu/scep \ |
| 142 | + -c scep-ca.crt \ |
| 143 | + -k wifi-client.key \ |
| 144 | + -r wifi-client.csr \ |
| 145 | + -l wifi-client.crt \ |
| 146 | + -p "$TOKEN" |
| 147 | +``` |
| 148 | + |
| 149 | +Renewal with sscep (uses existing cert/key, no new challenge): |
| 150 | + |
| 151 | +```bash |
| 152 | +sscep enroll \ |
| 153 | + -u https://pint.csh.rit.edu/scep \ |
| 154 | + -c scep-ca.crt \ |
| 155 | + -k wifi-client.key \ |
| 156 | + -r wifi-client.csr \ |
| 157 | + -l wifi-client.crt \ |
| 158 | + -K wifi-client.key \ |
| 159 | + -O wifi-client.crt |
| 160 | +``` |
| 161 | + |
| 162 | +### Automatic renewal via systemd timer |
| 163 | + |
| 164 | +```ini |
| 165 | +# /etc/systemd/system/pint-wifi-renew.service |
| 166 | +[Unit] |
| 167 | +Description=Renew PINT WiFi certificate |
| 168 | + |
| 169 | +[Service] |
| 170 | +Type=oneshot |
| 171 | +ExecStart=/usr/local/bin/pint-wifi-renew.sh |
| 172 | + |
| 173 | +# /etc/systemd/system/pint-wifi-renew.timer |
| 174 | +[Unit] |
| 175 | +Description=PINT WiFi certificate renewal check |
| 176 | + |
| 177 | +[Timer] |
| 178 | +OnCalendar=monthly |
| 179 | +Persistent=true |
| 180 | + |
| 181 | +[Install] |
| 182 | +WantedBy=timers.target |
| 183 | +``` |
| 184 | + |
| 185 | +The renewal script checks remaining validity with `openssl x509 -checkend` and only re-enrolls if the cert is within 30 days of expiry. |
| 186 | + |
| 187 | +--- |
| 188 | + |
| 189 | +## Android |
| 190 | + |
| 191 | +No viable path for unmanaged personal devices. Android's certificate APIs only support SCEP enrollment through a full MDM/EMM enrollment (Intune, Workspace ONE, etc.) — there is no user-initiated flow for installing a SCEP-enrolled certificate into the system keystore for Wi-Fi use without MDM. The current PKCS#12 download flow is the best available option for Android. |
0 commit comments