Holder-App in Credential Manager einbinden

Mit der Credential Manager Holder API können Sie in Ihrer Android-App für Inhaber (auch „Wallet“-App genannt) digitale Berechtigungsnachweise verwalten und Prüfern präsentieren.

Bild der Benutzeroberfläche für digitale Anmeldedaten im Credential Manager
Abbildung 1: Benutzeroberfläche der Auswahl für digitale Anmeldedaten.

Zentrale Begriffe und Funktionsweise

Bevor Sie die Holder API verwenden, sollten Sie sich mit den folgenden Konzepten vertraut machen.

Formate von Anmeldedaten

Anmeldedaten können in Inhaber-Apps in verschiedenen Anmeldedatenformaten gespeichert werden. Diese Formate sind Spezifikationen dafür, wie ein Ausweisdokument dargestellt werden soll. Jedes Format enthält die folgenden Informationen zum Ausweisdokument:

  • Typ:Die Kategorie, z. B. Hochschulabschluss oder Führerschein.
  • Attribute:Attribute wie Vor- und Nachname.
  • Codierung:Die Art und Weise, wie die Anmeldedaten strukturiert sind, z. B. SD-JWT oder mdoc
  • Gültigkeit:Methode zur kryptografischen Überprüfung der Authentizität des Anmeldedaten.

Die Codierung und Validierung erfolgt in den einzelnen Anmeldedatenformaten etwas anders, aber die Funktion ist dieselbe.

Die Registrierung unterstützt zwei Formate:

Ein Prüfer kann eine OpenID4VP-Anfrage für SD-JWT und mobile Dokumente stellen, wenn er den Credential Manager verwendet. Die Auswahl hängt vom Anwendungsfall und der Branche ab.

Registrierung von Anmeldedaten-Metadaten

Im Credential Manager werden die Anmeldedaten eines Inhabers nicht direkt gespeichert, sondern die Metadaten der Anmeldedaten. Eine Inhaber-App muss zuerst Anmeldedaten-Metadaten mit RegistryManager beim Credential Manager registrieren. Bei diesem Registrierungsprozess wird ein Registrierungseintrag erstellt, der zwei Hauptzwecken dient:

  • Abgleich:Registrierte Anmeldedaten-Metadaten werden verwendet, um sie mit zukünftigen Anfragen von Prüfern abzugleichen.
  • Anzeige:Dem Nutzer werden in der Anmeldedaten-Auswahl benutzerdefinierte UI-Elemente angezeigt.

Sie verwenden die Klasse OpenId4VpRegistry, um Ihre digitalen Berechtigungsnachweise zu registrieren, da sie sowohl das mdoc- als auch das SD-JWT-Format unterstützt. Prüfer senden OpenID4VP-Anfragen, um diese Anmeldedaten anzufordern.

Anmeldedaten Ihrer App registrieren

Wenn Sie die Credential Manager Holder API verwenden möchten, fügen Sie dem Build-Skript Ihres App-Moduls die folgenden Abhängigkeiten hinzu:

Groovy

dependencies {
    // Use to implement credentials registrys

    implementation "androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha04"
    implementation "androidx.credentials.registry:registry-digitalcredentials-preview:1.0.0-alpha04"
    implementation "androidx.credentials.registry:registry-provider:1.0.0-alpha04"
    implementation "androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha04"

}

Kotlin

dependencies {
    // Use to implement credentials registrys

    implementation("androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha04")
    implementation("androidx.credentials.registry:registry-digitalcredentials-preview:1.0.0-alpha04")
    implementation("androidx.credentials.registry:registry-provider:1.0.0-alpha04")
    implementation("androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha04")

}

RegistryManager erstellen

Erstellen Sie eine RegistryManager-Instanz und registrieren Sie eine OpenId4VpRegistry-Anfrage damit.

// Create the registry manager
val registryManager = RegistryManager.create(context)

// The guide covers how to build this out later
val registryRequest = OpenId4VpRegistry(credentialEntries, id)

try {
    registryManager.registerCredentials(registryRequest)
} catch (e: Exception) {
    // Handle exceptions
}

OpenId4VpRegistry-Anfrage erstellen

Wie bereits erwähnt, müssen Sie ein OpenId4VpRegistry registrieren, um eine OpenID4VP-Anfrage von einem Prüfer zu verarbeiten. Wir gehen davon aus, dass Sie einige lokale Datentypen mit Ihren Wallet-Anmeldedaten geladen haben (z. B. sdJwtsFromStorage). Sie konvertieren sie nun basierend auf ihrem Format in unsere Jetpack-Entsprechungen DigitalCredentialEntry – SdJwtEntry für SD-JWT oder MdocEntry für mdoc.

Sd-JWTs in die Registrierung aufnehmen

Ordnen Sie jede lokale SD-JWT-Anmeldedaten einem SdJwtEntry für die Registrierung zu:

