Want to e-seal a file directly? Use Meerkat e-Seal Signer — paste a hash, choose CAdES or XAdES format, and get a signed token with optional timestamp. This page documents the raw HTTP API. 🔏 Open e-Seal Signer →
Testing only — do not use in production documents. The Meerkat e-Seal is not a qualified or accredited trust service. Signatures issued here carry no legal weight and must not be embedded in documents intended for production use. The e-Seal signing certificate is issued under a private, untrusted CA hierarchy that is not recognized by any public root store.

e-Seal Identity

Endpoint URL https://thameur.org/eseal
Signing Certificate /C=TN/O=Meerkat MPCA by Thameur Belghith/CN=e-Seal Signer/organizationIdentifier=Bou Taba3
Valid From 2026-06-05
Valid Until 2029-06-04
Policy OID 2.16.788.1.99.1.60
Signer Validity 1095 days
Key Usage digitalSignature, nonRepudiation (critical)
Signing Key ECDSA P-256
Hash Algorithms SHA-256  ·  SHA-384  ·  SHA-512
e-Seal CA Certificate http://thameur.org/directory/mpca/mpca-eseal.crt
Chain (CA + Root) http://thameur.org/directory/mpca/mpca-eseal-chain.pem
Standard eIDAS, ETSI EN 319 412-3, ETSI EN 319 122-1 (CAdES)

API Endpoint

MethodURLDescription
POST https://thameur.org/eseal Submit a JSON request with a hash digest. Returns a CMS SignedData (CAdES) or XML ds:Signature (XAdES) based on the format parameter. Primary endpoint.
GET https://thameur.org/eseal Redirects to this documentation page.

Request

Header / Body fieldPresenceNotes
Content-Type required Must be application/json
hash required Hex or base64-encoded digest of the document to seal. The algorithm is inferred from the byte length (32 → SHA-256, 48 → SHA-384, 64 → SHA-512). Spaces, colons, and dashes are stripped automatically.
alg optional Hint for the hash algorithm (sha256, sha384, sha512). Ignored — algorithm is always inferred from hash length.
format optional cms (default) for CAdES/CMS SignedData output, or xades for XAdES XML output.
ts optional Boolean. true (default) to embed an RFC 3161 SignatureTimeStamp (T level); false for baseline level only (B level). If the TSA is unavailable, the timestamp is silently omitted regardless of this flag.

Request Body Examples

// CAdES-T (default — CMS + timestamp) { "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } // XAdES-B-T (XML + timestamp) { "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": "xades", "ts": true } // CAdES-B (no timestamp) { "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": "cms", "ts": false }

Success Response — CAdES (HTTP 200)

Header / BodyValue
Content-Typeapplication/cms
X-Eseal-Formatcms
X-Eseal-LevelCAdES-T if timestamped; CAdES-B if the TSA was unavailable or ts was false.
Body DER-encoded CMS SignedData (RFC 5652). The signed content (hash bytes) is embedded (eContent — non-detached). When X-Eseal-Level: CAdES-T, an id-aa-signatureTimeStampToken unsigned attribute (OID 1.2.840.113549.1.9.16.2.14) is included per ETSI EN 319 122-1 §5.3.3.

Success Response — XAdES (HTTP 200)

Header / BodyValue
Content-Typeapplication/xml; charset=utf-8
Content-Dispositionattachment; filename="eseal.xades"
X-Eseal-Formatxades
X-Eseal-LevelXAdES-B-T if timestamped; XAdES-B-B if ts was false or the TSA was unavailable.
Body UTF-8 XML document containing a detached ds:Signature per W3C XMLDSig + ETSI EN 319 132-2. The document digest is referenced via a ds:Reference (not embedded). When X-Eseal-Level: XAdES-B-T, a xades:SignatureTimeStamp unsigned attribute is present in xades:UnsignedSignatureProperties per ETSI EN 319 132-1 §5.3.4.

Error Response (JSON)

{ "error": "human-readable error message" }

Integration Guide

If you just want to e-seal a file without writing any code, use 🔏 Meerkat e-Seal Signer — it handles the hash computation and API call for you and lets you download the token directly. The steps below are for integrating the e-Seal endpoint into your own tooling.

Step 1 — Compute the hash of your document

# SHA-256 (most common) sha256sum myfile.pdf | awk '{print $1}' # SHA-384 sha384sum myfile.pdf | awk '{print $1}' # SHA-512 sha512sum myfile.pdf | awk '{print $1}' # On macOS (use openssl) openssl dgst -sha256 myfile.pdf | awk '{print $2}'

