Konzept

Versicherte können sich über das mobile FdV mittels Push-Notifications über Ereignisse in Fachdiensten (z.B. ePA-Aktensystem, E-Rezept-Fachdienst, Dienste der Kassen) informieren lassen. Der Versicherte kann im FdV aus den möglichen Ereignissen (zum Beispiel in Form von Channels) diejenigen auswählen, für die er benachrichtigt werden möchte. Standardmäßig werden keine Push-Notifications gesendet.

Architektur Status Please Comment green

An der Umsetzung des Notification Managements sind folgende Komponenten und Services beteiligt:

push overview
Abbildung 1. Systemüberblick

Der Zweck dieses Konzepts besteht darin, eine flexible und sichere Push Infrastruktur bereitzustellen, die es ermöglicht, dass verschiedene Fachdienste Push-Notifications an eine mobile Anwendung senden können. Durch die Implementierung eines zentralen Push Gateways pro Frontend wird eine einheitliche Schnittstelle geschaffen, die als Fassade für die plattformspezifischen APIs von Anbietern wie Google und Apple fungiert. Dies erlaubt es mehreren Backends, gleichzeitig Push-Benachrichtigungen für eine fremde App bereitzustellen, ohne dass jedes Backend individuell mit den plattformspezifischen APIs interagieren muss. Ein wesentlicher Vorteil dieses Ansatzes ist die erhöhte Sicherheit: Die sensiblen Zugangsdaten und Schlüssel, die für die Nutzung der plattformspezifischen Push-APIs erforderlich sind, verbleiben beim Push Gateway und müssen nicht mit den einzelnen Fachdiensten geteilt werden. Dadurch wird das Risiko eines unbefugten Zugriffs auf diese Geheimnisse minimiert und die Integrität der Kommunikation gewährleistet. Als Vorbild dient die Push Implementierung wie sie im Matrix Protokoll beschrieben ist.

secret channel.drawio
Abbildung 2. Veschlüsselter Kanal zwischen Fachdienst und FdV

Um zusätzliche Sicherheit zu gewährleisten kann eine Push-Nachricht zwischen dem Fachdienst und dem FdV verschlüsselt werden. Weder Push Gateway noch Push Provider besitzen die Information um eine Entschlüsselung vorzunehmen. Die Entscheidung ob eine Push-Nachricht verschlüsselt werden muss oder unverschlüsselt bleiben kann wird durch die Spezifikation des jeweiligen Fachdienstes vorgenommen. Bisher wird davon ausgegangen, dass eine Verschlüsselung für ePA und E-Rezept notwending sein wird, diese jedoch bei TIM optional ist.

Fachdienst Status Please Comment green

Der anwendungsspezifische Fachdienst verwaltet Geräte die sich bei ihm für den Push-Notification empfang registriert haben. Der Fachdienst erstellt Push-Notifications für vom Nutzer abonnierte Ereignisse und übermittelt diese an das zuständige Push Gateway. Er bietet Schnittstellen für die FdVs der Versicherten zur Registrierung und Konfiguration von Pushern an. Ein Pusher bezieht sich auf eine FdV-Instanz und ist eine Konfiguration im Fachdienst, in der die Informationen zur Adressierung der Push-Notifications hinterlegt werden (u.a. das zu nutzende Push Gateway, Schlüssel zur Verschlüsselung von Nachrichteninhalten). Der Versicherte kann für mehrere Endgeräte Pusher im Fachdienst hinterlegen.

Push Gateway Status Please Comment green

Das Push Gateway besitzt einen anwendungsübergreifenden Endpunkt, an den Push-Notifications übermittelt werden. Das Push Gateway leitet die Informationen der Push-Notification an den Push Provider weiter. Es wird vom Hersteller des FdV bereitgestellt, und es kann weitere Endpunkte für Kassendienste geben, die ebenfalls über dieses Push Gateway Notifications versenden.

Push Provider Status Please Comment green

Der Push Provider ist ein Service des Herstellers des mobilen Betriebssystems (z.B. Google, Apple). Der Push Provider sendet Notifications an App-Instanzen auf Endgeräten der Nutzer.

FdV Instanz Status Please Comment green

Die FdV-Instanz ist ein auf einem mobilen Endgerät installiertes FdV. Push-Notifications werden für eine FdV-Instanz registriert und an diese gesendet. Die FdV-Instanz kann mehrere Anwendungen integrieren (ePA, E-Rezept, TI-Messenger, Kassenanwendungen), für die der Versicherte jeweils Push-Notifications auswählen kann.

Authentisierung Status Please Comment green

