HOWTO · API · CSC v2

Rechnungen per CSC v2 API signieren.

Diese Anleitung zeigt, wie Sie eine Rechnung mit der Cloud Signature Consortium v2 API auf CodeB Sovereign Communications signieren, mit einem qualifizierten Zeitstempel nach RFC 3161 versehen und OCSP-Revocation-Daten für die langfristige Validierung (PAdES-B-LT) einbetten. Ergebnis: eine PDF, die die GoBD-Anforderung an Unveränderbarkeit kryptographisch erfüllt.

Voraussetzungen. Sie benötigen (1) einen OIDC-Bearer-Access-Token für Ihren CodeB-Tenant, (2) HTTPS-Zugriff auf https://<tenant>/csc.ashx, (3) eine PDF-Datei, deren Byte-Range Sie hashen wollen. Für ein produktives Rechnungsarchiv brauchen Sie zusätzlich einen konfigurierten QTSP als Zeitstempel-Quelle (Standard: Sectigo).

1 OIDC-Bearer-Access-Token beziehen

Der CSC v2 Endpunkt authentifiziert Aufrufer per OIDC-Bearer. Zwei Wege sind üblich:

  • Interaktiv (authorization_code) — für Web-Applikationen mit angemeldetem Nutzer.
  • Client-Credentials — für Server-zu-Server-Signieren aus Buchhaltungssoftware.

Client-Credentials-Beispiel (Ihr Tenant muss den Grant aktiviert haben):

curl -X POST "https://phone.aloaha.com/oidc.ashx?action=token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  -d "client_secret=$CLIENT_SECRET" \
  -d "scope=csc"

Die Antwort enthält access_token, das im weiteren Ablauf als Authorization: Bearer <token> mitgeschickt wird.

Hinweis zur Aussagekraft: der acting sub im Token bestimmt die Signaturidentität — das Zertifikat wird beim ersten Zugriff aus dem OIDC-Profil mit Vor-, Nachnamen und E-Mail befüllt.

2 Signaturzertifikat abfragen

Erste Abfrage listet die verfügbaren Credentials des Nutzers auf:

curl -X POST "https://phone.aloaha.com/csc.ashx?action=credentials/list" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Antwort: {"credentialIDs": ["cb_ec_p256_..."] }. Zweite Abfrage liefert die Zertifikatskette:

curl -X POST "https://phone.aloaha.com/csc.ashx?action=credentials/info" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"credentialID":"cb_ec_p256_..."}'

Antwort enthält cert.certificates[] (PEM), key.algo (1.2.840.10045.4.3.2 = ECDSA-SHA256) und SCAL (1 für automatische Autorisierung, 2 für 2FA-Bestätigung).

3 SHA-256-Hash der Rechnung bilden

Der Endpunkt signiert einen SHA-256-Hash, nicht die PDF selbst. Bei PAdES ist der Hash der ByteRange-Bereich der zu signierenden PDF — also alles außer dem Platzhalter, wo die Signatur eingesetzt wird.

Node.js-Beispiel:

const crypto = require('crypto');
const fs = require('fs');

// PDF vorbereiten: ByteRange-Bereich extrahieren
// (Details siehe sign.html client)
const pdfBytesToSign = getByteRangeBytes(pdfBuffer);
const hash = crypto.createHash('sha256').update(pdfBytesToSign).digest();
const hashB64 = hash.toString('base64');

Für schnelle Tests reicht auch der Hash über die gesamte Datei — dann liefert Ihnen die API zwar eine gültige Signatur, aber die Rechnung ist keine PAdES-konforme PDF-Signatur; nutzen Sie signatures/signDoc stattdessen, wenn der Server die PDF-Einhüllung übernehmen soll.

4 Signaturautorisierung (SAD) beziehen

CSC v2 trennt Autorisierung von Signaturausführung. Der Autorisierungsschritt liefert ein kurzlebiges SAD-JWT, das die Signatur eines konkreten Hashes durch den zugehörigen Nutzer freischaltet:

curl -X POST "https://phone.aloaha.com/csc.ashx?action=credentials/authorize" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "credentialID": "cb_ec_p256_...",
    "hash": ["'"$HASH_B64"'"],
    "numSignatures": 1,
    "hashAlgo": "2.16.840.1.101.3.4.2.1"
  }'

Antwort: {"SAD": "eyJ..."}. Das SAD ist ein JWT mit den claims sub, credential_id, hash, exp. Gültigkeit typisch 5 Minuten.

