Skip to main content

Sign Documents with Dilithium3

This guide shows you how to create post-quantum digital signatures using Qpher Dilithium3 (ML-DSA-65, NIST FIPS 204), producing quantum-resistant signatures for document integrity and authenticity.

Prerequisites​

  • A Qpher account with an active API key
  • At least one active Dilithium3 key pair (see Key Management)
  • The key_version of the active Dilithium3 key you want to use

How Dilithium3 Signing Works​

Dilithium3 is a lattice-based digital signature scheme standardized by NIST as ML-DSA-65 (FIPS 204):

  1. Your message is sent to the Qpher API (base64-encoded)
  2. The KMS-Orchestrator signs the message using your private key inside the secure enclave
  3. A 3,293-byte signature is returned — your private key never leaves the enclave
Private Key Protection

Your Dilithium3 private key (4,000 bytes) is stored encrypted with AES-256-GCM inside the KMS-Orchestrator. It never leaves the secure enclave, and no API endpoint can export it. All signing operations happen server-side.

Step 1: Prepare Your Message​

Base64-encode the content you want to sign. This can be a document, a hash, a JSON payload, or any binary data.

import base64
import hashlib

# Option A: Sign the raw document
message = base64.b64encode(document_bytes).decode()

# Option B: Sign a SHA-256 hash of the document (recommended for large files)
doc_hash = hashlib.sha256(document_bytes).digest()
message = base64.b64encode(doc_hash).decode()

Step 2: Send the Sign Request​

POST/api/v1/signature/signSign a message using Dilithium3 digital signatures
Sign a Document
curl -X POST https://api.qpher.ai/api/v1/signature/sign \
  -H "Content-Type: application/json" \
  -H "x-api-key: qph_your_key_here" \
  -d '{
    "message": "SGVsbG8sIFdvcmxkIQ==",
    "key_version": 1
  }'

Step 3: Understand the Response​

RequestPOST/api/v1/signature/sign
Content-Type: application/json
x-api-key: qph_your_key_here
{
  "message": "SGVsbG8sIFdvcmxkIQ==",
  "key_version": 1
}
Response200
{
  "data": {
    "signature": "base64-encoded-signature-3293-bytes...",
    "key_version": 1,
    "algorithm": "Dilithium3"
  },
  "request_id": "770e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-02-15T10:32:00Z"
}
FieldDescription
signatureBase64-encoded Dilithium3 signature (3,293 bytes when decoded)
key_versionThe key version used for signing — store this alongside the signature
algorithmThe signature algorithm used (Dilithium3)
request_idUnique request identifier for tracing and support
Signature Size

Dilithium3 signatures are 3,293 bytes (approximately 4,391 characters in base64). This is larger than RSA or ECDSA signatures but provides quantum resistance at NIST Security Level 3.

Storing Signatures​

Store the signature, the original message (or its hash), and the key_version together. You will need all three to verify the signature later.

{
"document_id": "doc-abc-123",
"message_hash": "sha256:e3b0c44298fc...",
"signature": "base64-encoded-signature...",
"key_version": 1,
"algorithm": "Dilithium3",
"signed_at": "2026-02-15T10:32:00Z"
}

Error Handling​

HTTP StatusError CodeCauseResolution
400ERR_INVALID_001Missing or invalid message or key_versionEnsure message is valid base64 and key_version is a positive integer
401ERR_AUTH_001Invalid or missing API keyCheck your x-api-key header
404ERR_SIG_003Key version not foundVerify the key_version exists for your tenant
409ERR_SIG_004Key is not in active statusOnly active keys can sign — generate or rotate to get a new active key
429ERR_RATE_001Rate limit exceededReduce request frequency or upgrade your plan
500ERR_CRYPTO_001Signing operation failedRetry the request; contact support if persistent