--- title: "Issue a certificate from a private CA" description: "Automate certificate issuance workflows and integrate certificate management.\n" source_url: https://dev.digicert.com/digicert-private-ca-api/tutorials/issue-a-certificate-from-a-private-ca.html --- Private CAs enable organizations to issue trusted certificates for internal services, devices, and users without relying on public certificate authorities. With the DigiCert Private CA API, you can automate certificate issuance workflows and integrate certificate management into your existing infrastructure. In this tutorial, you will: - List available CAs to identify your issuing CA. - Get CA templates to select the appropriate certificate profile. - Generate a certificate signing request (CSR) for your server. - Submit the certificate request to your private CA. - Download the issued certificate. - Validate the certificate chain using OpenSSL. ## Before you begin Before you begin, make sure you have: - A DigiCert ONE account with CA Manager access. - API token with the following permissions: - `VIEW_CM_CA` - View CA details and list available CAs - `VIEW_CM_CUSTOM_TEMPLATE` - View certificate templates - `CREATE_CM_CERTIFICATE` - Issue certificates from a CA - `VIEW_CM_CERTIFICATE` - View and download issued certificates - An existing private CA in active status. - OpenSSL installed. Use `openssl version` to verify. ## Endpoint overview | Method | Path | Description | |--------|------|-------------| | GET | /ca | List all available CAs | | GET | /template | Get certificate templates | | POST | /certificate | Submit certificate request | | GET | /certificate/{id} | Get certificate details | | GET | /ca/{id}/download | Download CA certificate | ## Step 1: List available CAs Before you can issue a certificate, you need to identify the CA that will sign it. Use this request to list all CAs you have access to and find an active issuing CA. **Request:** **curl** ```bash curl -X GET "https://demo.one.digicert.com/ca" \ -H "x-api-key: SERVICE_API_TOKEN" | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" SERVICE_API_TOKEN = "SERVICE_API_TOKEN" response = requests.get( f"{BASE_URL}/ca", headers={"x-api-key": SERVICE_API_TOKEN} ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class ApiExample { public static void main(String[] args) throws Exception { // Configuration String baseUrl = "https://demo.one.digicert.com"; String serviceApiToken = "SERVICE_API_TOKEN"; // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/ca")) .header("x-api-key", serviceApiToken) .GET() .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // Configuration string baseUrl = "https://demo.one.digicert.com"; string serviceApiToken = "SERVICE_API_TOKEN"; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", serviceApiToken); var response = await client.GetAsync($"{baseUrl}/ca"); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` **Successful response (200 OK):** ```json { "id": "01234567-89ab-cdef-0123-456789abcdef", "status": "active", "_note": "Response schema not fully documented in API spec" } ``` From the response, identify a CA with `status` set to `active` and `cert_type` set to `intermediate` (issuing CAs are typically intermediate CAs). Save the `id` value. You will need this in Step 4 when you submit the certificate request, and in Step 6 when you download the CA certificate for chain validation. ## Step 2: Get CA templates Certificate templates define the properties and extensions included in issued certificates. Each template is configured for specific use cases such as TLS server authentication, client authentication, or code signing. **Request:** **curl** ```bash curl -X GET "https://demo.one.digicert.com/template" \ -H "x-api-key: SERVICE_API_TOKEN" | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" SERVICE_API_TOKEN = "SERVICE_API_TOKEN" response = requests.get( f"{BASE_URL}/template", headers={"x-api-key": SERVICE_API_TOKEN} ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class ApiExample { public static void main(String[] args) throws Exception { // Configuration String baseUrl = "https://demo.one.digicert.com"; String serviceApiToken = "SERVICE_API_TOKEN"; // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/template")) .header("x-api-key", serviceApiToken) .GET() .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // Configuration string baseUrl = "https://demo.one.digicert.com"; string serviceApiToken = "SERVICE_API_TOKEN"; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", serviceApiToken); var response = await client.GetAsync($"{baseUrl}/template"); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` **Successful response (200 OK):** ```json { "id": "01234567-89ab-cdef-0123-456789abcdef", "status": "active", "_note": "Response schema not fully documented in API spec" } ``` From the response, identify a template with `cert_type` set to `end_entity` and `active` set to `true`. For server certificates, look for templates with names indicating TLS or server authentication. Save the `id` value. You will need this in Step 4 when you submit the certificate request. ## Step 3: Generate a CSR A certificate signing request (CSR) contains the public key and identity information for your certificate. You generate the CSR locally using OpenSSL, which also creates the corresponding private key. **Command:** ```bash openssl req -new -newkey rsa:2048 -nodes \ -keyout server.key \ -out server.csr \ -subj "/CN=server.example.internal/O=Example Corp/C=US" ``` This command generates: - `server.key` - Your private key (keep this secure and never share it) - `server.csr` - The CSR to submit to the CA Adjust the `-subj` values to match your server's identity: - `CN` - Common name (typically the server's fully qualified domain name) - `O` - Organization name - `C` - Two-letter country code > [!IMPORTANT] > Protect your private key. Anyone with access to this file can impersonate your server. To verify your CSR before submitting it: ```bash openssl req -text -noout -in server.csr ``` ## Step 4: Submit the certificate request Submit your CSR to the private CA for signing. The CA validates the request against the selected template and issues the certificate. **Request:** **curl** ```bash curl -X POST "https://demo.one.digicert.com/certificate" \ -H "x-api-key: SERVICE_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "ca_id": "CA_ID", "template_id": "TEMPLATE_ID", "csr": "CSR", "validity": "VALIDITY" }' | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" SERVICE_API_TOKEN = "SERVICE_API_TOKEN" # Request details CA_ID = "CA_ID" TEMPLATE_ID = "TEMPLATE_ID" CSR = "CSR" VALIDITY = "VALIDITY" payload = { "ca_id": CA_ID, "template_id": TEMPLATE_ID, "csr": CSR, "validity": VALIDITY, } response = requests.post( f"{BASE_URL}/certificate", headers={ "x-api-key": SERVICE_API_TOKEN, "Content-Type": "application/json" }, json=payload ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; public class ApiExample { public static void main(String[] args) throws Exception { // Configuration String baseUrl = "https://demo.one.digicert.com"; String serviceApiToken = "SERVICE_API_TOKEN"; // Request details String caId = "CA_ID"; String templateId = "TEMPLATE_ID"; String csr = "CSR"; String validity = "VALIDITY"; // Build payload ObjectMapper mapper = new ObjectMapper(); ObjectNode payload = mapper.createObjectNode(); payload.put("ca_id", caId); payload.put("template_id", templateId); payload.put("csr", csr); payload.put("validity", validity); // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/certificate")) .header("x-api-key", serviceApiToken) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload))) .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp using System; using System.Net.Http; using System.Text; using System.Text.Json.Nodes; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // Configuration string baseUrl = "https://demo.one.digicert.com"; string serviceApiToken = "SERVICE_API_TOKEN"; // Request details string caId = "CA_ID"; string templateId = "TEMPLATE_ID"; string csr = "CSR"; string validity = "VALIDITY"; // Build payload var payload = new JsonObject { ["ca_id"] = caId, ["template_id"] = templateId, ["csr"] = csr, ["validity"] = validity }; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", serviceApiToken); var content = new StringContent( payload.ToJsonString(), Encoding.UTF8, "application/json" ); var response = await client.PostAsync($"{baseUrl}/certificate", content); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Replace `CA_ID` with the CA ID from Step 1 and `TEMPLATE_ID` with the template ID from Step 2. The `csr` field should contain the PEM-encoded CSR content from your `server.csr` file. **Successful response (200 OK):** ```json { "id": "01234567-89ab-cdef-0123-456789abcdef", "status": "active", "_note": "Response schema not fully documented in API spec" } ``` From the response, save the certificate `id`. You will need this in Step 5 to download the certificate. ## Step 5: Download the certificate Retrieve the issued certificate using the certificate ID from the previous step. The response includes the certificate and optionally the certificate chain. **Request:** **curl** ```bash curl -X GET "https://demo.one.digicert.com/certificate/CERT_ID" \ -H "x-api-key: SERVICE_API_TOKEN" | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" SERVICE_API_TOKEN = "SERVICE_API_TOKEN" CERT_ID = "CERT_ID" response = requests.get( f"{BASE_URL}/certificate/{CERT_ID}", headers={"x-api-key": SERVICE_API_TOKEN} ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class ApiExample { public static void main(String[] args) throws Exception { // Configuration String baseUrl = "https://demo.one.digicert.com"; String serviceApiToken = "SERVICE_API_TOKEN"; String certId = "CERT_ID"; // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/certificate/" + certId)) .header("x-api-key", serviceApiToken) .GET() .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // Configuration string baseUrl = "https://demo.one.digicert.com"; string serviceApiToken = "SERVICE_API_TOKEN"; string certId = "CERT_ID"; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", serviceApiToken); var response = await client.GetAsync($"{baseUrl}/certificate/{certId}"); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Replace `{id}` with the certificate ID from Step 4. **Successful response (200 OK):** ```json { "id": "01234567-89ab-cdef-0123-456789abcdef", "status": "active", "_note": "Response schema not fully documented in API spec" } ``` From the response, extract the certificate from the `blob` field. The certificate is base64-encoded. Decode it and save to a file: ```bash echo "CERTIFICATE_BLOB_CONTENT" | base64 -d > server.crt ``` If the response includes a `chain` array, save those certificates as well for chain validation. ## Step 6: Validate the certificate chain Verify that your issued certificate properly chains to your private CA. This confirms the certificate will be trusted by systems that trust your CA. First, download the CA certificate: **Request:** **curl** ```bash curl -X GET "https://demo.one.digicert.com/ca/CA_ID/download?format=pem" \ -H "x-api-key: SERVICE_API_TOKEN" | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://demo.one.digicert.com" SERVICE_API_TOKEN = "SERVICE_API_TOKEN" CA_ID = "CA_ID" # Query parameters FORMAT = "pem" response = requests.get( f"{BASE_URL}/ca/{CA_ID}/download", headers={"x-api-key": SERVICE_API_TOKEN}, params={"format": FORMAT} ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class ApiExample { public static void main(String[] args) throws Exception { // Configuration String baseUrl = "https://demo.one.digicert.com"; String serviceApiToken = "SERVICE_API_TOKEN"; String caId = "CA_ID"; // Query parameters String format = "pem"; // Send request HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/ca/" + caId + "/download" + "?format=" + format)) .header("x-api-key", serviceApiToken) .GET() .build(); HttpResponse response = client.send( request, HttpResponse.BodyHandlers.ofString() ); System.out.println("Status Code: " + response.statusCode()); System.out.println(response.body()); } } ``` **C#** ```csharp using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // Configuration string baseUrl = "https://demo.one.digicert.com"; string serviceApiToken = "SERVICE_API_TOKEN"; string caId = "CA_ID"; // Query parameters string format = "pem"; // Send request using var client = new HttpClient(); client.DefaultRequestHeaders.Add("x-api-key", serviceApiToken); var response = await client.GetAsync($"{baseUrl}/ca/{caId}/download?format={format}"); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Replace `{id}` with the CA ID from Step 1. Save the CA certificate response to a file named `ca.crt`. **Validate the certificate chain:** ```bash openssl verify -CAfile ca.crt server.crt ``` A successful validation displays: ``` server.crt: OK ``` If your CA is an intermediate CA, you may need to include the full chain: ```bash openssl verify -CAfile root.crt -untrusted ca.crt server.crt ``` You can also view the certificate details: ```bash openssl x509 -in server.crt -text -noout ``` Verify that the `Issuer` field matches your CA's subject and the `Subject` matches your CSR. ## Common errors and solutions For general API errors (authentication, rate limits), see the API error reference. ### invalid_ca_status ```json { "errors": [ { "code": "invalid_ca_status", "message": "CA is not in active status" } ] } ``` The specified CA is not active. Use Step 1 to list CAs and select one with `status` set to `active`. ### invalid_template ```json { "errors": [ { "code": "invalid_template", "message": "Template not found or not active" } ] } ``` The specified template ID is invalid or the template is deactivated. Use Step 2 to list templates and select one with `active` set to `true`. ### invalid_csr ```json { "errors": [ { "code": "invalid_csr", "message": "CSR format is invalid or cannot be parsed" } ] } ``` The CSR is malformed or not properly PEM-encoded. Verify your CSR using `openssl req -text -noout -in server.csr`. Ensure you include the full PEM content including the `-----BEGIN CERTIFICATE REQUEST-----` and `-----END CERTIFICATE REQUEST-----` lines. ### template_constraint_violation ```json { "errors": [ { "code": "template_constraint_violation", "message": "CSR does not meet template requirements" } ] } ``` The CSR attributes do not match template constraints. Check template requirements such as key size, algorithm, or subject field requirements. Regenerate the CSR with compliant values.