Der Proof-of-Action (PoA)-Mechanismus ist ein zentrales Sicherheitsmerkmal von OmniOmni, das die Integrität und Authentizität sensibler Aktionen gewährleistet, die von Benutzern und Kundensystemen durchgeführt werden. Er bietet eine robuste Methode zur Verifizierung, dass eine bestimmte Aktion von der berechtigten Partei autorisiert wurde. PoA wird in verschiedenen Szenarien eingesetzt, darunter:

  • Investitionsgenehmigungen: Überprüfung, dass eine Investitionsanfrage vom Investor autorisiert wurde.
  • Übertragungsautorisierungen: Bestätigung, dass eine Token-Übertragung sowohl vom Absender als auch vom Empfänger genehmigt wurde.
Falls Sie nicht eine der ecrop-Lösungen zur Verwaltung von Parteien (Endbenutzern) nutzen, beachten Sie, dass bei der Erstellung einer Partei deren öffentlicher Schlüssel zusammen mit den Identifikationsdetails der Partei übermittelt werden muss. Dies ist eine Voraussetzung für das Funktionieren von PoA. Eine Änderung des öffentlichen Schlüssels erfordert zudem eine erneute Identifizierung der Partei.

Funktionsweise von PoA

PoA nutzt kryptografische Signaturen und Zeitstempel, um die Authentizität von Aktionen zu überprüfen. Hier ist eine Übersicht über den Prozess:

  1. Anfrage-Signierung (Client-seitig): Wenn ein Benutzer oder ein Kundensystem eine sensible Aktion initiiert (z. B. eine Investition genehmigt), werden die folgenden Daten zu einer einzelnen Zeichenkette zusammengefügt, getrennt durch Punkte (.):
    • HTTP-Methode (z. B. POST, PATCH)
    • Anfrage-Payload (bei JSON, alle Leerzeichen entfernen)
    • URI (beginnend mit RESTful-Ressourcennamen; Suchparameter sollten alphabetisch sortiert sein)
    • Zeitstempel (UTC, ISO 8601-Format)
    • Geräte-ID (eindeutiger Identifikator für das Gerät, das die Anfrage stellt. Bei mobilen Anwendungen ist dies die Geräte-ID des Mobilgeräts, bei API-Anfragen entspricht sie dem für die Authentifizierung verwendeten Client-Token.)
  2. Signaturerstellung: Ein SHA-256-Hash der Zeichenkette wird erstellt und anschließend mit dem privaten Schlüssel der handelnden Partei (Benutzer oder Kundensystem) verschlüsselt. Die resultierende Signatur wird dann Base64-kodiert.
  3. Anfrage-Header: Die folgenden Header werden in der API-Anfrage übermittelt:
    • X-Signature: Die Base64-kodierte Signatur.
    • X-Signature-DateTime: Der zur Signaturerstellung verwendete Zeitstempel.
    • X-Signature-DeviceId: Die Geräte-ID (falls verwendet).
  4. Signaturüberprüfung (Server-seitig): Nach Erhalt der Anfrage überprüft OmniOmni die Signatur mit den folgenden Schritten:
    • Abruf des öffentlichen Schlüssels der handelnden Partei. (Genauere Details zur Schlüsselabfrage müssen noch geklärt werden.)
    • Rekonstruktion der signierten Datenzeichenkette basierend auf den Anfrageinformationen.
    • Verifizierung der empfangenen Signatur mithilfe des öffentlichen Schlüssels des Endbenutzers.

Wichtige Überlegungen

  • Schlüsselverwaltung: Die Sicherheit des PoA-Mechanismus hängt stark von der sicheren Verwaltung der privaten Schlüssel ab. Stellen Sie sicher, dass private Schlüssel niemals offengelegt und sicher gespeichert werden.
  • Genauigkeit des Zeitstempels: Genaue Zeitstempel sind entscheidend zur Vermeidung von Replay-Angriffen. Synchronisieren Sie Ihre Systemuhr mit einer zuverlässigen Zeitquelle.
  • Fehlermanagement: Implementieren Sie eine robuste Fehlerbehandlung, um Szenarien zu verwalten, in denen die Signatur ungültig ist oder nicht verifiziert werden kann.

Technische Implementierung

Datenformat: JSON Web Signature (JWS) - RFC 7515 JWS ist eine kompakte, URL-sichere Methode zur Darstellung signierter Inhalte mithilfe von JSON-Datenstrukturen. Es ermöglicht die digitale Signierung von Informationen wie HTTP-Anfragen und -Antworten auf eine kompakte und sichere Weise.

Signiermethode: JSON Web Algorithms (JWA) - RFC 7518 JWA definiert eine Reihe von Algorithmen zur Sicherung digitaler Signaturen und zur Verschlüsselung von JSON-Daten. Es legt fest, welche Algorithmen mit JWS verwendet werden können, um Integrität und Authentizität zu gewährleisten.

Gemeinsame Konzepte und Vereinbarungen zwischen Client und Server

  • Schlüsselpaar-Algorithmus: JWS mit dem RSA-Algorithmus, angegeben als RS256 in JWA (JSON Web Algorithms - RFC 7518).
  • Schlüssellänge: 2048 Bit
  • Signaturalgorithmus: RSA Signature-Scheme-with-Appendix (RSASSA)
  • Format der zu signierenden Daten: Alle folgenden Bestandteile werden zu einer Zeichenkette mit dem Trennzeichen „.“ zusammengefügt:
Example to build the string that should be signed
    public static String prepareSignaturePayload(String method, String payload, String url, String dateTimeString, String deviceId) {
        String data = new StringJoiner(".")
                .add(method)
                .add(payload)
                .add(url)
                .add(dateTimeString)
                .add(deviceId)
                .toString();
        return data;
        // result e.g.:  POST.{"state":"WAITING"}./test/echo-poa?name=John&state=SENDER_APPROVAL_WAITING.2024-01-22T23:54:07.145771486.Device-id
    }
    public SignatureInfo sign(HttpMethod httpMethod, String url, Object payload, String deviceId, PrivateKey privateKey) throws JsonProcessingException, JOSEException {
        String payloadStr = payload == null ? "" : objectMapper.writeValueAsString(payload);
        String dateTimeString = dateFormat.format(new Date());
        String data = prepareSignaturePayload(httpMethod.toString(), payloadStr, url, dateTimeString, deviceId);
        JWSObject jwsObject = new JWSObject(
                new JWSHeader.Builder(JWSAlgorithm.RS256).build(),
                new Payload(data)
        );
        jwsObject.sign(new RSASSASigner(privateKey));
        String signature = jwsObject.serialize();
        signature = removePayloadFromCompactJws(signature);
        SignatureInfo signatureInfo = new SignatureInfo(dateTimeString, deviceId, signature);
        return signatureInfo;
    }
    public String prepareSignaturePayload(String method, String payload, String url, String dateTimeString, String deviceId) {
        return new StringJoiner(".")
                .add(method)
                .add(payload)
                .add(url)
                .add(dateTimeString)
                .add(deviceId)
                .toString();
    }
    public static String removePayloadFromCompactJws(String signature) {
        String[] parts = signature.split("\\.");
        return parts[0] + ".." + parts[2];
    }