push overview authentication
Abbildung 3. Authentisierung der beteiligten Komponenten

Die Verbindungen zwischen Push Gateway und den Fachdiensten sind beidseitig authentisiert und verschlüsselt. Die anderen sind Fachdienst oder Push-Provider spezifisch.

Verschlüsselung des Benachrichtigungsinhaltes Status Please Comment green

Push-Gateways können verschlüsselten und unverschlüsselte Push-Nachrichten empfangen und weiterleiten. Die Verschlüsselung des Benachrichtigungsinhaltes ist fachdienstspezifisch und wird durch die Spezifikation des Fachdienstes festgelegt.

Im Fall von Verschlüsselten Push-Nachrichten wird der Benachrichtigungsinhalt einer jeden Benachrichtigung mittels eines Authenticated-Encryption-Verfahrens verschlüsselt (AES/GCM), sodass der Inhalt der Benachrichtigung nicht von Dritten eingesehen oder verändert werden kann.

Wenn sich eine FdV-Instanz beim Fachdienst für Benachrichtigungen registriert, erzeugt die App ein initiales gemeinsames Geheimnis (initial-shared-secret (ISS)) und überträgt dieses kryptographisch gesichert an den Fachdienst. Registriert sich das FdV an verschieden Fachdiensten, so wird für jeden Fachdienst ein eigenes ISS erzeugt.

Dieses gemeinsame Geheimnis ist die Grundlage der kryptographischen Sicherung des Benachrichtigungsinhaltes. Die Benachrichtigung wird vom Fachdienst mit verschlüsseltem Benachrichtigungsinhalt über das Push Gateway und den Push Provider an die FdV-Instanz übermittelt.

Ganz ähnlich wie bei vielen Messaging-Anwendungen werden die verwendeten Schlüssel für die kryptographische Absicherung der Nachrichten regelmäßig gewechselt auf eine Weise, dass eine Wiederherstellbarkeit von alten Schlüsseln kryptographisch ausgeschlossen ist.

Der Fachdienst erhält ein ISS und einen Zeitstempel von dessen Erzeugung von dem FdV bei der Registrierung. Mittels einer "Hashed Message Authentication Code (HMAC)-based key derivation function" (HKDF) [RFC-5869] werden per HKDF(ISS, info="<Jahr>-<Monat>") zwei Werte abgeleitet:

  1. Ein Geheimnis für den Monat und Jahr des Zeitstempels (shared-secret-Jahr-Monat)

  2. ein AES/GCM-Schlüssel für den Monat und Jahr des Zeitstempels (AES/GCM-Schlüssel-Jahr-Monat).

Diese beiden Werte werden im Fachdienst sicher gespeichert und das ISS wird im Fachdienst gelöscht.

Beispiel für einen Austausch im Oktober 2023: Status Please Comment green

Das ISS sei zufällig erzeugt gleich (hexdump) f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2. Dann ergibt HKDF(ISS, info="2023-10", L=64) = (hexdump) 185fed66ea5cabbe00147bbd298b5dab0ed41b57ab254d35897b3a4504306e3b3b4adcd58dea98db8e9cb0f5763fcd04fe932d67926cc04b20ba2a2f304ffff9. Die ersten 32 Byte (256 Bit) sind das shared-secret-2023-10 gleich (hexdump) 185fed66ea5cabbe00147bbd298b5dab0ed41b57ab254d35897b3a4504306e3b und die letzten 32 Byte sind der AES/GCM-Schlüssel-2023-10 ist gleich (hexdump) 3b4adcd58dea98db8e9cb0f5763fcd04fe932d67926cc04b20ba2a2f304ffff9.

Soll im Oktober 2023 eine Benachrichtigung kryptographisch gesichert werden, um sie an das E-Rezept-FdV zu versenden, dann wird der Schlüssel AES/GCM-Key-2023-10 dafür verwendet. Soll im November 2023 eine Benachrichtigung gesichert werden, so muss per HKDF(shared-secret-2023-10, info="2023-11") wieder ein Geheimnis — diesmal für November 2023 — und ein neuer AES/GCM-Schlüssel abgeleitet werden. shared-secret-2023-11 = (hexdump) 0c8662d90b04818afb317406fe7fcfcf8d103cd9bc6ad7847890d28620e85ec3, AES/GCM-Schlüssel-2023-11 = (hexdump) 39aa5dacd538f53f4b956d84c9b8f2e26933274d160b9fd1a263a27681c6331b