fun mapToSdJwtEntries(sdJwtsFromStorage: List<StoredSdJwtEntry>): List<SdJwtEntry> {
    val list = mutableListOf<SdJwtEntry>()

    for (sdJwt in sdJwtsFromStorage) {
        list.add(
            SdJwtEntry(
                verifiableCredentialType = sdJwt.getVCT(),
                claims = sdJwt.getClaimsList(),
                entryDisplayPropertySet = sdJwt.toDisplayProperties(),
                id = sdJwt.getId() // Make sure this cannot be readily guessed
            )
        )
    }
    return list
}

mdocs in die Registry einfügen

Ordnen Sie Ihre lokalen mdoc-Anmeldedaten dem Jetpack-Typ MdocEntry zu:

fun mapToMdocEntries(mdocsFromStorage: List<StoredMdocEntry>): List<MdocEntry> {
    val list = mutableListOf<MdocEntry>()

    for (mdoc in mdocsFromStorage) {
        list.add(
            MdocEntry(
                docType = mdoc.retrieveDocType(),
                fields = mdoc.getFields(),
                entryDisplayPropertySet = mdoc.toDisplayProperties(),
                id = mdoc.getId() // Make sure this cannot be readily guessed
            )
        )
    }
    return list
}

Wichtige Punkte zum Code

  • Eine Möglichkeit, das Feld id zu konfigurieren, besteht darin, eine verschlüsselte Anmeldedaten-ID zu registrieren, damit nur Sie den Wert entschlüsseln können.
  • Die Felder für die UI-Anzeige beider Formate sollten lokalisiert werden.

Anmeldedaten registrieren

Kombinieren Sie die konvertierten Einträge und registrieren Sie die Anfrage mit dem RegistryManager:

val credentialEntries = mapToSdJwtEntries(sdJwtsFromStorage) + mapToMdocEntries(mdocsFromStorage)

val openidRegistryRequest = OpenId4VpRegistry(
    credentialEntries = credentialEntries,
    id = "my-wallet-openid-registry-v1" // A stable, unique ID to identify your registry record.
)

Jetzt können wir Ihre Anmeldedaten bei CredentialManager registrieren.

try {
    val response = registryManager.registerCredentials(openidRegistryRequest)
} catch (e: Exception) {
    // Handle failure
}

Sie haben Ihre Anmeldedaten jetzt im Credential Manager registriert.

Verwaltung von App-Metadaten

Die Metadaten, die Ihre Wallet-App bei Credential Manager registriert, haben die folgenden Eigenschaften:

  • Persistenz:Die Informationen werden lokal gespeichert und bleiben auch nach Neustarts erhalten.
  • Abgeschotteter Speicher:Die Registrierungseinträge jeder App werden separat gespeichert. Das bedeutet, dass eine App die Registrierungseinträge einer anderen App nicht ändern kann.
  • Schlüsselbasierte Updates:Die Registrierungseinträge jeder App werden mit einem id versehen, sodass Einträge neu identifiziert, aktualisiert oder gelöscht werden können.
  • Metadaten aktualisieren:Es empfiehlt sich, die gespeicherten Metadaten immer dann zu aktualisieren, wenn sich Ihre App ändert oder zum ersten Mal geladen wird. Wenn ein Register mehrmals unter demselben id aufgerufen wird, werden alle vorherigen Datensätze durch den letzten Aufruf überschrieben. Wenn Sie den Eintrag aktualisieren möchten, registrieren Sie ihn einfach noch einmal, ohne den alten Eintrag zuerst löschen zu müssen.

Optional: Matcher erstellen

Ein Matcher ist ein Wasm-Binärprogramm, das Credential Manager in einer Sandbox ausführt, um Ihre registrierten Anmeldedaten anhand einer eingehenden Prüferanfrage zu filtern.

  • Standard-Matcher:Die Klasse OpenId4VpRegistry enthält automatisch den Standard-Matcher OpenId4VP (OpenId4VpDefaults.DEFAULT_MATCHER), wenn Sie sie instanziieren. Bei allen Standard-OpenID4VP-Anwendungsfällen übernimmt die Bibliothek die Zuordnung für Sie.
  • Benutzerdefinierter Abgleich: Sie würden nur dann einen benutzerdefinierten Abgleich implementieren, wenn Sie ein nicht standardmäßiges Protokoll unterstützen, das eine eigene Abgleichslogik erfordert.

Ausgewählte Anmeldedaten verarbeiten

Wenn ein Nutzer Anmeldedaten auswählt, muss Ihre Inhaber-App die Anfrage verarbeiten. Sie müssen eine Aktivität definieren, die auf den Intent-Filter androidx.credentials.registry.provider.action.GET_CREDENTIAL wartet. In unserem Beispiel-Wallet wird dieses Verfahren veranschaulicht.

