Skip to main content
Google Cloud KMS (Key Management Service) provides enterprise-grade security for document signing by keeping the private key in a Hardware Security Module (HSM). The key never leaves Google’s infrastructure.

Why Use Google Cloud KMS?

  • Maximum Security: Private keys stored in FIPS 140-2 Level 3 certified HSMs
  • Compliance: Meets SOC 2, ISO 27001, PCI DSS, and other standards
  • Audit Trail: All signing operations logged in Google Cloud
  • Key Rotation: Automated key rotation without re-signing documents
  • Access Control: Fine-grained IAM permissions

Prerequisites

  • Google Cloud account with billing enabled
  • Cloud KMS API enabled
  • Service account with KMS permissions
  • A certificate for signing (can be self-signed or from a CA)

Setup Steps

1. Enable Cloud KMS API

# Enable the KMS API in your project
gcloud services enable cloudkms.googleapis.com

2. Create a Key Ring and Key

# Set your project ID
export PROJECT_ID="your-project-id"
export LOCATION="us-central1"
export KEYRING_NAME="documenso-signing"
export KEY_NAME="pdf-signing-key"

# Create a key ring
gcloud kms keyrings create $KEYRING_NAME \
  --location $LOCATION \
  --project $PROJECT_ID

# Create an asymmetric signing key (RSA 4096)
gcloud kms keys create $KEY_NAME \
  --keyring $KEYRING_NAME \
  --location $LOCATION \
  --purpose "asymmetric-signing" \
  --default-algorithm "rsa-sign-pkcs1-4096-sha256" \
  --protection-level "hsm" \
  --project $PROJECT_ID
The --protection-level "hsm" flag ensures the key is stored in a Hardware Security Module. This is required for compliance but costs more than software keys.

3. Get the Key Path

The key path format is:
projects/PROJECT_ID/locations/LOCATION/keyRings/KEYRING_NAME/cryptoKeys/KEY_NAME/cryptoKeyVersions/1
Example:
projects/my-project/locations/us-central1/keyRings/documenso-signing/cryptoKeys/pdf-signing-key/cryptoKeyVersions/1
You’ll need this for the NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH environment variable.

4. Create a Service Account

export SERVICE_ACCOUNT_NAME="documenso-signer"

# Create service account
gcloud iam service-accounts create $SERVICE_ACCOUNT_NAME \
  --display-name "Documenso PDF Signer" \
  --project $PROJECT_ID

# Grant KMS signing permission
gcloud kms keys add-iam-policy-binding $KEY_NAME \
  --keyring $KEYRING_NAME \
  --location $LOCATION \
  --member "serviceAccount:$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --role "roles/cloudkms.signerVerifier" \
  --project $PROJECT_ID

# Create and download service account key
gcloud iam service-accounts keys create ~/documenso-kms-key.json \
  --iam-account $SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com \
  --project $PROJECT_ID
Store the documenso-kms-key.json file securely. Anyone with this file can sign documents using your KMS key.

5. Prepare the Certificate

You need a public certificate that corresponds to your KMS key. You can either:
  • Generate a self-signed certificate
  • Obtain a certificate from a Certificate Authority

Option A: Self-Signed Certificate

# Create a certificate signing request (CSR)
openssl req -new -key <(openssl rsa -in /dev/null -out /dev/stdout) \
  -subj "/CN=Documenso/O=Your Organization" \
  -out signing.csr

# Create a self-signed certificate (valid for 10 years)
openssl x509 -req -days 3650 -in signing.csr \
  -signkey <(openssl rsa -in /dev/null -out /dev/stdout) \
  -out signing.crt
Since the actual signing happens with the KMS key, the certificate is only used for embedding metadata in the PDF signature. The private key shown in OpenSSL commands above is not used - it’s just for generating the certificate structure.

Option B: Certificate from a CA

If you need signatures to appear as “trusted” in PDF viewers:
  1. Generate a CSR
  2. Submit it to a CA that’s in the Adobe Approved Trust List (AATL)
  3. Receive your certificate chain

