Create a keypair and sign a file hash
9 minute read
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 user account with one of the following roles:
SSM_SIGNER- Sign files with assigned keypairsSSM_BUILD_ENGINEER- Sign files as part of build processes
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 -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 '.'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())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<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status Code: " + response.statusCode());
System.out.println(response.body());
}
}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, orEdDSAkey_storage- Storage location:HSM(recommended) orDISKkey_type- Purpose:PRODUCTIONorTESTproperties- Algorithm-specific settings (see example below)status- Availability:ONLINE(ready to sign) orOFFLINE
For an RSA keypair, set properties to include key_size:
{
"properties": {
"key_size": 2048
}
}
For an ECDSA keypair, set properties to include curve:
{
"properties": {
"curve": "P-256"
}
}
Successful response (201 Created):
{
"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:
openssl dgst -sha256 -binary myfile.exe | openssl base64
This outputs a base64-encoded SHA-256 hash of your file.
Request:
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 '.'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())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<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status Code: " + response.statusCode());
System.out.println(response.body());
}
}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 commandsig_alg- Signature algorithm matching your key type (e.g.,SHA256WithRSA)
Successful response (200 OK):
{
"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 -X GET "https://clientauth.demo.one.digicert.com/signingmanager/api/v1/signatures/SIGNATURE_ID" \
--cert client.crt \
--key client.key | jq '.'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())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<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status Code: " + response.statusCode());
System.out.println(response.body());
}
}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):
{
"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 usedsig_alg- The signature algorithm usedhash- The original hash that was signedcreated_on- Timestamp of when the signature was createdsignature- The cryptographic signature value
Common errors and solutions
For general API errors (authentication, rate limits), see the API error reference.
Invalid key algorithm
{
"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
{
"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
{
"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
{
"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.