--- title: "Prepare the private trust domain" description: "Assign the issuing CAs and download the CA certificates used by both workloads." source_url: https://dev.digicert.com/solutions/private-trust-stack/phase-2-private-trust-anchor.html --- # Prepare the private trust domain Assign the issuing CAs and download the CA certificates used by both workloads. ## Phase metadata - Phase: 2 - Workstream: Shared foundation — Step 2 of 2 - Product: DigiCert Private CA - Goal: Identify the root and workload-specific issuing CAs, assign each issuer to the account, and download the CA certificates. - Estimated time: 25 minutes - Inputs: account_id, service_api_token, root_ca_name, device_ica_name, signing_ica_name - Prerequisite: [Establish the account foundation](https://dev.digicert.com/md/solutions/private-trust-stack/phase-1-account-foundation.md) In this phase, you use DigiCert® Private CA to identify the private root CA and the issuing CA for each workload. You assign the issuers to the target account and download the CA certificates used for path validation. You can select the same issuing CA for both workloads when your public key infrastructure (PKI) policy permits it. The root CA certificate is the trust anchor. The issuing CAs sign end-entity certificates. Certificate-template selection happens in the consuming product because DigiCert® Device Trust Manager and DigiCert® Software Trust Manager expose separate template resources and identifiers. ## Phase prerequisites Make sure you have: - The `account_id` and `service_api_token` from [Establish the account foundation](https://dev.digicert.com/md/solutions/private-trust-stack/phase-1-account-foundation.md). - An active private root CA and one or two active issuing CAs beneath that root. - The exact approved names for the root CA, device issuing CA, and signing issuing CA. - A service-user role with **View CM CA** (`VIEW_CM_CA`) and **Manage CM CA Accounts** (`MANAGE_CM_CA_ACCOUNTS`) permissions in DigiCert Private CA. - The approved SHA-256 fingerprint for the root CA certificate. ## Endpoints used | Method | Path | Purpose | |--------|------|---------| | GET | `/certificate-authority/api/v1/ca` | List accessible CAs and inspect account assignments. | | POST | `/certificate-authority/api/v1/ca/{id}/accounts` | Assign an issuing CA to the account. | | GET | `/certificate-authority/api/v1/ca/{id}/download?format=pem` | Download a CA certificate in PEM format. | ## Step 2.1: Select the root and issuing CAs `GET /certificate-authority/api/v1/ca` returns an object with pagination metadata and stores CA records in `items`. The API reference does not document pagination parameters for this endpoint. Before selecting a CA by its approved exact name or ID, confirm that `total` equals the number of returned `items`. If the values differ, stop and confirm the pagination parameters supported by your tenant's API. **cURL** ```bash curl --fail-with-body --silent --show-error \ "https://demo.one.digicert.com/certificate-authority/api/v1/ca" \ -H "x-api-key: ${SERVICE_API_TOKEN}" \ -o ca-inventory.json jq -e ' if .total == (.items | length) then true else error("CA list is incomplete; confirm deployed paging parameters") end' ca-inventory.json > /dev/null select_active_ca_id() { jq -er --arg name "$1" ' [.items[] | select(.name == $name and .status == "active")] | if length == 1 then .[0].id else error("expected one active CA named " + $name) end' ca-inventory.json } ROOT_CA_ID="$(select_active_ca_id "${ROOT_CA_NAME}")" DEVICE_ICA_ID="$(select_active_ca_id "${DEVICE_ICA_NAME}")" SIGNING_ICA_ID="$(select_active_ca_id "${SIGNING_ICA_NAME}")" export ROOT_CA_ID DEVICE_ICA_ID SIGNING_ICA_ID jq -e --arg root_id "${ROOT_CA_ID}" \ --arg device_id "${DEVICE_ICA_ID}" \ --arg signing_id "${SIGNING_ICA_ID}" ' def selected($id): first(.items[] | select(.id == $id)); selected($root_id) as $root | selected($device_id) as $device | selected($signing_id) as $signing | if ((($root.cert_type // $root.ca_type // "") | ascii_downcase) == "root") and ((($device.cert_type // $device.ca_type // "") | ascii_downcase) == "intermediate") and ($device.configuration.issue_end_entities == true) and ($device.issuer.id == $root.id) and ((($signing.cert_type // $signing.ca_type // "") | ascii_downcase) == "intermediate") and ($signing.configuration.issue_end_entities == true) and ($signing.issuer.id == $root.id) then true else error("selected CAs do not form an end-entity issuing hierarchy") end ' ca-inventory.json > /dev/null jq --arg root_id "${ROOT_CA_ID}" \ --arg device_id "${DEVICE_ICA_ID}" \ --arg signing_id "${SIGNING_ICA_ID}" ' [.items[] | select(.id == $root_id or .id == $device_id or .id == $signing_id) | { id, name, status, ca_type, cert_type, issue_end_entities: .configuration.issue_end_entities, issuer, account_assignments }] ' ca-inventory.json ``` **Python** ```python import requests base_url = "https://demo.one.digicert.com" headers = {"x-api-key": service_api_token} response = requests.get( f"{base_url}/certificate-authority/api/v1/ca", headers=headers, timeout=30, ) response.raise_for_status() page = response.json() ca_records = page["items"] if page["total"] != len(ca_records): raise RuntimeError("CA list is incomplete. Confirm deployed paging parameters") active_cas = [item for item in ca_records if item.get("status") == "active"] def select_one_ca(name): matches = [item for item in active_cas if item.get("name") == name] if len(matches) != 1: raise RuntimeError(f"Expected one active CA named {name}. Found {len(matches)}") return matches[0] root = select_one_ca(root_ca_name) device_issuer = select_one_ca(device_ica_name) signing_issuer = select_one_ca(signing_ica_name) root_ca_id = root["id"] device_ica_id = device_issuer["id"] signing_ica_id = signing_issuer["id"] def ca_kind(ca): return (ca.get("cert_type") or ca.get("ca_type") or "").lower() if ca_kind(root) != "root": raise RuntimeError("The selected trust anchor is not a root CA") for label, issuer in (("device", device_issuer), ("signing", signing_issuer)): if ca_kind(issuer) != "intermediate": raise RuntimeError(f"The selected {label} issuer is not an intermediate CA") if (issuer.get("configuration") or {}).get("issue_end_entities") is not True: raise RuntimeError(f"The selected {label} issuer cannot issue end entities") if (issuer.get("issuer") or {}).get("id") != root_ca_id: raise RuntimeError(f"The selected {label} issuer is not beneath the selected root") ``` Before continuing, confirm the following facts from the selected records: - The root has a root `cert_type` or `ca_type`. - Each issuer has an intermediate `cert_type` or `ca_type`, and its `configuration.issue_end_entities` value is `true`. - Each issuer's `issuer.id` equals the selected root CA ID. - The CA status is `active`. Do not infer code-signing eligibility from a top-level `issue_types` value. The end-entity template's extended key usage controls that purpose, and you validate it when you [select the code-signing certificate template](https://dev.digicert.com/md/solutions/private-trust-stack/phase-5-code-signing.md#step-51-select-the-code-signing-certificate-template). ## Step 2.2: Assign each issuer to the account Inspect each issuer's `account_assignments` before adding the account. If an assignment is already present, do not repeat the request that adds it. ```bash assign_ica_if_needed() { local ica_id="$1" if jq -e --arg ica_id "${ica_id}" --arg account_id "${ACCOUNT_ID}" ' any(.items[]; .id == $ica_id and any(.account_assignments[]?; (if type == "object" then .id else . end) == $account_id)) ' ca-inventory.json > /dev/null; then printf 'ICA %s is already assigned to account %s.\n' "${ica_id}" "${ACCOUNT_ID}" return fi curl --fail-with-body --silent --show-error \ -X POST \ "https://demo.one.digicert.com/certificate-authority/api/v1/ca/${ica_id}/accounts" \ -H "x-api-key: ${SERVICE_API_TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"accounts\": [\"${ACCOUNT_ID}\"]}" } assign_ica_if_needed "${DEVICE_ICA_ID}" if [[ "${SIGNING_ICA_ID}" != "${DEVICE_ICA_ID}" ]]; then assign_ica_if_needed "${SIGNING_ICA_ID}" fi ``` For a new assignment, a successful response returns `204 No Content`. Include each distinct, unassigned issuing CA ID only once. List the CAs again, and confirm that each selected issuer's `account_assignments` contains `account_id`. ## Step 2.3: Download and validate the CA certificates Download the root and workload-specific issuing CA certificates. Install the root certificate as the trust anchor in relying-party trust stores. Provide the appropriate issuing CA as an intermediate when a verifier needs it to construct the path. With `format=pem`, save the response as a PEM certificate. Stop if a downloaded file is not parseable by OpenSSL. Never automate trust-store distribution without validating the certificate and its approved fingerprint. ```bash curl --fail-with-body --silent --show-error \ "https://demo.one.digicert.com/certificate-authority/api/v1/ca/${ROOT_CA_ID}/download?format=pem" \ -H "x-api-key: ${SERVICE_API_TOKEN}" \ --output root-ca.pem curl --fail-with-body --silent --show-error \ "https://demo.one.digicert.com/certificate-authority/api/v1/ca/${DEVICE_ICA_ID}/download?format=pem" \ -H "x-api-key: ${SERVICE_API_TOKEN}" \ --output device-issuing-ca.pem curl --fail-with-body --silent --show-error \ "https://demo.one.digicert.com/certificate-authority/api/v1/ca/${SIGNING_ICA_ID}/download?format=pem" \ -H "x-api-key: ${SERVICE_API_TOKEN}" \ --output signing-issuing-ca.pem ``` Inspect and validate the certificates before distribution: ```bash openssl x509 -in root-ca.pem -noout -subject -issuer -serial -fingerprint -sha256 openssl x509 -in device-issuing-ca.pem -noout -subject -issuer -serial -fingerprint -sha256 openssl x509 -in signing-issuing-ca.pem -noout -subject -issuer -serial -fingerprint -sha256 openssl verify -CAfile root-ca.pem device-issuing-ca.pem openssl verify -CAfile root-ca.pem signing-issuing-ca.pem ``` > **Warning** > > Distribute `root-ca.pem` through your approved configuration-management process and verify its SHA-256 fingerprint through a separate trusted channel. Do not trust a downloaded certificate until you validate its identity. ## Phase 2 checkpoint - [ ] `root_ca_id` identifies the expected active root CA. - [ ] `device_ica_id` and `signing_ica_id` identify active intermediates beneath that root. - [ ] Each distinct issuing CA is assigned to `account_id`. - [ ] `device-issuing-ca.pem` and `signing-issuing-ca.pem` validate to `root-ca.pem`. - [ ] The root fingerprint matches the value approved for distribution. The shared foundation is complete. Next, begin the [device identity workstream](https://dev.digicert.com/md/solutions/private-trust-stack/phase-3-device-infrastructure.md), begin the [code-signing workstream](https://dev.digicert.com/md/solutions/private-trust-stack/phase-5-code-signing.md), or work on both in parallel. Both workstreams are required. ## Outputs and handoff | Output | Produced in | Used by | | --- | --- | --- | | `root_ca_id` | Select the root and issuing CAs: root record `id` | Operations, audit, or cleanup | | `device_ica_id` | Select the root and issuing CAs: device issuer record `id` | [Configure device certificate infrastructure](https://dev.digicert.com/md/solutions/private-trust-stack/phase-3-device-infrastructure.md) | | `signing_ica_id` | Select the root and issuing CAs: signing issuer record `id` | [Configure and test private code signing](https://dev.digicert.com/md/solutions/private-trust-stack/phase-5-code-signing.md) | | `root-ca.pem` | Download and validate the CA certificates | [Enroll and verify a device](https://dev.digicert.com/md/solutions/private-trust-stack/phase-4-device-enrollment.md), [Configure and test private code signing](https://dev.digicert.com/md/solutions/private-trust-stack/phase-5-code-signing.md) | | `device-issuing-ca.pem` | Download and validate the CA certificates | [Enroll and verify a device](https://dev.digicert.com/md/solutions/private-trust-stack/phase-4-device-enrollment.md) | | `signing-issuing-ca.pem` | Download and validate the CA certificates | [Configure and test private code signing](https://dev.digicert.com/md/solutions/private-trust-stack/phase-5-code-signing.md) |