Hash sign a document with Content Signing Service

Use DigiCert Content Signing Service with Content Trust Manager and Cloud Signature Consortium (CSC) APIs to sign a PDF or XML document by hash.

Use DigiCert Content Signing Service with Content Trust Manager and Cloud Signature Consortium (CSC) APIs to sign a PDF or XML document by hash.

In this workflow, Content Signing Service prepares the document and creates the hash. Content Trust Manager authorizes the signing credential and signs the hash. Content Signing Service then embeds the returned signature into the original document.

Before you begin

Make sure you have:

A DigiCert ONE account with access to Content Trust Manager.
An active signing credential.
An API token, or OAuth client credentials if your integration uses OAuth.
Go>Sign Mobile configured for signing authorization.
Content Signing Service running locally or in your signing environment.
A PDF or XML document to sign.
A timestamp authority URL if your signature profile requires timestamping.

URLs and variables

The examples use shell variables so you can paste each request into a terminal after setting your own values.

export DCONE_BASE_URL="https://demo.one.digicert.com"
export DTM_BASE_URL="$DCONE_BASE_URL/documentmanager"
export CSS_BASE_URL="http://localhost:8080"
export API_TOKEN="YOUR_API_TOKEN"
export CREDENTIAL_ID="YOUR_CREDENTIAL_ID"
export INPUT_FILE="./sample.pdf"
export OUTPUT_FILE="./sample-signed.pdf"
export TSA_URL="http://ts.quovadisglobal.com/eu"

Use your production DigiCert ONE URL in production. If your Content Signing Service is not running on localhost:8080, update CSS_BASE_URL.

The DigiCert-supplied Postman collection defines the exact Content Signing Service routes for document registration, hash creation, and signature embedding. The examples below use configurable route variables for those local service calls:

export CSS_REGISTER_DOCUMENT_PATH="/api/v1/document"
export CSS_CREATE_HASH_PATH="/api/v1/document/{document_id}"
export CSS_EMBED_SIGNATURE_PATH="/api/v1/document/{document_id}/signature"

If your Postman collection uses different paths, keep the request flow and payloads but replace these path values.

Authentication

For API-token authentication, send the API token with DigiCert ONE requests:

x-api-key: YOUR_API_TOKEN
Content-Type: application/json

For OAuth authentication, send the access token instead:

Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

The code samples below use API-token authentication.

Optional: start Content Signing Service

Run Content Signing Service before you register documents or create hashes.

docker volume create --name=content-signing-service-data
docker volume create --name=content-signing-service-logs
docker run -d \
  -e API_KEY="$API_TOKEN" \
  -e DTM_BASE_URL="$DTM_BASE_URL" \
  -v content-signing-service-data:/app/db \
  -v content-signing-service-logs:/app/logs \
  -p 8080:8080 \
  --name content-signing-service \
  repo.pkiplatform.digicert.com/dcone/documentmanager/clients/content-signing-service:latest

Workflow

  1. List signing credentials.
  2. Get credential information.
  3. Register the document.
  4. Generate the document hash.
  5. Authorize the credential and obtain Signature Activation Data (SAD).
  6. Sign the hash.
  7. Embed the signature into the document.
  8. Download and validate the signed document.

Step 1: List signing credentials

Retrieve the credentials available to your account and choose the credentialID you want to use for signing.

curl --request POST "$DTM_BASE_URL/api/v1/credentials/list" \
  --header "x-api-key: $API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{}'

Example response:

{
  "credentialIDs": [
    "d9f6f7f8-1111-2222-3333-0a1b2c3d4e5f"
  ]
}

Save one value as CREDENTIAL_ID:

export CREDENTIAL_ID="d9f6f7f8-1111-2222-3333-0a1b2c3d4e5f"

Step 2: Get credential information

Retrieve details for the selected credential, including the certificate chain and supported signing algorithms.

curl --request POST "$DTM_BASE_URL/api/v1/credentials/info" \
  --header "x-api-key: $API_TOKEN" \
  --header "Content-Type: application/json" \
  --data "{
    \"credentialID\": \"$CREDENTIAL_ID\",
    \"certificates\": \"chain\",
    \"certInfo\": true,
    \"authInfo\": true
  }"

Example response fields to save:

{
  "key": {
    "algo": ["1.2.840.113549.1.1.1"],
    "len": 3072
  },
  "cert": {
    "certificates": ["BASE64_CERT", "BASE64_INTERMEDIATE", "BASE64_ROOT"]
  },
  "SCAL": "2",
  "multisign": 1
}

Use a signing algorithm supported by the credential. For RSA with SHA-256, the signing algorithm is commonly represented as:

export SIGN_ALGO="1.2.840.113549.1.1.11"
export HASH_ALGO="2.16.840.1.101.3.4.2.1"

Step 3: Register the document

Register the PDF or XML document with Content Signing Service before creating the hash.

For a visible PDF signature, include the signature rectangle and display text. Coordinates are measured in PDF points from the lower-left corner of the page.

REGISTER_URL="$CSS_BASE_URL$CSS_REGISTER_DOCUMENT_PATH"

