Signature Verification Guide
This guide explains how to verify digital signatures in PDF documents using the Vanilla.PDF C API.
For a complete working example, see verify.c.
Overview
PDF digital signatures use PKCS#7 (CMS) format to store cryptographic signatures. Verification involves:
Cryptographic verification - Verify the signature matches the document content
Certificate chain validation - Validate the signer’s certificate against trusted roots
Algorithm strength check - Detect weak algorithms (MD5, SHA-1, small keys)
Time validation - Optionally validate certificate at signing time
Verification Status Codes
The verification result includes a status code indicating the outcome:
Status |
Description |
|---|---|
|
Signature is cryptographically valid and certificate chain is trusted |
|
Signature verification failed (content modified or wrong key) |
|
Signing certificate has expired |
|
Certificate not yet valid (future start date) |
|
Certificate chain does not lead to a trusted root |
|
Weak cryptographic algorithm detected |
|
Document modified after signing |
Weak Algorithm Detection
The verifier detects the following weak algorithms:
Weak Digest Algorithms:
MD2, MD4, MD5, SHA-1, MD5-SHA1
Weak Signature Algorithms:
MD2/MD4/MD5 with RSA
SHA-0 with RSA/DSA
SHA-1 with RSA/DSA/ECDSA
RIPEMD-160 with RSA, MDC2 with RSA
Weak Key Sizes:
RSA keys < 2048 bits
DSA keys < 2048 bits
EC keys < 256 bits
Creating a Trust Store
Create a trusted certificate store and load certificates:
// Create or load trusted certificate store
RETURN_ERROR_IF_NOT_SUCCESS(TrustedCertificateStore_Create(&trust_store));
if (certs_path != NULL) {
printf("Loading trusted certificates from: %s\n", certs_path);
RETURN_ERROR_IF_NOT_SUCCESS(TrustedCertificateStore_LoadFromDirectory(trust_store, certs_path));
} else {
printf("Loading system default trusted certificates\n");
RETURN_ERROR_IF_NOT_SUCCESS(TrustedCertificateStore_LoadSystemDefaults(trust_store));
}
Configuring Verification Settings
Configure verification behavior with custom settings:
// Configure verification settings
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationSettings_Create(&settings));
if (skip_certificate_validation) {
printf("WARNING: Skipping certificate chain validation (insecure)\n");
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationSettings_SetSkipCertificateValidation(settings, VANILLAPDF_RV_TRUE));
}
if (allow_weak_algorithms) {
printf("Allowing weak cryptographic algorithms\n");
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationSettings_SetAllowWeakAlgorithmsFlag(settings, VANILLAPDF_RV_TRUE));
}
if (check_signing_time) {
printf("Checking certificate validity at signing time\n");
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationSettings_SetCheckSigningTimeFlag(settings, VANILLAPDF_RV_TRUE));
}
/* TODO: CRL/OCSP revocation checking (https://github.com/vanillapdf/vanillapdf/issues/157)
if (check_revocation) {
printf("Checking certificate revocation status\n");
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationSettings_SetCheckRevocationFlag(settings, VANILLAPDF_RV_TRUE));
}
*/
Available settings:
SetSkipCertificateValidation- Skip X509 chain validation (testing only)SetAllowWeakAlgorithmsFlag- Allow MD5, SHA-1, small keysSetCheckSigningTimeFlag- Validate cert at signing time instead of current time
Verifying a Signature
Call the verification API and inspect the results:
// Verify signature using DigitalSignatureExtensions
RETURN_ERROR_IF_NOT_SUCCESS(DigitalSignatureExtensions_Verify(digital_signature, document, trust_store, settings, &result));
// Get verification status
SignatureVerificationStatusType status;
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationResult_GetStatus(result, &status));
// Get verification flags
boolean_type is_signature_valid = VANILLAPDF_RV_FALSE;
boolean_type is_document_intact = VANILLAPDF_RV_FALSE;
boolean_type is_cert_trusted = VANILLAPDF_RV_FALSE;
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationResult_IsSignatureValid(result, &is_signature_valid));
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationResult_IsDocumentIntact(result, &is_document_intact));
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationResult_IsCertificateTrusted(result, &is_cert_trusted));
// Get signer common name
BufferHandle* signer_name = NULL;
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationResult_GetSignerCommonName(result, &signer_name));
// Get verification message
BufferHandle* message_buffer = NULL;
RETURN_ERROR_IF_NOT_SUCCESS(SignatureVerificationResult_GetMessage(result, &message_buffer));
Command Line Usage
The vanillapdf.tools CLI provides signature verification:
# Basic verification (uses system trust store)
vanillapdf.tools verify -f document.pdf
# Skip certificate validation (for self-signed certificates)
vanillapdf.tools verify -f document.pdf --skip-certificate-validation
# Allow weak algorithms (for legacy documents)
vanillapdf.tools verify -f document.pdf --allow-weak-algorithms
# Check certificate at signing time
vanillapdf.tools verify -f document.pdf --check-signing-time
# Use custom certificate directory
vanillapdf.tools verify -f document.pdf -c /path/to/trusted/certs
# Combine options
vanillapdf.tools verify -f document.pdf \
--allow-weak-algorithms \
--check-signing-time
Security Recommendations
Always use system trust store — Let the OS manage trusted root certificates
Avoid
--skip-certificate-validation— Only use for testing self-signed certificatesAvoid
--allow-weak-algorithms— Only enable when absolutely necessary for legacy documentsUse
--check-signing-time— For old documents where certificates may have expiredKeep certificates updated — Ensure your system’s certificate store is current