Der Intent startet Ihre Aktivität mit der Verifier-Anfrage und dem Aufruferursprung, den Sie mit der Funktion PendingIntentHandler.retrieveProviderGetCredentialRequest extrahieren. Dadurch wird ein ProviderGetCredentialRequest mit allen Informationen zurückgegeben, die mit der Anfrage des Prüfers verknüpft sind. Es gibt drei Hauptkomponenten:

  • Die Anruf-App:Die App, die die Anfrage gestellt hat. Sie kann mit getCallingAppInfo abgerufen werden.
  • Die ausgewählte Anmeldedaten:Informationen dazu, welchen Kandidaten der Nutzer ausgewählt hat, abgerufen über selectedCredentialSet extension method. Dies entspricht der von Ihnen registrierten Anmeldedaten-ID.
  • Spezifische Anfragen:Die spezifische Anfrage des Prüfers, die über die Methode getCredentialOptions abgerufen wird. Bei einem Ablauf für digitale Anmeldedaten ist in dieser Liste nur ein GetDigitalCredentialOption zu finden.

In den meisten Fällen sendet der Prüfer eine Anfrage zur Präsentation digitaler Anmeldedaten, die Sie mit dem folgenden Beispielcode verarbeiten können:

request.credentialOptions.forEach { option ->
    if (option is GetDigitalCredentialOption) {
        Log.i(TAG, "Got DC request: ${option.requestJson}")
        processRequest(option.requestJson)
    }
}

Ein Beispiel dafür finden Sie in der Beispiel-Wallet.

Identität des Prüfers prüfen

  1. Extrahieren Sie ProviderGetCredentialRequest aus dem Intent:
val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
  1. Privilegierte Herkunft prüfen:Privilegierte Apps wie Webbrowser können durch Festlegen des Parameters „origin“ Aufrufe im Namen anderer Prüfer vornehmen. Um diesen Ursprung abzurufen, müssen Sie eine Liste privilegierter und vertrauenswürdiger Aufrufer (eine Zulassungsliste im JSON-Format) an die getOrigin() API von CallingAppInfo übergeben.
val origin = request?.callingAppInfo?.getOrigin(
    privilegedAppsJson // Your allow list JSON
)

Wenn „origin“ nicht leer ist:Der Ursprung wird zurückgegeben, wenn die packageName und die aus signingInfo abgerufenen Zertifikatsfingerabdrücke mit denen einer App übereinstimmen, die in der Zulassungsliste enthalten ist, die an die getOrigin() API übergeben wurde. Nachdem der Ursprungswert abgerufen wurde, sollte die Anbieter-App dies als privilegierten Aufruf betrachten und diesen Ursprung in der OpenID4VP-Antwort festlegen, anstatt den Ursprung anhand der Signatur der aufrufenden App zu berechnen.

Der Google Passwortmanager verwendet eine öffentlich verfügbare Zulassungsliste für Aufrufe von getOrigin(). Als Anmeldedatenanbieter können Sie diese Liste verwenden oder eine eigene im von der API beschriebenen JSON-Format bereitstellen. Es liegt im Ermessen des Anbieters, welche Liste verwendet wird. Informationen zum Erhalten eines privilegierten Zugriffs mit Drittanbieter-Anmeldedaten finden Sie in der Dokumentation des Drittanbieters.

Wenn „origin“ leer ist, stammt die Verifizierungsanfrage von einer Android-App. Der App-Ursprung, der in die OpenID4VP-Antwort eingefügt werden soll, wird als android:apk-key-hash:<encoded SHA 256 fingerprint> berechnet.

val appSigningInfo = request?.callingAppInfo?.signingInfoCompat?.signingCertificateHistory[0]?.toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val certHash = Base64.encodeToString(md.digest(appSigningInfo), Base64.NO_WRAP or Base64.NO_PADDING)
return "android:apk-key-hash:$certHash"

Benutzeroberfläche für Karteninhaber rendern

Wenn Anmeldedaten ausgewählt werden, wird die Inhaber-App aufgerufen und der Nutzer wird durch die Benutzeroberfläche der App geführt. Es gibt zwei Standardmethoden für diesen Workflow:

  • Wenn für die Freigabe der Anmeldedaten eine zusätzliche Nutzerauthentifizierung erforderlich ist, verwenden Sie die BiometricPrompt API. Im Beispiel wird dies veranschaulicht.
  • Andernfalls entscheiden sich viele Wallets für eine stille Rückgabe, indem sie eine leere Aktivität rendern, die die Daten sofort an die aufrufende App zurückgibt. Dadurch werden Nutzerklicks minimiert und eine nahtlosere Nutzung ermöglicht.

Anmeldedatenantwort zurückgeben

Sobald Ihre Inhaber-App bereit ist, das Ergebnis zurückzusenden, beenden Sie die Aktivität mit der Anmeldedatenantwort:

PendingIntentHandler.setGetCredentialResponse(
    resultData,
    GetCredentialResponse(DigitalCredential(response.responseJson))
)
setResult(RESULT_OK, resultData)
finish()

Wenn es eine Ausnahme gibt, können Sie die Anmeldedaten-Ausnahme auf ähnliche Weise senden:

PendingIntentHandler.setGetCredentialException(
    resultData,
    GetCredentialUnknownException() // Configure the proper exception
)
setResult(RESULT_OK, resultData)
finish()

Ein vollständiges Beispiel für die Rückgabe der Anmeldedatenantwort im Kontext finden Sie in der Beispiel-App.