curl --request POST "$REGISTER_URL" \
  --header "Content-Type: application/json" \
  --data "{
    \"document_type\": \"PDF\",
    \"signature_profile\": \"PAdES-B-T\",
    \"tsa_url\": \"$TSA_URL\",
    \"signature_rectangle\": {
      \"page\": 1,
      \"x\": 72,
      \"y\": 72,
      \"w\": 220,
      \"h\": 80
    },
    \"signature_text1\": \"Digitally signed by Example Signer\",
    \"signature_text2\": \"Reason: Document approval\",
    \"reason\": \"Document approval\",
    \"contact\": \"signer@example.com\",
    \"type\": \"NOT_CERTIFIED\",
    \"preservation_size\": 16384
  }"

Example response:

{
  "document_id": "8d845ce8-2222-4444-8888-5f9d6b0c12a1"
}

Save the document_id:

export DOCUMENT_ID="8d845ce8-2222-4444-8888-5f9d6b0c12a1"

For XML signing, register the document with XML settings instead of PDF appearance settings:

{
  "document_type": "XML",
  "signature_profile": "XAdES-B-T",
  "canonicalization_method": "http://www.w3.org/2001/10/xml-exc-c14n#",
  "xades_signature_type": "ENVELOPED",
  "tsa_url": "http://ts.quovadisglobal.com/eu"
}

Step 4: Generate the document hash

Upload the registered document to Content Signing Service. The service prepares the signature container and returns the hash that must be signed.

CREATE_HASH_PATH="${CSS_CREATE_HASH_PATH/\{document_id\}/$DOCUMENT_ID}"
CREATE_HASH_URL="$CSS_BASE_URL$CREATE_HASH_PATH"

curl --request POST "$CREATE_HASH_URL" \
  --form "file=@$INPUT_FILE"

Example response:

{
  "document_id": "8d845ce8-2222-4444-8888-5f9d6b0c12a1",
  "hash": "BASE64_DOCUMENT_HASH",
  "hash_algorithm": "2.16.840.1.101.3.4.2.1",
  "signing_algorithm": "1.2.840.113549.1.1.11"
}

Save the returned values:

export DOCUMENT_HASH="BASE64_DOCUMENT_HASH"
export HASH_ALGO="2.16.840.1.101.3.4.2.1"
export SIGN_ALGO="1.2.840.113549.1.1.11"

Step 5: Authorize the credential

Authorize the credential to sign the hash. This request starts the Go>Sign Mobile authorization flow.

curl --request POST "$DTM_BASE_URL/api/v1/credential/authorize" \
  --header "x-api-key: $API_TOKEN" \
  --header "Content-Type: application/json" \
  --data "{
    \"credentialID\": \"$CREDENTIAL_ID\",
    \"numSignatures\": 1,
    \"hash\": [
      \"$DOCUMENT_HASH\"
    ],
    \"description\": \"Sign $INPUT_FILE\"
  }"

Approve the authorization request in Go>Sign Mobile.

Example response:

{
  "SAD": "BASE64_SIGNATURE_ACTIVATION_DATA",
  "expiresIn": 3600
}

Save the SAD:

export SAD="BASE64_SIGNATURE_ACTIVATION_DATA"

Step 6: Sign the hash

Submit the hash, signing algorithm, credential ID, and SAD to generate the digital signature.

curl --request POST "$DTM_BASE_URL/api/v1/signatures/signHash" \
  --header "x-api-key: $API_TOKEN" \
  --header "Content-Type: application/json" \
  --data "{
    \"credentialID\": \"$CREDENTIAL_ID\",
    \"SAD\": \"$SAD\",
    \"hash\": [
      \"$DOCUMENT_HASH\"
    ],
    \"hashAlgo\": \"$HASH_ALGO\",
    \"signAlgo\": \"$SIGN_ALGO\"
  }"

Example response:

{
  "signatures": [
    "BASE64_SIGNATURE_VALUE"
  ]
}

Save the returned signature:

export SIGNATURE="BASE64_SIGNATURE_VALUE"

Step 7: Embed the signature

Send the signature back to Content Signing Service so it can embed the signature into the prepared document.

EMBED_SIGNATURE_PATH="${CSS_EMBED_SIGNATURE_PATH/\{document_id\}/$DOCUMENT_ID}"
EMBED_SIGNATURE_URL="$CSS_BASE_URL$EMBED_SIGNATURE_PATH"

curl --request POST "$EMBED_SIGNATURE_URL" \
  --header "Content-Type: application/json" \
  --output "$OUTPUT_FILE" \
  --data "{
    \"signature\": \"$SIGNATURE\",
    \"hash\": \"$DOCUMENT_HASH\",
    \"hash_algorithm\": \"$HASH_ALGO\",
    \"signing_algorithm\": \"$SIGN_ALGO\",
    \"sad\": \"$SAD\"
  }"

The response body is the signed PDF or XML document. Save it as your final signed file.

Verify the results

For PDF documents, open the signed file in Adobe Acrobat Reader and verify that the signature is valid.

For XML documents, use an XML signature validation tool that supports your selected XAdES profile.

xmlsec1 --verify "$OUTPUT_FILE"

Next steps

Reauthorization

Go>Sign Mobile authorization remains valid until the configured authorization limit is reached.

EnvironmentAuthorization limit
Demo250,000 hashes or 30 days
Production1,000,000 hashes or 95 days

After authorization expires, repeat the authorization step before signing more documents.

Demo environment trust

Documents signed in the DigiCert ONE demo environment may appear untrusted because demo certificates are not trusted by default.

To display a trusted signature in Adobe Acrobat Reader, import and trust the demo certificate chain.