--- title: "Hash sign a document with Content Signing Service" description: "Use DigiCert Content Signing Service with Content Trust Manager and Cloud Signature Consortium (CSC) APIs to sign a PDF or XML document by hash.\n" source_url: https://dev.digicert.com/content-trust-api/tutorials/hash-sign-a-document-with-content-signing-service.html --- 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. ```bash 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: ```bash 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: ```http x-api-key: YOUR_API_TOKEN Content-Type: application/json ``` For OAuth authentication, send the access token instead: ```http 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. ```bash 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. 1. Get credential information. 1. Register the document. 1. Generate the document hash. 1. Authorize the credential and obtain Signature Activation Data (SAD). 1. Sign the hash. 1. Embed the signature into the document. 1. 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. ```bash curl --request POST "$DTM_BASE_URL/api/v1/credentials/list" \ --header "x-api-key: $API_TOKEN" \ --header "Content-Type: application/json" \ --data '{}' ``` Example response: ```json { "credentialIDs": [ "d9f6f7f8-1111-2222-3333-0a1b2c3d4e5f" ] } ``` Save one value as `CREDENTIAL_ID`: ```bash 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. ```bash 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: ```json { "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: ```bash 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. ```bash 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: ```json { "document_id": "8d845ce8-2222-4444-8888-5f9d6b0c12a1" } ``` Save the `document_id`: ```bash export DOCUMENT_ID="8d845ce8-2222-4444-8888-5f9d6b0c12a1" ``` For XML signing, register the document with XML settings instead of PDF appearance settings: ```json { "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. ```bash 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: ```json { "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: ```bash 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. ```bash 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: ```json { "SAD": "BASE64_SIGNATURE_ACTIVATION_DATA", "expiresIn": 3600 } ``` Save the SAD: ```bash 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. ```bash 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: ```json { "signatures": [ "BASE64_SIGNATURE_VALUE" ] } ``` Save the returned signature: ```bash 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. ```bash 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. ```bash xmlsec1 --verify "$OUTPUT_FILE" ``` ## Next steps ### Reauthorization Go>Sign Mobile authorization remains valid until the configured authorization limit is reached. | Environment | Authorization limit | | --- | --- | | Demo | 250,000 hashes or 30 days | | Production | 1,000,000 hashes or 95 days | After authorization expires, repeat the authorization step before signing more documents. ### Demo environment trust > **Note** > > 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.