Darstellung
Authentifizierung
Die eBOnaut API authentifiziert deine Requests ausschließlich über HMAC-signierte Request-Umschläge. Es gibt keine Session-Tokens oder API-Keys im Header – die gesamte Authentifizierung steckt im Request-Body.
Zwei HMAC-Ebenen
Es existieren zwei Arten von Zugangsdaten, je nach Kontext der Anfrage:
| Ebene | Herkunft | Verwendung |
|---|---|---|
| Partner-HMAC | Wird von eBOnaut initial ausgestellt | codesetup/set und codesetup/get – der komplette Pairing-Flow zum Anbinden neuer Kund:innen |
| Kunden-HMAC | Einmalig aus codesetup/get, nachdem die Kund:in die Verbindung aktiviert hat | Alle übrigen Endpunkte – jeder Request läuft im Kontext der Kund:in, deren Zugangsdaten verwendet werden |
Speichere pro angebundener Kund:in ein eigenes HMAC-Triple und wähle je nach Request den passenden Satz aus. Die Partner-Zugangsdaten nutzt du ausschließlich für das Onboarding.
Prinzip
Jeder Request enthält statt der eigentlichen Parameter einen signierten Umschlag mit drei Feldern:
json
{
"hmac_ID": "<22-stellige numerische Key-ID>",
"message": "<iv_hex>:<verschlüsselte_nachricht_hex>",
"hmac": "<HMAC-SHA256 der message, hex>"
}hmac_ID– ID des HMAC-Keys (genau 22 Zeichen, numerisch), wird dir von eBOnaut zugewiesenmessage– die mit AES-256-CBC verschlüsselte Nachricht (Format:iv:ciphertext, beides hex-kodiert)hmac– HMAC-SHA256 über den komplettenmessage-String, berechnet mit dem Secret Key
Die Nachricht (message)
Die verschlüsselte Nachricht ist ein JSON-Objekt:
json
{
"timestamp": 1761500000,
"hmac_unique": "beliebige-eindeutige-id",
"body": {
"...": "die eigentlichen Request-Parameter"
}
}| Feld | Bedeutung |
|---|---|
timestamp | Unix-Zeitstempel der Erstellung – Nachrichten sind 5 Minuten gültig |
hmac_unique | Eindeutige ID pro Request – Wiederverwendung wird abgelehnt (Replay-Schutz) |
body | Die eigentlichen Request-Parameter des Endpunkts |
Ablauf beim Erstellen eines Requests
- Nachricht bauen – JSON-Objekt mit
timestamp,hmac_uniqueundbodyerstellen - Verschlüsseln – Nachricht mit AES-256-CBC und dem
encryption_keyverschlüsseln (zufälliger IV pro Request), alsiv_hex:ciphertext_hexformatieren - Signieren –
hmac = HMAC_SHA256(message, secret_key)berechnen, hex-kodiert - Senden –
hmac_ID,message,hmacals Request-Body (form-data oder JSON)
Beispiele
Alle Beispiele bauen den gleichen Umschlag: Nachricht als JSON bauen → mit AES-256-CBC verschlüsseln (iv_hex:ciphertext_hex) → HMAC-SHA256 über den message-String → als hmac_ID, message, hmac senden.
PHP
php
$body = ['search' => 'DE.Justiz.f8d8388f-1a64-'];
$iv = random_bytes(16);
$encrypted = openssl_encrypt(
json_encode([
'timestamp' => time(),
'hmac_unique' => bin2hex(random_bytes(8)),
'body' => $body,
]),
'aes-256-cbc',
$encryptionKey, // von eBOnaut ausgestellt
OPENSSL_RAW_DATA,
$iv
);
$message = bin2hex($iv) . ':' . bin2hex($encrypted);
$payload = [
'hmac_ID' => $hmacId, // 22-stellige Key-ID
'message' => $message,
'hmac' => hash_hmac('sha256', $message, $secretKey),
];Node.js
js
const crypto = require('node:crypto')
const hmacId = '<hmac_id>' // 22-stellige Key-ID
const secretKey = '<secret_key>' // von eBOnaut ausgestellt
const encryptionKey = '<enc_key>' // 32-stellig
const body = { search: 'DE.Justiz.f8d8388f-1a64-' }
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv(
'aes-256-cbc',
Buffer.from(encryptionKey, 'utf8'),
iv
)
const encrypted = Buffer.concat([
cipher.update(JSON.stringify({
timestamp: Math.floor(Date.now() / 1000),
hmac_unique: crypto.randomBytes(8).toString('hex'),
body,
}), 'utf8'),
cipher.final(),
])
const message = `${iv.toString('hex')}:${encrypted.toString('hex')}`
const form = new FormData()
form.append('hmac_ID', hmacId)
form.append('message', message)
form.append('hmac', crypto.createHmac('sha256', secretKey).update(message).digest('hex'))
const res = await fetch('https://api.ebonaut.io/contact/search', {
method: 'POST',
headers: { Origin: 'https://partner.example.com' },
body: form,
})
console.log(await res.json())Python
python
import hashlib
import hmac
import json
import os
import time
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
HMAC_ID = '<hmac_id>' # 22-stellige Key-ID
SECRET_KEY = '<secret_key>' # von eBOnaut ausgestellt
ENCRYPTION_KEY = '<enc_key>' # 32-stellig
body = {'search': 'DE.Justiz.f8d8388f-1a64-'}
iv = os.urandom(16)
plaintext = json.dumps({
'timestamp': int(time.time()),
'hmac_unique': os.urandom(8).hex(),
'body': body,
})
cipher = AES.new(ENCRYPTION_KEY.encode(), AES.MODE_CBC, iv)
encrypted = cipher.encrypt(pad(plaintext.encode(), AES.block_size))
message = iv.hex() + ':' + encrypted.hex()
payload = {
'hmac_ID': HMAC_ID,
'message': message,
'hmac': hmac.new(SECRET_KEY.encode(), message.encode(), hashlib.sha256).hexdigest(),
}
res = requests.post(
'https://api.ebonaut.io/contact/search',
headers={'Origin': 'https://partner.example.com'},
data=payload,
)
print(res.json())cURL
bash
curl -X POST "https://api.ebonaut.io/contact/search" \
-H "Origin: https://partner.example.com" \
-F "hmac_ID=<hmac_id>" \
-F "message=<message>" \
-F "hmac=<hmac>"Fehlerfälle
Die API lehnt den Umschlag mit 403 ab, wenn:
hmac_ID,messageoderhmacfehlen oder das Format nicht stimmt- der HMAC-Key unbekannt oder abgelaufen ist (optionales
hmac_expiration) - die Signatur nicht zur Nachricht passt
- der
timestampälter als 5 Minuten ist hmac_uniquebereits verwendet wurde
Uhrzeit synchronisieren
Dein Server muss per NTP synchronisiert sein. Weicht die Systemzeit um mehr als ca. 4 Minuten ab, werden gültige Requests als abgelaufen abgelehnt.
Zugangsdaten
Jeder Satz besteht aus hmac_ID, secret_key und encryption_key. Wie du an die Partner- und Kunden-Zugangsdaten kommst und wie lange sie gültig sind, steht im Pairing-Ablauf.
secret_key und encryption_key behandelst du wie Passwörter: Leg sie nicht im Quellcode, in Logs oder in Client-Anwendungen ab.
Origin-Prüfung
Zusätzlich zur HMAC-Signatur prüft die API den Origin-Header gegen eine Allowlist:
- Requests ohne Origin oder mit nicht freigegebenem Origin →
403
http
Origin: https://partner.example.com