Alle shared-secret-Jahr-Monat und alle AES/GCM-Schlüssel-Jahr-Monat, die älter sind als zwei Monate werden, sowohl im Notification Service als auch im E-Rezept-FdV gelöscht, jedoch niemals das jüngste noch verfügbare (auch wenn es älter als zwei Monate ist). Der fachliche Hintergrund von "zwei Monaten" ist, dass sichergestellt sein muss, dass falls der E-Rezept-FD die Benachrichtigung Sekunden vor Monatsende erstellt, und diese im E-Rezept-FdV erst nach einigen Sekunden dann im Folgemonat empfangen werden, die Entschlüsselung im E-Rezept-FdV immer noch möglich sein muss.

Sollte erst im Januar 2024 die nächste Benachrichtigung gesendet werden, so muss die Ableitung für 2023-12 erzeugt werden und darauf basierend anschließend die Ableitung für 2024-01. Anschließend werden die Ableitungs- und Schlüsseldaten für 2023-11 gelöscht. Die Schlüsseldaten für 2024-01 werden für die kryptographische Sicherung verwendet.

Somit erreicht man das Ziel, dass bei Kompromittierung eines AES/GCM-Jahr-Monat-Schlüssels nur die Benachrichtigungen der letzten zwei Monate entschlüsselt werden können.

Registrierung einer ePA-FdV-Instanz Status Please Comment green

Damit eine FdV-Instanz Push-Notifications empfangen kann, muss diese zunächst beim Push-Provider sowie in den gewünschten Fachdiensten registriert werden.

registration
Abbildung 4. Registrierung einer FdV-Instanz für Push-Notifications im Fachdienst Status Please Comment green
  1. Die FdV-Instanz registriert sich beim Push Provider und erhält ein pushkey, das die FdV-Instanz eindeutig identifiziert.

  2. Die FdV-Instanz erzeugt ein initial_shared_secret und speichert den Zeitpunkt (<Jahr>-<Monat>) zu welchem dieses erzeugt wurde als time_iss_created.

  3. Der Nutzer meldet sich beim Fachdienst (z.B. Aktensystem) an und registriert einen Pusher. Teil der Registrierungsdaten sind:

    • der pushkey,

    • die app_id,

    • die Art des Pushers (kind, hier immer "http"),

    • die Adresse des Push Gateways (data.url)

    • das initial_shared_secret

    • time_iss_created

      Die genauen Felder und Erklärungen dazu sind in der OpenAPI-Spezifikation zu finden. Die app_id und die Adresse des Push Gateways wurden vom Hersteller im FdV hinterlegt.

  4. Die FdV-Instanz und der Fachdienst erzeugen den ersten Schlüssel aus dem initial_shared_secret und dem time_iss_created. Anschließend löschen sie das initial_shared_secret.

Möchte der Versicherte Push Notifications von mehreren Fachdiensten erhalten, wird durch die FdV-Instanz in jedem dieser Fachdienste ein Pusher registriert.

Registrierung einer FdV-Instanz für Push-Notifications im Push-Provider
      {
        "lang": "en",
        "kind": "http",
        "app_display_name": "Mat Rix",
        "device_display_name": "iPhone 9",
        "app_id": "com.example.app.ios",
        "pushkey": "<APNS/GCM TOKEN>",
        "data": {
          "url": "https://push-gateway.location.here/_matrix/push/v1/notify"
        },
        "append": false
      }

Deregistrierung einer FdV-Instanz Status Please Comment green

Der gleiche Endpunkt wird sowohl zur Deregistrierung als auch zur Registrierung bei einer FdV-Instanz verwendet. Bei der Deregistrierung werden nur das pushkey, die app_id und kind (die Art des Pushers benötigt). Die Art des Pushers muss dann null sein, damit der Fachdienst weiß, dass der Pusher gelöscht werden soll.

Liste aktuelle Pushers enthalten Status Please Comment green

Die FdV-Instanz kann eine Liste aller registrierten Pusher des Nutzers durch eine GET Operation auf dem bestimmten Endpunkt des Fachdienstes erhalten. Die Details dazu sind in der OpenAPI-Spezifikation beschrieben.

Versenden von Push-Notifications Status Please Comment green

Die folgende Abbildung veranschaulicht den Ablauf, wenn ein Ereignis in einem Fachdienst eintritt, für welches Push-Benachrichtigungen gesendet werden sollen (z.B. wenn ein Nutzer ein neues Dokument in einem Konto des ePA-Aktensystems einstellt):

