Skip to content

Schnellstart ​

Diese Seite führt in vier Schritten vom ersten Pairing bis zu einer angelegten Nachricht. Als Grundlage dient eine kleine Hilfsfunktion, die den HMAC-Umschlag baut und den Request sendet. Alle weiteren Beispiele in der Dokumentation lassen sich damit nachbauen.

Bevor es losgeht

Du brauchst Partner-Zugangsdaten und einen freigegebenen Origin, siehe Erste Schritte. Zum Ausprobieren empfehlen wir die Entwicklungsumgebung https://api.dev.ebonaut.io.

Hilfsfunktion call() ​

Die Funktion verschlüsselt den Body, signiert ihn und sendet ihn als form-data. Sie liefert die geparste JSON-Antwort.

js
const crypto = require('node:crypto')

const BASE_URL = 'https://api.dev.ebonaut.io'
const ORIGIN = 'https://partner.example.com' // freigegebener Origin

async function call(path, creds, body = {}) {
  const iv = crypto.randomBytes(16)
  const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(creds.encryptionKey, 'utf8'), iv)
  const encrypted = Buffer.concat([
    cipher.update(JSON.stringify({
      timestamp: Math.floor(Date.now() / 1000),
      hmac_unique: crypto.randomUUID(),
      body,
    }), 'utf8'),
    cipher.final(),
  ])
  const message = `${iv.toString('hex')}:${encrypted.toString('hex')}`

  const form = new FormData()
  form.append('hmac_ID', creds.hmacId)
  form.append('message', message)
  form.append('hmac', crypto.createHmac('sha256', creds.secretKey).update(message).digest('hex'))

  const res = await fetch(`${BASE_URL}/${path}`, {
    method: 'POST',
    headers: { Origin: ORIGIN },
    body: form,
  })
  const data = await res.json()
  return { status: res.status, data }
}
php
const BASE_URL = 'https://api.dev.ebonaut.io';
const ORIGIN   = 'https://partner.example.com'; // freigegebener Origin

function call(string $path, array $creds, array $body = []): array
{
    $iv = random_bytes(16);
    $encrypted = openssl_encrypt(
        json_encode([
            'timestamp'   => time(),
            'hmac_unique' => bin2hex(random_bytes(16)),
            'body'        => (object) $body,
        ]),
        'aes-256-cbc',
        $creds['encryptionKey'],
        OPENSSL_RAW_DATA,
        $iv
    );
    $message = bin2hex($iv) . ':' . bin2hex($encrypted);

    $ch = curl_init(BASE_URL . '/' . $path);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => ['Origin: ' . ORIGIN],
        CURLOPT_POSTFIELDS     => [
            'hmac_ID' => $creds['hmacId'],
            'message' => $message,
            'hmac'    => hash_hmac('sha256', $message, $creds['secretKey']),
        ],
    ]);
    $raw    = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    return ['status' => $status, 'data' => json_decode($raw, true)];
}
python
import hashlib, hmac, json, os, time, uuid
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

BASE_URL = 'https://api.dev.ebonaut.io'
ORIGIN = 'https://partner.example.com'  # freigegebener Origin

def call(path, creds, body=None):
    iv = os.urandom(16)
    plaintext = json.dumps({
        'timestamp': int(time.time()),
        'hmac_unique': str(uuid.uuid4()),
        'body': body or {},
    })
    cipher = AES.new(creds['encryption_key'].encode(), AES.MODE_CBC, iv)
    message = iv.hex() + ':' + cipher.encrypt(pad(plaintext.encode(), AES.block_size)).hex()

    res = requests.post(
        f'{BASE_URL}/{path}',
        headers={'Origin': ORIGIN},
        data={
            'hmac_ID': creds['hmac_id'],
            'message': message,
            'hmac': hmac.new(creds['secret_key'].encode(), message.encode(), hashlib.sha256).hexdigest(),
        },
    )
    return res.status_code, res.json()