Step 2 — Send the hash to the e-Seal endpoint

# Store the hash HASH=$(sha256sum myfile.pdf | awk '{print $1}') # Send to e-Seal endpoint curl -s -X POST https://thameur.org/eseal \ -H 'Content-Type: application/json' \ -d "{\"hash\": \"$HASH\"}" \ -o signature.cms # Inspect the CMS structure openssl asn1parse -inform DER -in signature.cms | head -40

Step 3 — Verify the e-Seal signature

# Download the e-Seal CA chain curl -s -o eseal_chain.pem http://thameur.org/directory/mpca/mpca-eseal-chain.pem # Verify the CMS signature (content is embedded — no -content flag needed). # -purpose any is required: the e-Seal cert carries id-kp-documentSigning # (RFC 9336 OID 1.3.6.1.5.5.7.3.36), not emailProtection; OpenSSL's default # smimesign purpose would reject it as "unsuitable certificate purpose". openssl cms -verify -inform DER -in signature.cms \ -CAfile eseal_chain.pem -purpose any -noout # Extract the signed content (should match your original hash bytes) openssl cms -verify -inform DER -in signature.cms \ -CAfile eseal_chain.pem -purpose any | xxd

One-liner (hash + e-seal in one step)

curl -s -X POST https://thameur.org/eseal \ -H 'Content-Type: application/json' \ -d "{\"hash\": \"$(openssl dgst -sha256 myfile.pdf | awk '{print $2}')\"}" \ -o signature.cms \ && openssl asn1parse -inform DER -in signature.cms | head -30

Python (using the requests library)

import hashlib, requests # Compute SHA-256 hash with open('myfile.pdf', 'rb') as f: digest = hashlib.sha256(f.read()).hexdigest() # Send to e-Seal endpoint resp = requests.post( 'https://thameur.org/eseal', json={'hash': digest}, ) resp.raise_for_status() with open('signature.cms', 'wb') as f: f.write(resp.content)

JavaScript (Node.js)

