Phase 4
Enroll and verify a device
Create passcode enrollment configuration and validate an issued device certificate.
Phase details
- Workstream
- Device identity · Step 2 of 2
- Product
- Device Trust Manager
- Time
- 25 minutes
- Setup
- Mixed setup and verification
- Goal
- Attach a passcode authentication policy to a device group, issue a device certificate, and validate its key and chain.
- Inputs
account_idservice_api_tokendivision_idcertificate_policy_idpasscoderoot-ca.pemdevice-issuing-ca.pem- Expected outputs
authentication_policy_idpasscode_iddevice_group_iddevice_certificate_idcertificate_request_id- Prerequisite
- Configure device certificate infrastructure
In this phase, you use DigiCert® Device Trust Manager to create an authentication policy and add a passcode to it. You assign the authentication policy and certificate policy to a device group, then request a device certificate using passcode authentication. Finally, you verify that the returned private key matches the certificate and that the certificate path terminates at the root trust anchor prepared for the private trust domain.
Phase prerequisites
Make sure you have:
account_idandservice_api_tokenfrom Establish the account foundation anddivision_idandcertificate_policy_idfrom Configure the device certificate infrastructure.root-ca.pemanddevice-issuing-ca.pemfrom Prepare the private trust domain.- A Device Trust Manager role that contains Solution administrator for administrative setup.
- A passcode that contains six to 64 characters and is generated and stored through an approved secret-management process.
- A secure destination for the server-generated device private key.
Endpoints used
| Method | Path | Purpose |
|---|---|---|
| POST | /devicetrustmanager/authentication-service/api/v1/authentication-policy | Create the authentication policy. |
| POST | /devicetrustmanager/authentication-service/api/v1/passcode | Create the enrollment passcode. |
| POST | /devicetrustmanager/api/v4/device-group | Create the group and policy assignment. |
| GET | /devicetrustmanager/api/v4/device-group | Retrieve the group ID and inspect the assignment. |
| POST | /devicetrustmanager/certificate-issuance-service/api/v2/certificate | Request and register a device certificate. |
| GET | /devicetrustmanager/certificate-issuance-service/api/v2/certificate-request/{certificate_request_id}/status | Poll an enrollment that requires approval. |
| POST | /devicetrustmanager/certificate-issuance-service/api/v2/certificate/{certificate_id}/download-approved-certificate | Download an approved certificate and server-generated key. |
Step 4.1: Create an authentication policy
jq -n \
--arg account_id "${ACCOUNT_ID}" '
{
name: "Private device enrollment",
account_id: $account_id,
description: "Passcode authentication for the private device REST policy"
}' |
curl --fail-with-body --silent --show-error \
-X POST \
"https://demo.one.digicert.com/devicetrustmanager/authentication-service/api/v1/authentication-policy" \
-H "x-api-key: ${SERVICE_API_TOKEN}" \
-H "Content-Type: application/json" \
--data-binary @- \
-o authentication-policy-response.json
AUTHENTICATION_POLICY_ID="$(jq -er '.id' authentication-policy-response.json)"
export AUTHENTICATION_POLICY_ID
jq '{id, name, account_id, status}' authentication-policy-response.json
Save the response id as authentication_policy_id.
Step 4.2: Create a passcode credential
The API sets the default value of enable_username_for_passcode to true and requires a username when that setting is enabled. This workflow does not use a username, so set the property to false explicitly. Supply a passcode with six to 64 characters through a protected prompt, and do not put it in shell history or logs.
read -rsp "Enrollment passcode: " ENROLLMENT_PASSCODE
echo
if (( ${#ENROLLMENT_PASSCODE} < 6 || ${#ENROLLMENT_PASSCODE} > 64 )); then
echo "The enrollment passcode must contain 6-64 characters." >&2
return 1 2>/dev/null || exit 1
fi
jq -n \
--arg name "Private device enrollment passcode" \
--arg account_id "${ACCOUNT_ID}" \
--arg authentication_policy_id "${AUTHENTICATION_POLICY_ID}" \
--arg passcode "${ENROLLMENT_PASSCODE}" \
'{
name: $name,
account_id: $account_id,
authentication_policy_id: $authentication_policy_id,
enable_username_for_passcode: false,
passcode: $passcode,
usage_limit: 10
}' |
curl --fail-with-body --silent --show-error \
-X POST \
"https://demo.one.digicert.com/devicetrustmanager/authentication-service/api/v1/passcode" \
-H "x-api-key: ${SERVICE_API_TOKEN}" \
-H "Content-Type: application/json" \
--data-binary @- \
-o passcode-response.json
chmod 600 passcode-response.json
PASSCODE_ID="$(jq -er '.id' passcode-response.json)"
export PASSCODE_ID
jq '{id, name, authentication_policy_id, enable_username_for_passcode, passcode_hint}' \
passcode-response.json
Your tenant’s API might return the passcode in the response. Do not depend on that behavior. Treat the entire response as a secret, do not print the passcode property, and continue using the original ENROLLMENT_PASSCODE value from your secret store. Save the response id as passcode_id.
Step 4.3: Create and retrieve the device group
The policies array contains policy-assignment objects, not policy-ID strings. The assignment below marks the certificate policy as the bootstrap certificate policy and applies the authentication-policy override documented for device groups.
DEVICE_GROUP_NAME="Private trust test devices"
curl --fail-with-body --silent --show-error \
-X POST "https://demo.one.digicert.com/devicetrustmanager/api/v4/device-group" \
-H "x-api-key: ${SERVICE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"${DEVICE_GROUP_NAME}\",
\"description\": \"Validation group for private device certificates\",
\"division_id\": \"${DIVISION_ID}\",
\"account_id\": \"${ACCOUNT_ID}\",
\"is_static\": true,
\"policies\": [
{
\"policy_id\": \"${CERTIFICATE_POLICY_ID}\",
\"assignment_name\": \"Private device bootstrap certificate\",
\"type\": \"bootstrapCertificate\",
\"auth_policy_id\": \"${AUTHENTICATION_POLICY_ID}\",
\"status\": \"ACTIVE\"
}
]
}"
curl --fail-with-body --silent --show-error --get \
"https://demo.one.digicert.com/devicetrustmanager/api/v4/device-group" \
-H "x-api-key: ${SERVICE_API_TOKEN}" \
--data-urlencode "account_id=${ACCOUNT_ID}" \
--data-urlencode "division_id=${DIVISION_ID}" \
--data-urlencode "name=${DEVICE_GROUP_NAME}" \
--data-urlencode "include_assignment=true" |
jq --arg name "${DEVICE_GROUP_NAME}" \
'[.records[] | select(.name == $name and .status == "ACTIVE")] |
if length == 1 then .[0] else error("expected one active device group") end' \
> device-group.json
DEVICE_GROUP_ID="$(jq -er '.id' device-group.json)"
export DEVICE_GROUP_ID
jq '{id, name, status, policies}' device-group.json
Save the exact matching record’s id as device_group_id. Confirm its policy assignment contains the intended certificate_policy_id and authentication_policy_id and reports status: ACTIVE.
Step 4.4: Request the device certificate with the passcode
Passcode authentication is an alternative to authentication with an API token for this endpoint. Send x-passcode, and omit x-api-key. Include device_group_id so Device Trust Manager applies the group policy assignment and its authentication-policy override.
This demo uses the server-side key-generation policy created when you configure the device certificate infrastructure. For production devices with protected key storage, configure client-side generation and submit a certificate signing request (CSR) instead.
umask 077
jq -n \
--arg certificate_policy_id "${CERTIFICATE_POLICY_ID}" \
--arg device_group_id "${DEVICE_GROUP_ID}" \
--arg common_name "device-001.example.internal" \
'{
certificate_policy_id: $certificate_policy_id,
device_group_id: $device_group_id,
certificate_profile_attributes: [
{id: "subject.common_name", value: $common_name}
],
server_side_key_gen: true,
key_type: "rsa_2048",
private_key_format: "pem",
private_key_syntax: "pkcs8",
response_with_certificate_only: false,
include_certificate_chain: true,
split_certificate_response: true
}' |
curl --fail-with-body --silent --show-error \
-X POST \
"https://demo.one.digicert.com/devicetrustmanager/certificate-issuance-service/api/v2/certificate" \
-H "x-passcode: ${ENROLLMENT_PASSCODE}" \
-H "Content-Type: application/json" \
--data-binary @- \
-o device-enrollment-response.json
chmod 600 device-enrollment-response.json
jq '{certificate_request_id, status, result, certificate_id, device}' \
device-enrollment-response.json
CERTIFICATE_REQUEST_ID="$(jq -er '.certificate_request_id' device-enrollment-response.json)"
DEVICE_CERTIFICATE_ID="$(jq -er '.certificate_id' device-enrollment-response.json)"
export CERTIFICATE_REQUEST_ID DEVICE_CERTIFICATE_ID
With require_approval_for_enroll: false, a successful response reports AUTO_APPROVED and SUCCESS and includes the issued certificate.
If enrollment requires approval
If your policy requires approval:
- Poll
GET /devicetrustmanager/certificate-issuance-service/api/v2/certificate-request/{certificate_request_id}/statuswith the same passcode. - Stop polling after the configured timeout or when the request reaches a final status.
- If the status becomes
APPROVEDorAUTO_APPROVED, send aPOSTrequest to/devicetrustmanager/certificate-issuance-service/api/v2/certificate/{certificate_id}/download-approved-certificatewith the required output-format body. - If the status becomes
REJECTEDorCANCELED, stop without downloading the certificate.
400 response after a long delay, do not retry immediately. Follow Enrollment returns an empty 400 response to check for resources created by the request and preserve the information needed for escalation.Step 4.5: Validate the key and certificate path
Extract the sensitive response fields without printing them:
jq -r '.pem' device-enrollment-response.json > device.pem
jq -r '.private_key' device-enrollment-response.json > device.key
chmod 600 device.pem device.key
Confirm the private key matches the end-entity certificate and validate its path to the approved root:
test "$(openssl x509 -in device.pem -pubkey -noout | openssl sha256)" = \
"$(openssl pkey -in device.key -pubout | openssl sha256)"
openssl verify \
-CAfile root-ca.pem \
-untrusted device-issuing-ca.pem \
device.pem
openssl x509 -in device.pem -noout -subject -issuer -serial -dates -purpose
Confirm that the subject matches the requested device identity and that the certificate purpose and extensions match the approved device template. Store the private key in the device’s protected key store and remove intermediate response files according to your secret-handling policy.
Phase 4 checkpoint
- The passcode is attached to
authentication_policy_id, with username mode explicitly disabled. - The device group’s active policy assignment contains the certificate and authentication policy IDs.
- Enrollment used
x-passcodewithout an API token and includeddevice_group_id. - The response reports a successful final status and includes
device_certificate_id. - The private key matches the certificate.
- The certificate path validates to
root-ca.pemthroughdevice-issuing-ca.pem, and its purpose is appropriate for the device workload.
The device enrollment and certificate-validation path is validated.
- If code signing is incomplete, configure and test private code signing.
- If both workstreams are validated, prepare the solution for production operations.