API Endpoint
| Method | URL | Description |
| POST |
https://thameur.org/eseal |
Submit a JSON request with a hash digest. Returns a DER-encoded CMS SignedData. Primary endpoint. |
| GET |
https://thameur.org/eseal |
Redirects to this documentation page. |
Request
| Header / Body field | Presence | Notes |
| 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. |
Request Body Example
{
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
Success Response (HTTP 200)
| Header / Body | Value |
| Content-Type |
application/cms |
| X-Eseal-Timestamped |
yes if an RFC 3161 signature timestamp was successfully embedded (CAdES-T); no if the TSA was unavailable and only a basic CAdES-B was returned. |
| Body |
DER-encoded CMS SignedData (RFC 5652) with an embedded id-aa-signatureTimeStampToken unsigned attribute (ETSI EN 319 122-1 §5.3.3 — CAdES-T). Contains the signed hash bytes as the encapsulated content, the signer certificate chain, and an RFC 3161 timestamp of the signature value from the Meerkat TSA. |
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
sha256sum myfile.pdf | awk '{print $1}'
sha384sum myfile.pdf | awk '{print $1}'
sha512sum myfile.pdf | awk '{print $1}'
openssl dgst -sha256 myfile.pdf | awk '{print $2}'
Step 2 — Send the hash to the e-Seal endpoint
HASH=$(sha256sum myfile.pdf | awk '{print $1}')
curl -s -X POST https://thameur.org/eseal \
-H 'Content-Type: application/json' \
-d "{\"hash\": \"$HASH\"}" \
-o signature.cms
openssl asn1parse -inform DER -in signature.cms | head -40
Step 3 — Verify the e-Seal signature
curl -s -o eseal_chain.pem http://pki.thameur.org/mpca/eseal_chain.pem
openssl cms -verify -inform DER -in signature.cms \
-CAfile eseal_chain.pem -noverify -noout
openssl cms -verify -inform DER -in signature.cms \
-CAfile eseal_chain.pem -noverify | 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
with open('myfile.pdf', 'rb') as f:
digest = hashlib.sha256(f.read()).hexdigest()
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)
| Field | Description |
| contentType | id-signedData (1.2.840.113549.1.7.2) |
| version | v1 (1) |
| digestAlgorithms | Hash algorithm inferred from the input (SHA-256, SHA-384, or SHA-512). |
| encapContentInfo.eContentType | id-data (1.2.840.113549.1.7.1) |
| encapContentInfo.eContent | The raw hash bytes submitted in the request (embedded, non-detached). |
| certificates | e-Seal signing certificate + e-Seal CA + Root CA (full chain). |
| signerInfo.digestAlgorithm | Same as digestAlgorithms. |
| signerInfo.signatureAlgorithm | ECDSA with the matching SHA algorithm (e.g. ecdsa-with-SHA256). |
| signerInfo.signature | DER-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.