Die Beispiele unten verwenden die Node.js-Variante. Mit den anderen Sprachen funktionieren sie identisch.

Schritt 1: Pairing-Code erzeugen ​

Dieser Request wird mit den Partner-Zugangsdaten signiert.

js
const partner = { hmacId: '<partner_hmac_id>', secretKey: '<partner_secret>', encryptionKey: '<partner_enc_key>' }

const { data } = await call('codesetup/set', partner)
console.log(data)
// { codesetup_ID: "483920", codesetup_url: "https://app.ebonaut.io/activate/483920" }

Gib codesetup_url (oder den sechsstelligen Code) an deine Kund:in weiter. Diese bestätigt die Verbindung in der eBOnaut-App, mehr dazu im Pairing-Ablauf.

Schritt 2: Kunden-Zugangsdaten abholen ​

Frag alle 5 bis 10 Sekunden ab, bis die Kund:in aktiviert hat (409 bedeutet: noch nicht aktiviert).

js
let customer
while (!customer) {
  const { status, data } = await call(`codesetup/get/${codesetupId}`, partner)
  if (status === 200) {
    customer = {
      hmacId: data.hmac_ID,
      secretKey: data.hmac_secret_key,
      encryptionKey: data.hmac_encryption_key,
    }
  } else if (status === 409) {
    await new Promise((r) => setTimeout(r, 5000))
  } else {
    throw new Error(`Pairing fehlgeschlagen: ${status} ${JSON.stringify(data)}`)
  }
}
// customer jetzt sicher speichern, die Daten sind nur einmal abrufbar!

Zugangsdaten sofort speichern

Die Kunden-Zugangsdaten werden nur einmal ausgeliefert. Leg sie umgehend verschlüsselt ab (Secret-Manager oder verschlüsselte Datenbank), nicht im Quellcode oder in Logs.

Schritt 3: Verbindung bestätigen ​

Der erste Request mit den Kunden-Zugangsdaten verlängert deren Gültigkeit von 3 Stunden auf 3 Monate. Ruf dafür user/me auf:

js
const { status, data } = await call('user/me', customer)
console.log(status, data.user_mail) // 200 "max.mustermann@example.com"

Ab jetzt laufen alle Requests im Kontext dieser Kund:in.

Schritt 4: Erste Nachricht vorbereiten ​

Ruf zuerst die hinterlegten Zertifikate ab. Für das Senden brauchst du die cert_ID des Absender-Zertifikats:

js
const certs = await call('cert/getall', customer)
const certId = certs.data[0].cert_ID

Such anschließend die Empfänger:in:

js
const contacts = await call('contact/search', customer, { search: 'DE.Justiz.f8d8388f-1a64-' })
const recipient = contacts.data.data[0].contact_justiz_ID

Leg dann einen Entwurf an:

js
const item = await call('postboxitem/set', customer, {
  transmitter_cert_ID: certId,
  recipient_justiz_ID: recipient,
  content_subject: 'Testnachricht',
})
console.log(item.data) // "6bd525df-5b16-4a55-b5d9-ee4cf5277d7b" (postboxitem_ID)

Der Entwurf hat den Status draft. Dateien hängst du über die File-API an, versendet wird mit postboxitem/send.

Wenn etwas nicht klappt ​

SymptomHäufige Ursache
403 bei jedem RequestOrigin fehlt oder ist nicht freigegeben, falscher Schlüssel, Systemzeit weicht ab
403 nur bei Wiederholunghmac_unique wurde erneut verwendet, pro Request eine neue ID erzeugen
406Pflichtfeld fehlt oder Format falsch, die message nennt die betroffenen Felder
409 bei codesetup/getDie Kund:in hat noch nicht aktiviert, weiter abfragen
410 bei codesetup/getDer Code ist nach einer Stunde abgelaufen, neu erzeugen

Weitere Hinweise findest du unter Fehler & Statuscodes.

Mit 🌈 entwickelt in Berlin 🐻 & Köln ⛪
Hosting zu 100% in Deutschland 🇩🇪