Skip to main content
Digital signatures cryptographically prove that a PDF has not been tampered with since signing and identify the signer. They are required or expected for contracts, regulatory filings, legal documents, and compliance workflows. Forme supports PKCS#7 detached signatures using X.509 certificates, implemented in pure Rust with no external signing service.

Signing at Render Time

Pass the signature prop on the Document component to sign the PDF during rendering. You need an X.509 certificate and its corresponding RSA private key in PEM format.
import { readFileSync } from 'fs';
import { Document, Page, Text } from '@formepdf/react';
import { renderDocument } from '@formepdf/core';

const certificatePem = readFileSync('./cert.pem', 'utf-8');
const privateKeyPem = readFileSync('./key.pem', 'utf-8');

const pdfBytes = await renderDocument(
  <Document
    title="Service Agreement"
    signature={{
      certificatePem,
      privateKeyPem,
      reason: 'Approved',
      location: 'San Francisco, CA',
      contact: 'legal@acme.com',
    }}
  >
    <Page size="Letter" margin={54}>
      <Text style={{ fontSize: 24, fontWeight: 700 }}>Service Agreement</Text>
      <Text style={{ fontSize: 10, lineHeight: 1.6, marginTop: 12 }}>
        This agreement is entered into as of March 29, 2026...
      </Text>
    </Page>
  </Document>
);

Signature Props

PropTypeRequiredDescription
certificatePemstringYesX.509 certificate in PEM format.
privateKeyPemstringYesRSA private key in PEM format (PKCS#8).
reasonstringNoReason for signing (e.g., “Approved”, “Reviewed”). Shown in signature details.
locationstringNoSigning location (e.g., “San Francisco, CA”).
contactstringNoSigner contact information (e.g., email address).

Standalone Signing via signPdf()

Use signPdf() to sign an existing PDF without re-rendering it:
import { signPdf } from '@formepdf/core';
import { readFileSync, writeFileSync } from 'fs';

const pdfBytes = readFileSync('contract.pdf');
const certificatePem = readFileSync('./cert.pem', 'utf-8');
const privateKeyPem = readFileSync('./key.pem', 'utf-8');

const signedPdf = await signPdf(pdfBytes, {
  certificatePem,
  privateKeyPem,
  reason: 'Approved',
  location: 'San Francisco, CA',
  contact: 'legal@acme.com',
});

writeFileSync('signed-contract.pdf', signedPdf);

Signing via the Hosted API

Use the /v1/sign endpoint to apply a digital signature to a PDF you have already rendered or received from another source.
curl -X POST https://api.formepdf.com/v1/sign \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d "{
    \"pdf\": \"$(base64 -w0 contract.pdf)\",
    \"certificatePem\": \"$(cat cert.pem)\",
    \"privateKeyPem\": \"$(cat key.pem)\",
    \"reason\": \"Approved\",
    \"location\": \"San Francisco, CA\",
    \"contact\": \"legal@acme.com\"
  }" \
  --output signed-contract.pdf
The endpoint returns 200 OK with the signed PDF as the response body (Content-Type: application/pdf).

Visible vs Invisible Signatures

By default, Forme applies an invisible signature. The signature is embedded in the PDF’s metadata and can be verified in the signature panel of any PDF viewer, but nothing appears on the page itself. To add a visible signature annotation, set visible: true and specify the position and size:
<Document
  signature={{
    certificatePem,
    privateKeyPem,
    reason: 'Approved',
    visible: true,
    x: 350,
    y: 700,
    width: 200,
    height: 50,
  }}
>
  {/* ... */}
</Document>
The visible annotation displays the signer’s common name (from the certificate) at the specified position. Coordinates are in points from the bottom-left corner of the page.
PropTypeDefaultDescription
visiblebooleanfalseWhether to show a visible signature annotation.
xnumber0X coordinate in points from the left edge.
ynumber0Y coordinate in points from the bottom edge.
widthnumber200Width of the visible signature in points.
heightnumber50Height of the visible signature in points.
Visible signature appearance text uses the built-in Helvetica font, limited to Latin characters. This is a PDF specification constraint on form field appearances.

Generating a Test Certificate

For development and testing, generate a self-signed X.509 certificate with OpenSSL:
# Generate a private key
openssl genrsa -out key.pem 2048

# Generate a self-signed certificate (valid for 365 days)
openssl req -new -x509 -key key.pem -out cert.pem -days 365 \
  -subj "/CN=Test Signer/O=Acme Corp/C=US"
This creates key.pem (private key) and cert.pem (certificate) in the current directory.
Self-signed certificates work for testing and internal use. For production documents that need third-party trust verification, use a certificate from a trusted Certificate Authority (CA) or your organization’s PKI.

Known Limitations

  • RSA keys only. Only RSA private keys are supported. ECDSA, Ed25519, and other key types are not supported. If you pass a non-RSA key, you will get a clear error message.
  • Leaf certificate only. Only the signing certificate is embedded in the signature. Intermediate CA certificates are not included. PDF viewers may show “unknown signer” if they cannot build the full chain to a trusted root.
  • No timestamp authority (TSA). Signatures do not include a trusted timestamp from a TSA server. The signing date is set from the server clock but is not cryptographically verified by a third party.
  • No Long-Term Validation (LTV). CRL and OCSP responses are not embedded. Signature validity cannot be verified after the signing certificate expires.
  • One signature per render. Each render operation applies a single signature. To apply multiple signatures (e.g., co-signers), sign sequentially using the /v1/sign endpoint.
  • Self-signed certificates show “unknown signer” in Acrobat. This is expected. Acrobat only trusts certificates from its trusted root store. Self-signed certs are fine for testing and internal workflows.
  • Helvetica only in signature appearance text. Visible signature annotations use the PDF built-in Helvetica font. Custom fonts are not supported in signature form field appearances.