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.
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:
- SD-JWT:Entspricht der IETF-Spezifikation für auf SD-JWT basierende überprüfbare Anmeldedaten (SD-JWT VC).
- Mobile Dokumente (mdocs):Entsprechen der Spezifikation ISO/IEC 18013-5:2021.
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
idzu 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
idversehen, 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
idaufgerufen 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
OpenId4VpRegistryenthält automatisch den Standard-MatcherOpenId4VP(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
getCallingAppInfoabgerufen 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
getCredentialOptionsabgerufen wird. Bei einem Ablauf für digitale Anmeldedaten ist in dieser Liste nur einGetDigitalCredentialOptionzu 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
- Extrahieren Sie
ProviderGetCredentialRequestaus dem Intent:
val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
- 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 vonCallingAppInfoü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.