--- title: "Create a keypair and sign a file hash" description: "Cryptographically prove the authenticity and integrity of your software artifacts.\n" source_url: https://dev.digicert.com/software-trust-api/tutorials/create-a-keypair-and-sign-a-file-hash.html --- Keypairs are the foundation of code signing in Software Trust Manager. A keypair consists of a private key securely stored in an HSM (Hardware Security Module) and a corresponding public key used to verify signatures. By signing file hashes with your keypair, you cryptographically prove the authenticity and integrity of your software artifacts. In this tutorial, you will: - Create a signing keypair in Software Trust Manager. - Sign a file hash using your new keypair. - Verify the signature was recorded successfully. ## Before you begin Before you begin, make sure you have: - A DigiCert ONE account with Software Trust Manager access. - A user account with one of the following roles: - `SSM_SIGNER` - Sign files with assigned keypairs - `SSM_BUILD_ENGINEER` - Sign files as part of build processes - A client authentication certificate (.p12 file) for mTLS authentication. - OpenSSL installed for generating file hashes. Use `openssl version` to verify. ## Endpoint overview | Method | Path | Description | |--------|------|-------------| | POST | /api/v1/keypairs | Create a new signing keypair | | POST | /api/v1/keypairs/{keypair_id}/sign | Sign a file hash | | GET | /api/v1/signatures/{signature_id} | Get signature details | ## Key algorithms Select a key algorithm based on your signing requirements: | Algorithm | Description | Use case | |-----------|-------------|----------| | `RSA` | RSA algorithm, requires `key_size` property | Windows Authenticode, general signing | | `ECDSA` | Elliptic Curve DSA, requires `curve` property | Modern applications, smaller signatures | | `EdDSA` | Edwards-curve DSA, requires `curve` property | High-performance signing | ## Signature algorithms Select a signature algorithm compatible with your key algorithm: | Key Algorithm | Signature Algorithms | |---------------|---------------------| | RSA | `SHA256WithRSA`, `SHA384WithRSA`, `SHA512WithRSA` | | ECDSA | `SHA256WithECDSA`, `SHA384WithECDSA`, `SHA512WithECDSA` | | EdDSA | `ED25519` | ## Step 1: Create a signing keypair Create a keypair that will be used to sign your files. The private key is generated and stored securely in Software Trust Manager—it never leaves the HSM. **Request:** **curl** ```bash curl -X POST "https://clientauth.demo.one.digicert.com/signingmanager/api/v1/keypairs" \ --cert client.crt \ --key client.key \ -H "Content-Type: application/json" \ -d '{ "alias": "my_new_keypair", "key_alg": "KEY_ALG", "key_storage": "KEY_STORAGE", "key_type": "KEY_TYPE", "properties": { "key_size": 0, "curve": "curve_value" }, "status": "STATUS" }' | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://clientauth.demo.one.digicert.com" CLIENT_CERT = "client.crt" CLIENT_KEY = "client.key" # Request details ALIAS = "my_new_keypair" KEY_ALG = "KEY_ALG" KEY_STORAGE = "KEY_STORAGE" KEY_TYPE = "KEY_TYPE" STATUS = "STATUS" payload = { "alias": ALIAS, "key_alg": KEY_ALG, "key_storage": KEY_STORAGE, "key_type": KEY_TYPE, "properties": {"key_size": 0, "curve": "curve_value"}, "status": STATUS, } response = requests.post( f"{BASE_URL}/signingmanager/api/v1/keypairs", headers={"Content-Type": "application/json"}, cert=(CLIENT_CERT, CLIENT_KEY), json=payload ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.io.FileInputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.security.KeyStore; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; 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://clientauth.demo.one.digicert.com"; String certFile = "client.p12"; String certPassword = "CERT_PASSWORD"; // Request details String alias = "my_new_keypair"; String keyAlg = "KEY_ALG"; String keyStorage = "KEY_STORAGE"; String keyType = "KEY_TYPE"; String status = "STATUS"; // Build payload ObjectMapper mapper = new ObjectMapper(); ObjectNode payload = mapper.createObjectNode(); payload.put("alias", alias); payload.put("key_alg", keyAlg); payload.put("key_storage", keyStorage); payload.put("key_type", keyType); ObjectNode propertiesNode = mapper.createObjectNode(); propertiesNode.put("key_size", 0); propertiesNode.put("curve", "curve_value"); payload.set("properties", propertiesNode); payload.put("status", status); // Load PKCS12 certificate for mTLS KeyStore keyStore = KeyStore.getInstance("PKCS12"); try (FileInputStream fis = new FileInputStream(certFile)) { keyStore.load(fis, certPassword.toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, certPassword.toCharArray()); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), null, null); // Send request HttpClient client = HttpClient.newBuilder() .sslContext(sslContext) .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/signingmanager/api/v1/keypairs")) .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.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Json.Nodes; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // Configuration string baseUrl = "https://clientauth.demo.one.digicert.com"; string certFile = "client.p12"; string certPassword = "CERT_PASSWORD"; // Request details string alias = "my_new_keypair"; string keyAlg = "KEY_ALG"; string keyStorage = "KEY_STORAGE"; string keyType = "KEY_TYPE"; string status = "STATUS"; // Build payload var payload = new JsonObject { ["alias"] = alias, ["key_alg"] = keyAlg, ["key_storage"] = keyStorage, ["key_type"] = keyType, ["properties"] = new JsonObject { ["key_size"] = 0, ["curve"] = "curve_value" }, ["status"] = status }; // Send request var certificate = new X509Certificate2(certFile, certPassword); var handler = new HttpClientHandler(); handler.ClientCertificates.Add(certificate); using var client = new HttpClient(handler); var content = new StringContent( payload.ToJsonString(), Encoding.UTF8, "application/json" ); var response = await client.PostAsync($"{baseUrl}/signingmanager/api/v1/keypairs", content); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Configure the keypair with these fields: - `alias` - A unique name for your keypair (e.g., `my-signing-key`) - `key_alg` - Algorithm: `RSA`, `ECDSA`, or `EdDSA` - `key_storage` - Storage location: `HSM` (recommended) or `DISK` - `key_type` - Purpose: `PRODUCTION` or `TEST` - `properties` - Algorithm-specific settings (see example below) - `status` - Availability: `ONLINE` (ready to sign) or `OFFLINE` For an RSA keypair, set `properties` to include `key_size`: ```json { "properties": { "key_size": 2048 } } ``` For an ECDSA keypair, set `properties` to include `curve`: ```json { "properties": { "curve": "P-256" } } ``` **Successful response (201 Created):** ```json { "id": "01234567-89ab-cdef-0123-456789abcdef", "status": "active", "_note": "Response schema not fully documented in API spec" } ``` From the response, save the keypair `id`. You will need this in Step 2 when signing your file hash. ## Step 2: Sign a file hash Before signing, you need to compute the hash of your file. Software Trust Manager signs the hash, not the file itself. This approach is more efficient and secure—your file content never leaves your environment. **Generate the file hash:** ```bash openssl dgst -sha256 -binary myfile.exe | openssl base64 ``` This outputs a base64-encoded SHA-256 hash of your file. **Request:** **curl** ```bash curl -X POST "https://clientauth.demo.one.digicert.com/signingmanager/api/v1/keypairs/KEYPAIR_ID/sign" \ --cert client.crt \ --key client.key \ -H "Content-Type: application/json" \ -d '{ "hash": "eeUnw3ky7+hI7Kawc9NnZ4CCCenu8rDDfV+YTwNutt+JHXX3LZsVRRjBzViDUobR", "sig_alg": "SHA3-512WithRSA/PSS" }' | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://clientauth.demo.one.digicert.com" CLIENT_CERT = "client.crt" CLIENT_KEY = "client.key" KEYPAIR_ID = "KEYPAIR_ID" # Request details HASH = "eeUnw3ky7+hI7Kawc9NnZ4CCCenu8rDDfV+YTwNutt+JHXX3LZsVRRjBzViDUobR" SIG_ALG = "SHA3-512WithRSA/PSS" payload = { "hash": HASH, "sig_alg": SIG_ALG, } response = requests.post( f"{BASE_URL}/signingmanager/api/v1/keypairs/{KEYPAIR_ID}/sign", headers={"Content-Type": "application/json"}, cert=(CLIENT_CERT, CLIENT_KEY), json=payload ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.io.FileInputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.security.KeyStore; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; 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://clientauth.demo.one.digicert.com"; String certFile = "client.p12"; String certPassword = "CERT_PASSWORD"; String keypairId = "KEYPAIR_ID"; // Request details String hash = "eeUnw3ky7+hI7Kawc9NnZ4CCCenu8rDDfV+YTwNutt+JHXX3LZsVRRjBzViDUobR"; String sigAlg = "SHA3-512WithRSA/PSS"; // Build payload ObjectMapper mapper = new ObjectMapper(); ObjectNode payload = mapper.createObjectNode(); payload.put("hash", hash); payload.put("sig_alg", sigAlg); // Load PKCS12 certificate for mTLS KeyStore keyStore = KeyStore.getInstance("PKCS12"); try (FileInputStream fis = new FileInputStream(certFile)) { keyStore.load(fis, certPassword.toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, certPassword.toCharArray()); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), null, null); // Send request HttpClient client = HttpClient.newBuilder() .sslContext(sslContext) .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/signingmanager/api/v1/keypairs/" + keypairId + "/sign")) .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.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Json.Nodes; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // Configuration string baseUrl = "https://clientauth.demo.one.digicert.com"; string certFile = "client.p12"; string certPassword = "CERT_PASSWORD"; string keypairId = "KEYPAIR_ID"; // Request details string hash = "eeUnw3ky7+hI7Kawc9NnZ4CCCenu8rDDfV+YTwNutt+JHXX3LZsVRRjBzViDUobR"; string sigAlg = "SHA3-512WithRSA/PSS"; // Build payload var payload = new JsonObject { ["hash"] = hash, ["sig_alg"] = sigAlg }; // Send request var certificate = new X509Certificate2(certFile, certPassword); var handler = new HttpClientHandler(); handler.ClientCertificates.Add(certificate); using var client = new HttpClient(handler); var content = new StringContent( payload.ToJsonString(), Encoding.UTF8, "application/json" ); var response = await client.PostAsync($"{baseUrl}/signingmanager/api/v1/keypairs/{keypairId}/sign", content); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Replace `{keypair_id}` with the keypair ID from Step 1. Configure the signing request with: - `hash` - The base64-encoded file hash from the OpenSSL command - `sig_alg` - Signature algorithm matching your key type (e.g., `SHA256WithRSA`) **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 signature `id` and the `signature` value. The `id` is used in Step 3 to verify the signature was recorded. The `signature` value is the cryptographic signature you can use to verify your file. ## Step 3: Verify the signature Retrieve the signature record to confirm it was created successfully and review the signing details. **Request:** **curl** ```bash curl -X GET "https://clientauth.demo.one.digicert.com/signingmanager/api/v1/signatures/SIGNATURE_ID" \ --cert client.crt \ --key client.key | jq '.' ``` **python** ```python import requests # Configuration BASE_URL = "https://clientauth.demo.one.digicert.com" CLIENT_CERT = "client.crt" CLIENT_KEY = "client.key" SIGNATURE_ID = "SIGNATURE_ID" response = requests.get( f"{BASE_URL}/signingmanager/api/v1/signatures/{SIGNATURE_ID}", cert=(CLIENT_CERT, CLIENT_KEY) ) print(f"Status Code: {response.status_code}") print(response.json()) ``` **Java** ```java import java.io.FileInputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.security.KeyStore; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; public class ApiExample { public static void main(String[] args) throws Exception { // Configuration String baseUrl = "https://clientauth.demo.one.digicert.com"; String certFile = "client.p12"; String certPassword = "CERT_PASSWORD"; String signatureId = "SIGNATURE_ID"; // Load PKCS12 certificate for mTLS KeyStore keyStore = KeyStore.getInstance("PKCS12"); try (FileInputStream fis = new FileInputStream(certFile)) { keyStore.load(fis, certPassword.toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, certPassword.toCharArray()); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), null, null); // Send request HttpClient client = HttpClient.newBuilder() .sslContext(sslContext) .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/signingmanager/api/v1/signatures/" + signatureId)) .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.Security.Cryptography.X509Certificates; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // Configuration string baseUrl = "https://clientauth.demo.one.digicert.com"; string certFile = "client.p12"; string certPassword = "CERT_PASSWORD"; string signatureId = "SIGNATURE_ID"; // Send request var certificate = new X509Certificate2(certFile, certPassword); var handler = new HttpClientHandler(); handler.ClientCertificates.Add(certificate); using var client = new HttpClient(handler); var response = await client.GetAsync($"{baseUrl}/signingmanager/api/v1/signatures/{signatureId}"); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Status Code: {(int)response.StatusCode}"); Console.WriteLine(responseBody); } } ``` Replace `{signature_id}` with the signature ID from Step 2. **Successful response (200 OK):** ```json { "id": "01234567-89ab-cdef-0123-456789abcdef", "keypair_id": "01234567-89ab-cdef-0123-456789abcdef", "key_pair": "KEY_PAIR_VALUE", "signature": "...(signature string)...", "sig_alg": "SIG_ALG_VALUE", "hash": "eeUnw3ky7+hI7Kawc9NnZ4CCCenu8rDDfV+YTwNutt+JHXX3LZsVRRjBzViDUobR", "signing_status": "SIGNING_STATUS_VALUE", "account": [ "01234567-89ab-cdef-0123-456789abcdef" ], "created_on": "CREATED_ON_VALUE", "created_by": "CREATED_BY_VALUE", "created_by_user": "CREATED_BY_USER_VALUE", "modified_on": "MODIFIED_ON_VALUE", "modified_by": "MODIFIED_BY_VALUE", "modified_by_user": "MODIFIED_BY_USER_VALUE", "client_ip": "211.22.33.44", "certificate_id": "01234567-89ab-cdef-0123-456789abcdef", "client_type": "SigningManager-PKCS11/1.31.0 (linux; amd64)", "signature_type": "SIGNATURE_TYPE_VALUE" } ``` Verify the response shows `signing_status` as `SUCCESS`. The response also includes: - `keypair_id` - Confirms which keypair was used - `sig_alg` - The signature algorithm used - `hash` - The original hash that was signed - `created_on` - Timestamp of when the signature was created - `signature` - The cryptographic signature value ## Common errors and solutions For general API errors (authentication, rate limits), see the API error reference. ### Invalid key algorithm ```json { "errors": [ { "code": "INVALID_KEY_ALG", "message": "Invalid key algorithm specified" } ] } ``` The `key_alg` value is not valid. Use one of: `RSA`, `ECDSA`, or `EdDSA`. ### Missing required properties ```json { "errors": [ { "code": "INVALID_PROPERTIES", "message": "Key properties are required for the specified algorithm" } ] } ``` You must include the `properties` object with algorithm-specific settings. RSA requires `key_size`, while ECDSA and EdDSA require `curve`. ### Signature algorithm mismatch ```json { "errors": [ { "code": "INVALID_SIG_ALG", "message": "Signature algorithm is not compatible with keypair algorithm" } ] } ``` The signature algorithm must match the keypair's key algorithm. For example, use `SHA256WithRSA` with RSA keys, not `SHA256WithECDSA`. ### Keypair not found or not accessible ```json { "errors": [ { "code": "KEYPAIR_NOT_FOUND", "message": "Keypair not found or you do not have access" } ] } ``` The keypair ID is invalid or you don't have permission to use it for signing. Verify the ID and ensure your user has the `SSM_SIGNER` or `SSM_BUILD_ENGINEER` role.