send push
Abbildung 5. Push-Notification-Versand
  1. Der Fachdienst führt folgende Schritte durch

    1. Erzeugen des Nachrichteninhalts für dieses spezifische Ereignis. Die Strukturierung ist fachdienstspezifisch.

    2. Erzeugen eines neuen gültigen Schlüssels, wenn kein gültiger vorhanden ist.

  2. Für jeden registrierten Pusher p, der für das Ereignis abonniert ist, wird eine Push-Benachrichtigung Notification_p mit mindestens folgenden Inhalten erzeugt (Die möglichen Felder und deren Beschreibungen sind auf der OpenAPI-Seite zu finden):

    1. ciphertext = Nachrichteninhalt aus 1a, Base64(IV || Chiffrat || Authentication Tag) verschlüsselt mit dem aktuell gültigen Schlüssel.

    2. time_message_encrypted = Zeitpunkt der Verschlüsselung des Nachrichteinhaltes.

    3. devices = (mindestens: app_id, push_token)

  3. Für jeden Pusher p wird die Push-Benachrichtigung Notification_p an das Push Gateways des Pushers p übermittelt.

  4. Das Push Gateway übermittelt die Push-Benachrichtigung Notification_p an den Push Provider.

  5. Der Push-Provider sendet die Notification an die zur push_token gehörende FdV-Instanz.

  6. Die FdV-Instanz entschlüsselt den Nachrichteninhalt mit dem aktuell gültigen Schlüssel (erzeugt ihn, wenn er nicht schon vorhanden ist) und zeigt dem Nutzer den Nachrichteninhalt entsprechend an.

  7. Bei Bedarf kann sich der Nutzer in der FdV-Instanz anmelden, um sich beispielsweise ein eingestelltes Dokument anzusehen.

Versenden aus Push Gateway Sicht Status Work In Progress red

Apple

HTTP-Header

Wert

Beschreibung

:method

POST

:path

/3/device/<pushkey>

authorization

<provider_token>

Required for token-based authentication

apns-push-type

alert

apns-id

apns-expiration

apns-priority

<prio != high ? 5 : 10>

apns-topic

apns-collapse-id

Themen für produktspezifische Implementierungen Status Please Comment green

Die folgenden Themen sind in diesem Dokument nicht enthalten, da sie zu stark von der produktspezifischen Implementierung abhängen:

  1. Lokalisierung: Die genaue Implementierung der Lokalisierung ist stark abhängig vom Payload und damit vom spezifischen Anwendungsfall.

  2. Channels: Die Anforderungen und Implementierung von Channels sind anwendungsspezifisch und können je nach Produkt variieren.

  3. Payload: Die genaue Struktur und der Inhalt der Nutzdaten können je nach Anwendungsfall und Produktfunktionalität unterschiedlich sein. Die Struktur wird im Fachkonzept der jeweiligen Anwendung spezifiziert.

  4. Implementierung des Push Gateways: Die technische Umsetzung des Push-Mechanismus kann von der gewählten Infrastruktur und den spezifischen Anforderungen des Push Gateways abhängen. Die Technologie entwickelt sich schnell weiter, was auch die Kommunikation zwischen Push Gateway und Push Providers beeinflusst.

  5. Berechtigung: Die Berechtigungen für die verschiedenen Endpunkte sind anwendungsfallspezifisch und können je nach Produkt variieren.

Für Hinweise zur Implementierung dieser Themen verweisen wir auf die produktspezifischen Spezifikationen und Implementierungsleitfäden.

Optionale Features können hier gefunden werden.

Beispiele Status Work In Progress red

Device Registrierung

Variante 1, Platform via app_id
{
    "lang": "en",
    "kind": "http",
    "app_display_name": "Mat Rix",
    "device_display_name": "iPhone 9",
    "app_id": "com.example.app.ios",
    "pushkey": "<APNS/GCM TOKEN>",
    "data": {
        "url": "https://push-gateway.location.here/_matrix/push/v1/notify"
    },
    "append": false
}
Variante 2, Platform via data
{
    "lang": "en",
    "kind": "http",
    "app_display_name": "Mat Rix",
    "device_display_name": "iPhone 9",
    "app_id": "com.example.app",
    "pushkey": "<APNS/GCM TOKEN>",
    "data": {
        "url": "https://push-gateway.location.here/_matrix/push/v1/notify",
        "platform": "ios"
    },
    "append": false
}
Variante 3, Platform via url
{
    "lang": "en",
    "kind": "http",
    "app_display_name": "Mat Rix",
    "device_display_name": "iPhone 9",
    "app_id": "com.example.app",
    "pushkey": "<APNS/GCM TOKEN>",
    "data": {
        "url": "https://push-gateway.location.here/ios/_matrix/push/v1/notify"
    },
    "append": false
}

