Today we are going to look under the hood of certificate requests or renewals on an MDM (Intune) managed Windows client. The environment is simple and uses a Windows client and SCEPman as the Cloud CA, which is easily set up and nothing more than an Azure App Service.
It is especially interesting, as this scenario uses MDM managed Windows client, which means the OMA-DM client is used to accomplish the certificate requests and renewals, which differs from a GPO-managed (domain joined) Windows client. The Windows MDM client uses a subset of the Open Mobile Alliance (OMA) Device Management (DM) standard protocol v1.2.1 and the executable of the OMA-DM client is the omadmclient.exe.
- MDM Synchronization
- The Scheduled Task
- Challenge validation by Intune
- Error handling
- Further investigations
- Closing words
For a better overview, the following figure shows the certificate processing on an MDM-managed Windows client. No worries, we will deep dive into the details of the complete processing chain.
Let’s get started with the most basic step, the MDM sync. Without the MDM sync, nothing will be processed on the Windows client at all! The following figure shows the task scheduler with MDM synchronization tasks.
As soon as the regular MDM sync is triggered the omadmclient.exe will check with the server-side (Intune) if a certificate needs to be requested or if a certificate needs to be renewed.
The MDM sync will be triggered by the tasks from the task scheduler:
- at user login
- every 8h
- client receives a push notification via the Windows Notification Services (WNS)
Here are the official docs about sync schedules.
The task scheduler uses a GUID for the subfolder of the EnterpriseMgmt tasks (seen in the picture above). This is the CurrentEnrollmentId. The GUID can be resolved from the registry like shown below:
If you like to start the sync via UI, use Settings > Accounts > Access work or school > click the Azure AD connection and Info button > scroll down and click Sync. If you prefer the scripted way, here is a nice PowerShell snippet to start the MDM Sync:
[Windows.Management.MdmSessionManager,Windows.Management,ContentType=WindowsRuntime] $session = [Windows.Management.MdmSessionManager]::TryCreateSession() $session.StartAsync()
As mentioned, the omadmclient.exe will process the MDM sync and will receive profiles if new certificate profiles are assigned. The profile(s) (SCEP device configuration template) will be stored in the registry. This includes the ServerURL, SubjectName, SANs, KeyLength, KeyUsage, EKU, Validity, and everything that we have configured in Intune except the renewal threshold, which is held only on the server-side (nowhere available on the client side). More detailed information about the renewal process later on. Here is the stored SCEP device configuration template value in the registry:
The registry values represent the Intune SCEP profile values, except the renewal threshold. Here is a screenshot of the configured SCEP device configuration template in Intune:
As a result of the research, the renewal threshold must not be present on the client side, if the server-side is taking care of the threshold! That’s what we expect Intune is actually doing.
If we look closely into the MDM client implementation, we can see that client and server-side caching is implemented via the NodeCache CSP. Here is the official NodeCache CSP documentation from Microsoft and an important statement from there is highlighted below:
The NodeCache configuration service provider is used to manage the client cache. This configuration service provider is to be used only by enterprise management servers. It provides a level of abstraction that decouples the management of the node list from a specific backing store. It synchronizes the client cache with the server side cache. It also provides an API for monitoring device-side cache changes.
Worth reading is the article about a typical DM session with the NodeCache CSP to get an even better understanding of the client and server communication.
Based on this information we can conclude that the server-side is holding all necessary information about the certificates on the client (again compare MS docs statement: “It synchronizes the client cache with the server cache.”). We can also see in the client cache the encoded certificate (see picture below). The certificate itself is in the ExpectedValue which represents the EncodedCertificate. As the cache is synchronized, the server side will have the same information.
This means the server-side can calculate the renewal threshold for the certificates as it knows the certificates enrolled on the client, by inspecting them and calculating the renewal with the configured threshold from the profile. With the help of my SyncML Viewer tool we can see that the MDM server is asking for changed nodes in the NodeCache for the client and server-side cache synchronization:
And then we see the server requesting thumbprints of already enrolled certificates, here with CmdID 65 and 66:
The server is getting responses for the requests (CmdRef 65, 66). In this case, one certificate is available and a thumbprint is delivered in the data field; another thumbprint is not available, so no data field value is sent back (we deleted the certificate from the client):
This information is enough for the server-side (Intune) to calculate the renewal for the first certificate as the certificate is also available in the server-side cache (remember the cache is held in sync). The renewal threshold is configured in the SCEP profile and therefore also known by the server side. Calculation can be done.
For the second certificate, the server-side also knows that a new certificate should be requested, as it expects the client to have this certificate, but the client wasn’t able to prove that it still has the certificate (no thumbprint available, empty XML data field).
Finally, Intune sent two sets of certificate request information in this test case. Each set contains among other properties a challenge that authorizes the client to request a new certificate. The picture below shows just one Challenge sent back from Intune:
As we have seen, the OMA-DM client will process certificate information and will receive a challenge from Intune for an upcoming certificate request and will store it for the ongoing request time in the registry (DPAPI encrypted). The challenge is used by Intune to verify a valid request. The challenge value is stored encrypted (DPAPI) in the registry. Challenge construction and validation with Intune will be described and shown later in the article.
The omadmclient.exe will then create a scheduled task for dmcertinst.exe under the scheduled task folder EnterpriseMgmt to request the certificate from Intune.
The Scheduled Task
The name for the scheduled task to request a certificate is built after this naming scheme:
The MDM Account ID is found here:
In Intune we can find the device configuration Policy ID easily in the URL of the policy:
And depending if it is a user or device certificate, we have the SID for System (well-known S-1-5-18) or the User SID appended to the task name:
Here is an example from the test device:
The scheduled task is scheduled for 3 attempts every 1 min:
As a command we see dmcertinst.exe with various parameters:
Here is the complete command from the test device:
dmcertinst.exe -s -k "Software\Microsoft\SCEP\MS DM Server\ModelName_AC_dede853d-e274-406b-9d0d-47b6fe65b440_LogicalName_286033f4_0515_414d_b58f_08e74f6e8e3c_Hash_68180377\Install" -h HKLM -t ModelName_AC_dede853d-e274-406b-9d0d-47b6fe65b440_LogicalName_286033f4_0515_414d_b58f_08e74f6e8e3c_Hash_68180377
-s not known
-k Registry-Key where to find the settings
-h Registry-Hive of the settings, HKLM or HKCU
-t SCEP registry template name?! Assumption: Could be used to build the task name and general identification or creation of diagnostics entry (see below) or deletion of the scheduled task at the end of the process.
Observed on the test device, the dmcertinst.exe binary reads the settings as defined via the calling parameters here “HKLM\Software\Microsoft\SCEP\MS DM Server\ModelName_AC_dede853d-e274-406b-9d0d-47b6fe65b440_LogicalName_286033f4_0515_414d_b58f_08e74f6e8e3c_Hash_68180377\Install”
But most important for a successful certificate request, we can also see in Process Monitor (procmon.exe) that dmcertinst.exe is reading (RegQueryValue) the important Challenge value from the registry:
After a successful request or the 3 attempts were made, the Challenge key is deleted (RegDeleteKey) from the registry again:
Finally the scheduled task is deleted.
At this point, the client has successfully sent a Certificate Signing Request (CSR) to SCEPman (the Cloud CA). Now the CA endpoint needs to validate if the Windows client is allowed to request a certificate. This is done by SCEPman on the server side. The service will verify the request challenge with Intune via Microsoft Intune API and the SCEP challenge validation (scep_challenge_provider) and will act accordingly on the success or failure. This is the same process for requests to other SCEP services (MS on-premises NDES implementation published via Azure App Proxy or other third parties. Also for the validation part against Intune (described below), the process is the same for other server-side implementations.
Challenge validation by Intune
The SCEP challenge validation can be seen in detail below. The following excerpt is from the official Microsoft documentation: Use APIs to add third-party CAs for SCEP to Intune
Devices that check-in with Intune are assigned the SCEP profile, and are configured with these parameters. A dynamically-generated SCEP challenge password is created by Intune, and then assigned to the device.
This challenge contains:
- The dynamically-generated challenge password
- The details on the parameters expected in the certificate signing request (CSR) that the device issues to the SCEP server
- The challenge expiration time (currently, challenges are valid for one hour)
Here is an example of the challenge:
<CertEnrollToken> <Data> <CertEnrollChallenge>[PKCS7 Base64 String]</CertEnrollChallenge> <SignerThumbprint>E6DDF5843F0358F454E050EA13DD51246EDCFBA5</SignerThumbprint> </Data> <Signature>[Hex encoded PKCS7 String]</Signature> <DeviceId>28fe573c-1077-4187-ab33-b70c89f9e682</DeviceId> <CertificateRequestId>ModelName=AC_dede853d-e274-406b-9d0d-46b6fe65b440/LogicalName_286033f4_0515_414d_b58f_08e74f6e8e3c;Version=3;Hash=68180377</CertificateRequestId> <Timestamp>2022-09-19T17:36:59.4897428Z</Timestamp> </CertEnrollToken>
Intune encrypts this information, signs the encrypted blob, and then packages these details as the SCEP challenge password.
Devices contacting the SCEP server to request a certificate then include this SCEP challenge password in the CSR. The SCEP server sends the CSR including the SCEP challenge password to Intune for validation. This challenge password and CSR must pass validation for the SCEP server to issue a certificate to the device. When a SCEP challenge is validated, the following checks happen:
- Validates the signature of the encrypted blob
- Validates that the challenge hasn’t expired
- Validates that the profile is still targeted to the device
- Validates that the certificate properties requested by the device in the CSR match the expected values
As with every tech component, something can go wrong. For example, the Cloud CA endpoint is not available or communication is suddenly interrupted, etc.
In case something goes wrong during the certificate request, dmcertinst.exe will write an ErrorCode and Status to the registry:
Software\Microsoft\SCEP\MS DM Server\ModelName_AC_<MDM_AccountId>_LogicalName_<Intune_PolicyId>_Hash_<HashValue><SID>
On the test device the path was “HKLM\Software\Microsoft\SCEP\MS DM Server\ModelName_AC_dede853d-e274-406b-9d0d-47b6fe65b440_LogicalName_286033f4_0515_414d_b58f_08e74f6e8e3c_Hash_68180377” as shown below:
The stored hex error code can be resolved via certutil.exe:
certutil -error <hex-code>
Here is the example output from the error code above. The Cloud CA SCEPman was turned off for this test, and as a result we got an error 404 not found:
As a little bonus 😊 – (ab)use dmcertinst.exe to request certificates manually on an MDM-managed Windows client from any SCEP service. This hack might come in handy in situations where you want to debug some SCEP service or just to gain a deeper understanding of how Intune’s SCEP client works. There is another SCEP client built into Windows, certreq, but it is tailored for Microsoft’s NDES server. An alternative for production use is the Open Source SCEPclient maintained by the SCEPman team.
As SCEPman provides also a static validation endpoint to request certificates. We thought it might be nice to try to request a certificate with a fixed password encoded and written in the challenge value in the registry. On the SCEPman server-side checks for this password on the static endpoint.
We configured the static validation endpoint with a simple password (“password”), encoded the password accordingly, and have written it into the registry. Then we started the request via dmcertinst.exe.
You can download the script we wrote to make dmcertinst.exe create and submit the SCEP request from GitHub:
If you want to add some parameters or improve otherwise, Pull Requests are welcome!
Here is an example output of the script:
And on the server-side (SCEPman) we have the corresponding log entry:
2022-09-21 08:35:49.356 +00:00 [Information] Scepman.Core.CertificationAuthority.KeyVaultCA: Issued a certificate with serial number 509D9E1CCF4F7A5CA4F72243A8AAFEB0CF419A43
We tried the same certificate request on a Windows Server 2016 (which has the dmcertinst.exe as well), but we did not succeed. Probably Windows Servers are missing other MDM prerequisites which are not fulfilled. No investment was made to dig deeper into that.
A special thanks to my colleague Christoph Hannebauer for researching with me on this exciting topic! All the insights we gained, drive a deep understanding of the technology behind the MDM SCEP certificate implementation on Intune-managed Windows clients and are very useful for troubleshooting scenarios.