SCAL2-Konten (2FA erforderlich): Bei SCAL2-Konten liefert dieser Endpunkt stattdessen eine authorization_id, und Sie müssen den zweiten Faktor über credentials/authorize/confirm bestätigen. Details siehe API-Referenz.

5 Hash signieren

Mit SAD und Hash wird jetzt die eigentliche Signatur angefordert:

curl -X POST "https://phone.aloaha.com/csc.ashx?action=signatures/signHash" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "credentialID": "cb_ec_p256_...",
    "SAD": "eyJ...",
    "hash": ["'"$HASH_B64"'"],
    "signAlgo": "1.2.840.10045.4.3.2",
    "hashAlgo": "2.16.840.1.101.3.4.2.1"
  }'

Antwort: {"signatures": ["MEUCIQ..."]} — die CMS-Signaturbytes, base64-kodiert, die in das /Sig-Dictionary der PDF eingesetzt werden.

Vereinfachung: signDoc statt signHash

Wenn Ihr Client die PDF-Byte-Range nicht selbst konstruieren will, lädt signatures/signDoc die komplette PDF hoch und liefert die fertig signierte PDF zurück. Praktisch für kleine Volumen; skaliert schlechter, weil jede PDF komplett über die Leitung geht.

6 Qualifizierten RFC-3161-Zeitstempel einbetten (PAdES-B-T)

Damit die Signatur beweiskräftig auf einen amtlich beglaubigten Zeitpunkt gebunden ist, wird ein qualifizierter Zeitstempel angehängt. CodeB proxiert die QTSP-Verbindung:

curl -X POST "https://phone.aloaha.com/csc.ashx?action=signatures/timestamp" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @signature-hash.bin \
  -o timestamp-token.tst

Die zurückgegebene timestamp-token.tst ist der RFC-3161-Zeitstempel-Token, den Sie als id-aa-signatureTimeStampToken-Attribut in die CMS-Signatur einbetten. Das Ergebnis ist eine PAdES-B-T-Signatur.

Standard-QTSP ist Sectigo (URL http://timestamp.sectigo.com/qualified). Die Konfiguration liegt im Windows-Registry-Wert HKLM\SOFTWARE\CodeB\TSAURL. Kunden mit eigener QTSP-Beziehung (z.B. maltesische Gattaca) können den Endpunkt umkonfigurieren.

7 OCSP-Revocation-Daten einbetten (PAdES-B-LT)

Damit die Signatur auch nach zehn Jahren noch verifizierbar bleibt, müssen die zum Signaturzeitpunkt gültigen Revocation-Daten des Signaturzertifikats in die PDF eingebettet werden. Ohne diesen Schritt müsste ein späterer Verifikationstool den ursprünglichen OCSP-Responder erreichen — der möglicherweise nicht mehr existiert.

curl -X POST "https://phone.aloaha.com/csc.ashx?action=ocsp" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "certificate": "-----BEGIN CERTIFICATE-----\n...",
    "issuer_certificate": "-----BEGIN CERTIFICATE-----\n..."
  }'

Antwort: {"ocsp_response": "MIIBoAoBAK..."}. Die OCSP-Antwort wird als id-aa-ets-revocationValues-Attribut in die CMS-Signatur eingebettet. Das Ergebnis ist eine PAdES-B-LT-Signatur — das empfohlene Niveau für Rechnungsarchive mit zehnjähriger Aufbewahrungsfrist.

8 Fehlerbehandlung

Der Endpunkt liefert bei Fehlern strukturierte JSON-Antworten nach RFC 6749 Muster:

{
  "error": "invalid_request",
  "error_description": "hash: must be 32 raw bytes (SHA-256), base64-encoded"
}

Häufige Fehler und deren Ursache:

  • 400 invalid_request — fehlende oder falsche Parameter. Meist ist der Hash nicht 32 Byte lang oder nicht base64-kodiert.
  • 400 only_sha256_supported — ein anderer Hash-Algorithmus als SHA-256 wurde angefordert.
  • 401 invalid_token — Bearer-Token abgelaufen oder ungültig. Neu holen.
  • 403 https_required — der Aufruf kam über HTTP. CSC v2 akzeptiert nur HTTPS.
  • 413 body_too_large — Body über 32 KiB (bzw. 10 MiB für signDoc). PDF vor dem Hochladen kompaktieren oder signHash nutzen.
  • 429 — Rate-Limit überschritten (30 Anfragen/min pro Aktion und IP). Antwort enthält Retry-After: 30.
  • 501 crypto_module_not_configured — das Signaturmodul ist auf dem Tenant nicht konfiguriert. IT-Administrator kontaktieren.

Vollständige API-Referenz → Zurück zur Übersicht English