6. Prepare Certificate Files

You need to provide the certificate to Documenso in one of these formats:
  • Single certificate file (PEM format)
  • Certificate chain file (PEM format, includes intermediate certificates)
# If you have a chain, concatenate all certificates:
cat signing.crt intermediate.crt root.crt > chain.pem

Configuration

Documenso supports multiple ways to provide credentials and certificates:

Method 1: File Paths (VM/Server Deployment)

NEXT_PRIVATE_SIGNING_TRANSPORT="gcloud-hsm"
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH="projects/my-project/locations/us-central1/keyRings/documenso-signing/cryptoKeys/pdf-signing-key/cryptoKeyVersions/1"

# Service account credentials
GOOGLE_APPLICATION_CREDENTIALS="/app/credentials/documenso-kms-key.json"

# Certificate (choose ONE)
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_PATH="/app/certs/signing.crt"
# OR for chain:
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_FILE_PATH="/app/certs/chain.pem"

Method 2: Base64-Encoded (Containers/Serverless)

# Convert credentials to base64
base64 -i documenso-kms-key.json | tr -d '\n' > credentials.base64.txt
base64 -i signing.crt | tr -d '\n' > cert.base64.txt
# OR for chain:
base64 -i chain.pem | tr -d '\n' > chain.base64.txt
Configure:
NEXT_PRIVATE_SIGNING_TRANSPORT="gcloud-hsm"
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH="projects/my-project/locations/us-central1/keyRings/documenso-signing/cryptoKeys/pdf-signing-key/cryptoKeyVersions/1"

# Service account credentials (base64-encoded)
GOOGLE_APPLICATION_CREDENTIALS="/tmp/documenso-creds.json"
NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS="ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIs..."

# Certificate (choose ONE)
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_CONTENTS="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t..."
# OR for chain:
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_CONTENTS="LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t..."
When using NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS, the file specified in GOOGLE_APPLICATION_CREDENTIALS will be created automatically if it doesn’t exist.

Method 3: Google Secret Manager (Advanced)

Store the certificate in Google Secret Manager:
# Create secret
echo -n "$(cat chain.pem)" | gcloud secrets create documenso-signing-cert \
  --data-file=- \
  --project $PROJECT_ID

# Grant access to service account
gcloud secrets add-iam-policy-binding documenso-signing-cert \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor" \
  --project $PROJECT_ID
Configure:
NEXT_PRIVATE_SIGNING_TRANSPORT="gcloud-hsm"
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH="projects/my-project/locations/us-central1/keyRings/documenso-signing/cryptoKeys/pdf-signing-key/cryptoKeyVersions/1"
GOOGLE_APPLICATION_CREDENTIALS="/app/credentials/documenso-kms-key.json"
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_SECRET_MANAGER_CERT_PATH="projects/my-project/secrets/documenso-signing-cert/versions/latest"

Environment Variables Reference

NEXT_PRIVATE_SIGNING_TRANSPORT
string
required
Set to "gcloud-hsm" to use Google Cloud KMS signing.
NEXT_PRIVATE_SIGNING_TRANSPORT="gcloud-hsm"
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH
string
required
Full path to the Google Cloud KMS key version.Format: projects/PROJECT_ID/locations/LOCATION/keyRings/KEYRING/cryptoKeys/KEY/cryptoKeyVersions/VERSION
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH="projects/my-project/locations/us-central1/keyRings/documenso-signing/cryptoKeys/pdf-signing-key/cryptoKeyVersions/1"
GOOGLE_APPLICATION_CREDENTIALS
string
required
Path to the service account JSON key file.
GOOGLE_APPLICATION_CREDENTIALS="/app/credentials/documenso-kms-key.json"
NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS
string
Base64-encoded contents of the service account JSON file. If provided, the file will be created at the path specified in GOOGLE_APPLICATION_CREDENTIALS.
NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS="ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIs..."

Certificate Configuration (Choose ONE method)

NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_PATH
string
Path to the public certificate file (PEM format).
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_PATH="/app/certs/signing.crt"
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_CONTENTS
string
Base64-encoded contents of the public certificate file.
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_CONTENTS="LS0tLS1CRUdJTi..."
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_FILE_PATH
string
Path to the certificate chain file (PEM format). Use this instead of the single certificate if you have intermediate certificates.
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_FILE_PATH="/app/certs/chain.pem"
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_CONTENTS
string
Base64-encoded contents of the certificate chain file.
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_CONTENTS="LS0tLS1CRUdJTi..."
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_SECRET_MANAGER_CERT_PATH
string
Path to retrieve the certificate from Google Secret Manager.Format: projects/PROJECT_ID/secrets/SECRET_NAME/versions/VERSION
NEXT_PRIVATE_SIGNING_GCLOUD_HSM_SECRET_MANAGER_CERT_PATH="projects/my-project/secrets/documenso-signing-cert/versions/latest"

Complete Configuration Examples

docker-compose.yml
version: '3.8'
services:
  documenso:
    image: documenso/documenso:latest
    environment:
      NEXT_PRIVATE_SIGNING_TRANSPORT: gcloud-hsm
      NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH: projects/my-project/locations/us-central1/keyRings/documenso-signing/cryptoKeys/pdf-signing-key/cryptoKeyVersions/1
      GOOGLE_APPLICATION_CREDENTIALS: /app/credentials/documenso-kms-key.json
      NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_FILE_PATH: /app/certs/chain.pem
    volumes:
      - ./documenso-kms-key.json:/app/credentials/documenso-kms-key.json:ro
      - ./chain.pem:/app/certs/chain.pem:ro

Cost Considerations

Google Cloud KMS pricing (as of 2024):
  • HSM key storage: ~$2.50/month per key version
  • Signing operations: ~$0.03 per 10,000 operations
  • Secret Manager (if used): ~$0.06 per 10,000 accesses
For most deployments, the cost is minimal. Signing 100,000 documents per month would cost approximately $3/month for KMS operations.

Troubleshooting

”No certificate found for Google Cloud HSM signing”

Cause: None of the certificate configuration methods are set. Solution: Provide the certificate using one of these:
  • NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_PATH or NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_CONTENTS
  • NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_FILE_PATH or NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_CONTENTS
  • NEXT_PRIVATE_SIGNING_GCLOUD_HSM_SECRET_MANAGER_CERT_PATH

”No key path provided for Google Cloud HSM signing”

Cause: NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH is not set. Solution: Set the full path to your KMS key version.

”Permission denied” errors

Cause: Service account doesn’t have the required permissions. Solution: Ensure the service account has the roles/cloudkms.signerVerifier role:
gcloud kms keys add-iam-policy-binding $KEY_NAME \
  --keyring $KEYRING_NAME \
  --location $LOCATION \
  --member "serviceAccount:$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --role "roles/cloudkms.signerVerifier"

“Invalid certificate chain”

Cause: Certificate chain is in the wrong order or incomplete. Solution: Ensure certificates are ordered correctly in the chain file:
# Correct order:
# 1. Your signing certificate
# 2. Intermediate CA certificate(s)
# 3. Root CA certificate

cat signing.crt intermediate.crt root.crt > chain.pem

Security Best Practices

1

Use HSM Protection Level

Always create keys with --protection-level "hsm" for compliance requirements.
2

Limit Service Account Permissions

Only grant the minimum required permissions (roles/cloudkms.signerVerifier).
3

Enable Audit Logging

Monitor all KMS operations in Cloud Logging:
gcloud logging read "resource.type=cloudkms_cryptokey" --project $PROJECT_ID
4

Rotate Service Account Keys

Regularly rotate service account keys (recommended: every 90 days).
5

Use Secret Manager for Sensitive Data

Store certificates and credentials in Google Secret Manager instead of environment variables.

Next Steps

Environment Variables

View all configuration options

Storage Setup

Configure document storage