Event-Trigger

Beispiel für ein Event-Trigger in ePA:

function trigger001() {
    user = getUser()
    devices = getDevices(user)
    channel = getChannel(trigger001)

    for device in devices {
        if device.channels[channel].isSubscribed {
            notification = createNotification(trigger001, device)
            sendNotification(notification, channel)
        }
    }
}

function createNotification(trigger, device) {
    month = date.now().month // "2024-11"
    encryptionKey = getEncryptionKey(device, month)

    payload = {
        event: "trigger001"
    }

    iv = random(32)
    (cipher, authTag) = aesEncrypt(payload)

    ciphertext = Base64(iv + cipher + authTag)

    return {
        month: month,
        ciphertext: ciphertext,
        devices: [
            app_id: device.app_id,
            pushkey: device.pushkey,
            pushkey_ts: device.pushkey_ts,
            data: device.data,
            tweaks: {}
        ]
    }
}

Push Gateway

IN

Push Gateway payload received
{
    "month": "2024-11",
    "ciphertext": "asdfdfjksfjklsdljkdsf==",
    "prio": "high",
    "counts": {},
    "devices": [
        {
            "app_id": "de.gematikkk.app.ios",
            "pushkey": "abcd-efghi-jklm-nopq",
            "pushkey_ts": 0,
            "data": {
                "format": "string"
            },
            "tweaks": {
            }
        }
    ]
}

OUT

APNS (Apple Push Notification Service)
Table 1. HTTP-Header

HTTP-Header

Wert

Beschreibung

:method

POST

:path

/3/device/<pushkey>

authorization

<provider_token>

Required for token-based authentication

apns-push-type

alert

apns-priority

<prio != high ? 5 : 10>

Push Gateway payload send to Push Provider
Payload:
{
    "aps": {
        "mutable-content": true
    }
    "ciphertext": notification.ciphertext,
    "month": notification.month
}
Push Gateway pseudo code
if notification.counts.badge then
    payload.aps.badge = notification.counts.badge
end if
FCM (Firebase Cloud Messaging)
Table 2. HTTP-Header

HTTP-Header

Wert

Beschreibung

:method

POST

:Content-Type

application/json

Defines the format of the request

:access_token_auth

true

Confirms token-based authentication (optional, specific to certain systems)

authorization

Authorization: Bearer <provider_token>

Required for token-based authentication.

Push Gateway payload send to Push Provider
Payload:
{
  "message":{
    "token":"PUSH_KEY",
    "android": {
      "priority": "HIGH",
    }
    "data":{
      "ciphertext": "ENCRYPTED_DATA_HERE",
      "month": notification.month
    }
  }
}

Mobile App

iOS-Extension:

Swift Pseudo Code
func didReceive(
    _ request: UNNotificationRequest,
    withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
  // Retrieve the push notification payload
    let payload = request.content.userInfo

    // Decrypt the ciphertext
    let ciphertext = payload["ciphertext"]
    let plaintext = decrypt(ciphertext)

    // Update the notification content
    let content = request.content.mutableCopy() as! UNMutableNotificationContent
    let title = LocalizedString("notification_title_\(event_id)")
    let body = LocalizedString("notification_body_\(event_id)")
    content.title = title
    content.body = body

    // Call the completion handler with the updated notification content
    contentHandler(content)
}

Android:

Kotlin Pseudo Code
fun onMessageReceived(
    request: RemoteMessage
) {
    // Retrieve the push notification payload
    val payload = request.data

    // Decrypt the ciphertext
    val ciphertext = payload["ciphertext"]
    val plaintext = decrypt(ciphertext)

    // Update the notification content
    val eventId = payload["event_id"]

    val title = resources.getString(resources.getIdentifier("notification_title_$eventId", "string", packageName))
    val body = resources.getString(resources.getIdentifier("notification_body_$eventId", "string", packageName))

    // Display the notification using the processed title and body
    showNotification(title, body)
}

fun showNotification(title: String?, body: String?) {
    val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    val notificationId = 1

     // Create a notification channel for Android O (API 26) and above
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            "default_channel",
            "Default Channel",
            NotificationManager.IMPORTANCE_HIGH
        )
        notificationManager.createNotificationChannel(channel)
    }
    // Build the notification using NotificationCompat for backward compatibility
    val notification = NotificationCompat.Builder(this, "default_channel")
        .setContentTitle(title)
        .setContentText(body)
        .build()
    // Display the notification with a unique notification ID
    notificationManager.notify(notificationId, notification)
}

Sync: 22.11.2024 14:56