PDFs mit CSC v2 signieren — PAdES-B-B bis B-LTA.
Integrieren Sie den CodeB-Fern-Signaturdienst in Ihr Produkt. Jeder authentifizierte Nutzer erhält ein EC-P-256-Signaturzertifikat, dessen Subject DN aus dem OIDC-Profil angereichert wird. Signieren Sie einen Hash mit signHash, verpacken Sie ihn client-seitig zu einer PAdES-Hülle (sign.html) oder lassen Sie es den Server per signDoc erledigen, fügen Sie einen RFC-3161-Zeitstempel hinzu, betten Sie RFC-6960-OCSP-Widerrufsdaten ein, hängen Sie einen Dokument-Zeitstempel an — PAdES bis B-LTA. Bereit für Integrationen mit der European Digital Identity Wallet.
ICryptoModule-Abstraktion ist HSM-vorbereitet; Azure Key Vault und PKCS#11 sind gestubbt und liefern HTTP 501, bis verdrahtet.Live testen → Vollständige API-Referenz
Die vier PAdES-Konformitätsstufen
Wählen Sie die niedrigste Stufe, die Ihren Beweisbedarf deckt. Höhere Stufen ergänzen Langzeit-Gültigkeit (überleben Zertifikatsablauf), zum Preis größerer Hüllen und zusätzlicher Netzwerk-Roundtrips.
PAdES-B-B
Basic — nur signierte Attribute. Prüfbar, solange das Signer-Zertifikat gültig ist.
- CMS-SignerInfo mit signingCertificateV2 (RFC 5035)
- id-aa-CMSAlgorithmProtection (RFC 6211)
PAdES-B-T
Time — ergänzt einen RFC-3161-TSA-Token, der belegt, dass die Signatur zu einem bestimmten Zeitpunkt existierte.
- B-B + id-aa-signatureTimeStampToken
- ~5 KiB Overhead pro Signatur
PAdES-B-LT
Long-Term — bettet OCSP-Antworten + Zertifikatskette ein, damit die Prüfung offline nach Zertifikatsablauf funktioniert.
- B-T + id-aa-ets-revocationValues + id-aa-ets-certValues
- ~10-15 KiB Overhead
PAdES-B-LTA
Long-Term with Archive — hängt einen /DocTimeStamp über die gesamte B-LT-PDF an. Erneuern Sie ihn vor Ablauf des Archiv-TSA, um die Gültigkeit unbegrenzt zu verlängern.
- B-LT + inkrementelle
/DocTimeStamp-Signatur - ISO 32000-2 §12.8.5
1 OIDC-Anmeldung — Access Token holen
CSC v2 reitet auf dem Tenant-OIDC-Provider. Standard OAuth 2.0 Authorization Code + PKCE. Siehe das OIDC-Anmeldungs-Kochbuch. Danach ist alles Authorization: Bearer <token>.
const accessToken = tokenResp.access_token;
const authHeaders = {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
};
2 Credentials des Nutzers listen
In Phase 1b hat jeder Nutzer genau ein Pro-Nutzer-EC-P-256-Credential.
const listResp = await fetch('/csc/v2/credentials/list', {
method: 'POST', headers: authHeaders, body: '{}'
}).then(r => r.json());
const credentialID = listResp.credentialIDs[0];
const info = await fetch('/csc/v2/credentials/info', {
method: 'POST', headers: authHeaders,
body: JSON.stringify({ credentialID })
}).then(r => r.json());
3 Signierte-Attribute-Hash berechnen, dann SAD anfordern
Die Signature Activation Data (SAD) ist ein kurzlebiger JWT, der Signer, Credential und exakte Hash-Liste bindet. Berechnen Sie SHA-256 der CMS-signedAttributes-DER (siehe Schritt 5), dann:
const authorize = await fetch('/csc/v2/credentials/authorize', {
method: 'POST', headers: authHeaders,
body: JSON.stringify({
credentialID, numSignatures: 1,
hash: [ hashB64 ],
hashAlgo: '2.16.840.1.101.3.4.2.1'
})
}).then(r => r.json());
// SCAL1 -> sofortiger SAD in authorize.SAD
// SCAL2 -> authorize.pending=true; Nutzer PIN-bestätigung anfordern
SAD.hash === request.hash bei signHash. Niemals einen SAD für einen anderen Hash wiederverwenden.4 Hash signieren
const sig = await fetch('/csc/v2/signatures/signHash', {
method: 'POST', headers: authHeaders,
body: JSON.stringify({
credentialID, SAD: authorize.SAD,
hash: [ hashB64 ], hashAlgo: '2.16.840.1.101.3.4.2.1',
signAlgo: '1.2.840.10045.4.3.2'
})
}).then(r => r.json());
const rawSig = base64Decode(sig.signatures[0]); // 64-Byte r||s
r||s-Konkatenation. Verpacken Sie sie in SEQUENCE { INTEGER r, INTEGER s }, bevor Sie sie in die CMS-SignerInfo.signature-OCTET-STRING einfügen.5 PAdES-Hülle zusammenbauen
Wenn Sie keine Bytes anfassen wollen: nutzen Sie den Browser-Signer oder das serverseitige signDoc. Wenn Sie selbst bauen wollen:
5.1 · /Contents-Platzhalter reservieren
Bauen Sie den PDF-inkrementellen Update-Trailer mit dem Signatur-Dictionary. Die /Contents<...>-Hex-Zeichenkette wird auf Ihre Zielgröße platzhalter-aufgefüllt (64 KiB komfortabel für B-LTA). Merken Sie sich die Byte-Offsets der <- und >-Klammern.
Klammer-Regel (ISO 32000-1 §12.8.1.1): Die <- und >-Klammern sitzen innerhalb des ByteRange-Lochs, nicht im signierten Bereich.
5.2 · signedAttributes-DER berechnen
Fünf verpflichtende Signed-Attribute, DER-sortiert: contentType, messageDigest, signingTime, signingCertificateV2 (mit issuerSerial), id-aa-CMSAlgorithmProtection. SHA-256 des DER-kodierten SET. Das ist der Hash, den Sie an signHash senden.
5.3 · CMS in den Platzhalter spleißen
CMS-SignedData mit eingebettetem Signer-Zertifikat bauen, dann hex-kodieren. Linksbündig in das /Contents<...>-Fenster; Rest mit 0 auffüllen. ByteRange-Ganzzahlen so anpassen, dass alle vier in den reservierten Platz passen.
6 B-T — RFC-3161-Zeitstempel hinzufügen
SHA-256 der CMS-SignerInfo.signature-OCTET-STRING-Bytes, dann an den Zeitstempel-Endpunkt senden:
const tsaHash = sha256(rawSigDerBytes);
const tsResp = await fetch('/csc/v2/signatures/timestamp', {
method: 'POST', headers: authHeaders,
body: JSON.stringify({ hash: base64(tsaHash), hashAlgo: '2.16.840.1.101.3.4.2.1' })
}).then(r => r.json());
// tsResp.token ist ein base64 DER RFC-3161-TimeStampToken.
// In CMS SignerInfo.unsignedAttrs als id-aa-signatureTimeStampToken spleißen.
signatures/timestamp leitet an HKLM\SOFTWARE\CodeB\TSAURL weiter (Standard Sectigo). signatures/tsa ist ein Tenant-interner Aussteller (siehe TSA-Server-Notiz).7 B-LT — OCSP-Antworten + Zertifikatskette einbetten
Langzeit-Validierung bedeutet, dass eine verlassende Partei die Signatur nach Ablauf des Signer-Zertifikats prüfen kann. Die Widerrufs-Evidenz und die Kette werden innerhalb der Signatur gebündelt.
const ocspReq = buildOcspRequestDer(signerCertDer);
const ocspBytes = await fetch('/csc/v2/ocsp', {
method: 'POST',
headers: { 'Content-Type': 'application/ocsp-request',
'Accept': 'application/ocsp-response' },
body: ocspReq
}).then(r => r.arrayBuffer());
// BasicOCSPResponse aus dem OCSPResponse.responseBytes.response extrahieren.
// Wiederholen für TSA. Beide als:
// id-aa-ets-revocationValues (OID 1.2.840.113549.1.9.16.2.24)
// id-aa-ets-certValues (OID 1.2.840.113549.1.9.16.2.23)
Fallback-Regeln (Muster des Browser-Signers):
- Einige OCSPs schlagen fehl: loggen, als B-LT mit Teildaten fortfahren.
- Alle OCSPs schlagen fehl: loggen, auf B-T degradieren.
Wire-Format und Fehler-Taxonomie: siehe OCSP-Responder-Seite.
8 B-LTA — DocTimeStamp anhängen
PDF-Ebene /DocTimeStamp ist eine Signatur-Dict-Variante mit /SubFilter /ETSI.RFC3161 und /Contents = rohes RFC-3161-TimeStampToken-DER (kein CMS-SignedData). ByteRange überdeckt die gesamte vorherige PDF außer dem neuen /Contents<...>-Loch.
Wiederholen Sie diesen Schritt alle paar Jahre vor Ablauf des Archiv-TSA-Zertifikats. Jeder frische /DocTimeStamp verlängert das Prüffenster.
Serverseitige Alternative — signDoc
Wenn Sie stattdessen die PDF senden und der Server alles zusammenbauen soll (inkl. inkrementellem Speichern), nutzen Sie POST /csc/v2/signatures/signDoc:
const signed = await fetch('/csc/v2/signatures/signDoc', {
method: 'POST', headers: authHeaders,
body: JSON.stringify({
credentialID, SAD,
documents: [{
document: base64OfPdfBytes,
signature_format: 'P',
conformance_level: 'AdES-B-LT',
signed_envelope_property: 'ENVELOPED',
parameters: {
signing_reason: 'Vertragsannahme',
signing_location: 'Valletta',
contact_info: 'legal@example.com'
}
}]
})
}).then(r => r.json());
const signedPdf = base64Decode(signed.documentWithSignature[0]);
signDoc: 10 MiB (sonst 32 KiB). Der Server macht SAD-Bindung, Hash-Berechnung und PAdES-Zusammenbau für Sie und liefert die fertige PDF zurück.Fehlerbehebung
- Adobe Reader: "Signatur nicht angewendet" — meist ein ByteRange-Bug oder fehlendes Widget-
/P. - Leerer Signaturbereich in Adobe — AcroForm-Dictionary fehlt oder listet das Widget nicht.
- CMS-Parser lehnt Hülle ab — signedAttrs müssen DER-nach-Tag sortiert sein.
- PAdES-B-T prüft, LT/LTA nicht — OCSP-Antworten müssen BasicOCSPResponse-Strukturen sein (aus
OCSPResponseausgepackt). - Zeitstempel-Fetch schlägt fehl — Client sollte auf PAdES-B-B degradieren und klar loggen. Nicht die ganze Signatur scheitern lassen.
Standards & Wire-Referenzen
- Cloud Signature Consortium API v2 §§ 11.1–11.10
- ETSI EN 319 142-1 — PAdES-Baseline-Profil
- ETSI EN 319 122-1 — CAdES-Baseline
- RFC 5652 — CMS
- RFC 5035 — ESSCertIDv2
- RFC 6211 — id-aa-CMSAlgorithmProtection
- RFC 3161 + RFC 5816 — TSP + ESSCertIDv2-Update
- RFC 6960 + RFC 5019 — OCSP + Lightweight-Profil
- ISO 32000-1 §12.8 + ISO 32000-2 §12.8.5 — PDF-Signatur-Dictionaries und DocTimeStamp
- Verordnung (EU) 910/2014 Art. 3(11), 26, 32 — AdES-Definition und Prüfanforderungen
Fragen? Fragen Sie uns.