const crypto = require('crypto'); const fs = require('fs'); const hash = crypto.createHash('sha256') .update(fs.readFileSync('myfile.pdf')) .digest('hex'); const response = await fetch('https://thameur.org/eseal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ hash }), }); fs.writeFileSync('signature.cms', Buffer.from(await response.arrayBuffer()));

CMS SignedData Structure (RFC 5652)

FieldDescription
contentTypeid-signedData (1.2.840.113549.1.7.2)
versionv1 (1)
digestAlgorithmsHash algorithm inferred from the input (SHA-256, SHA-384, or SHA-512).
encapContentInfo.eContentTypeid-data (1.2.840.113549.1.7.1)
encapContentInfo.eContentThe raw hash bytes submitted in the request (embedded, non-detached).
certificatese-Seal signing certificate + e-Seal CA + Root CA (full chain).
signerInfo.digestAlgorithmSame as digestAlgorithms.
signerInfo.signatureAlgorithmECDSA with the matching SHA algorithm (e.g. ecdsa-with-SHA256).
signerInfo.signatureDER-encoded ECDSA signature over the signed attributes (which include the message digest of the content).
signerInfo.unsignedAttrs[0]id-aa-signatureTimeStampToken (OID 1.2.840.113549.1.9.16.2.14) — RFC 3161 TimeStampToken covering SHA-256(signatureValue). Sourced from the Meerkat TSA at signing time. This is the CAdES-T extension.

The signed content is the hash bytes you supplied, not your original document. This means the CMS token proves that the e-Seal signing key operated on those specific bytes — to tie it back to your document, you must independently verify that the hash bytes match the expected digest of your document.

XAdES Signature Structure (ETSI EN 319 132-2)

When format=xades, the response is an XML document containing a detached ds:Signature element with XAdES qualifying properties. The signature covers the document's hash digest — the original document is not embedded.

ElementDescription
ds:SignatureRoot W3C XMLDSig signature element. Carries Id for internal cross-references.
ds:SignedInfo / ds:CanonicalizationMethodhttp://www.w3.org/TR/2001/REC-xml-c14n-20010315 (inclusive C14N).
ds:SignedInfo / ds:SignatureMethodECDSA with the hash algorithm matching the input length (e.g. ecdsa-sha256).
ds:Reference[@Id="Ref-Content"]References the signed document by digest only (detached, URI=""). ds:DigestValue is the base64-encoded hash bytes from the request.
ds:Reference[@Id="Ref-SignedProperties"]References the xades:SignedProperties element after C14N. The DigestValue covers the canonicalized SignedProperties XML.
ds:SignatureValueRaw ECDSA signature (r||s concatenation, 64 bytes for P-256) over the canonicalized ds:SignedInfo, base64-encoded.
ds:KeyInfo / ds:X509DataFull DER of the e-Seal signing certificate (base64), embedded for offline verification.
xades:SignedPropertiesContains xades:SigningTime and xades:SigningCertificateV2 (SHA-256 digest of the signing certificate for binding).
xades:UnsignedSignatureProperties / xades:SignatureTimeStamp(B-T only) Contains xades:EncapsulatedTimeStamp: base64-encoded DER of an RFC 3161 TimeStampToken covering SHA-256 of the canonicalized ds:SignatureValue element. Per ETSI EN 319 132-1 §5.3.4.

Verify with xmlsec1

# Download the e-Seal chain curl -s -o eseal_chain.pem http://thameur.org/directory/mpca/mpca-eseal-chain.pem # Request an XAdES-B-T signature curl -s -X POST https://thameur.org/eseal \ -H 'Content-Type: application/json' \ -d '{"hash":"'"$(openssl dgst -sha256 myfile.pdf | awk '{print $2}')"'","format":"xades"}' \ -o eseal.xades # Verify the XMLDSig signature (requires xmlsec1) xmlsec1 --verify --trusted-pem eseal_chain.pem eseal.xades # Inspect the XAdES XML xmllint --format eseal.xades

XAdES vs CAdES — when to choose which

CriterionCAdES (CMS)XAdES (XML)
Output formatBinary DER (.cms)UTF-8 XML (.xades)
Content embeddingHash bytes embedded in eContentDetached — only digest referenced
Verification toolingopenssl cms -verifyxmlsec1 --verify
Common use casesBinary document signing, email, S/MIMEXML documents, EU eIDAS, web services
Human-readableNo (ASN.1/DER)Yes (XML)

Error Responses

Errors return JSON {"error": "..."} with the matching HTTP status code.

HTTPCause
400Missing or unparseable hash field, or hash decodes to wrong byte length (not 32, 48, or 64 bytes).
405Wrong HTTP method (only GET and POST are accepted).
415Wrong Content-Type — must be application/json.
500OpenSSL cms -sign failed — internal error. The error message contains the OpenSSL stderr output.
503e-Seal not initialized — the signing key or certificate is missing. Run scripts/mpca_init.sh.

Technical Notes

  • CAdES format: The CMS output is a CAdES-T (by default) or CAdES-B (when ts=false) token per ETSI EN 319 122-1. The RFC 3161 signature timestamp (id-aa-signatureTimeStampToken, OID 1.2.840.113549.1.9.16.2.14) is embedded as an unsigned attribute in the SignerInfo, covering the SHA-256 hash of the ECDSA signature value.
  • XAdES format: The XML output follows ETSI EN 319 132-2 (XAdES baseline profile). At B-B level it contains only signed qualifying properties. At B-T level it adds a xades:SignatureTimeStamp unsigned attribute per ETSI EN 319 132-1 §5.3.4. The signature is detached — only the document's digest is referenced, not the document itself. The ECDSA signature value is raw r||s (not DER), base64-encoded, as required by the W3C XMLDSig specification.
  • Standard basis: The e-Seal signing certificate conforms to ETSI EN 319 412-3 (Certificate Profiles for Legal Persons). Key Usage is digitalSignature + nonRepudiation (critical). The subject includes organizationIdentifier in the ETSI-defined format.
  • eIDAS scope: Under eIDAS Regulation (EU) 910/2014, an e-Seal is the legal-person equivalent of an e-Signature. This testing service mimics the structure but is not qualified and has no legal standing.
  • CMS format: The response is a non-detached CMS SignedData (the hash bytes are embedded inside the token). This simplifies verification — you do not need the original hash separately to verify the signature, but you do need it to confirm the token covers your document.
  • Hash-only input: The endpoint accepts only a pre-computed hash, not the document itself. This keeps your document data off the server and matches the architecture of the RFC 3161 TSA endpoint.
  • Policy OID 2.16.788.1.99.1.60: A private OID registered under the Meerkat test PKI namespace (2.16.788.1.99). It has no meaning outside of this testing environment.
  • Signing key: ECDSA P-256. The signing certificate is issued by the Meerkat MPCA e-Seal CA (P-384), which chains to the Meerkat MPCA Root CA. None of these CAs are trusted by any public root store.
  • CORS: The endpoint responds with Access-Control-Allow-Origin: * to allow browser-based testing tools to call it directly.

We use only essential cookies and local browser storage for preferences and security. See our Privacy Policy for details.

Confirm action