Mit der Credential Manager Holder API kann Ihre Android-Inhaber-App (auch „Wallet“ genannt) digitale Berechtigungsnachweise verwalten und Prüfern präsentieren. In diesem Leitfaden wird beschrieben, wie Sie die Anmeldedaten Ihrer Inhaber-App im Credential Manager registrieren und wie Sie eine Präsentationsanfrage verarbeiten, wenn ein Nutzer Anmeldedaten auswählt. Die Holder API wird ab Android 6 (API-Level 23) unterstützt.
Abhängigkeiten hinzufügen
Wenn Sie die Credential Manager Holder API verwenden möchten, fügen Sie dem build.gradle-Script Ihres App-Moduls die folgenden Abhängigkeiten hinzu:
dependencies {
// Used to implement credential registries
implementation "androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha05"
implementation "androidx.credentials.registry:registry-digitalcredentials-openid:1.0.0-alpha05"
implementation "androidx.credentials.registry:registry-digitalcredentials-sdjwtvc:1.0.0-alpha05"
implementation "androidx.credentials.registry:registry-provider:1.0.0-alpha05"
implementation "androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha05"
}
Anmeldedaten registrieren
Erstellen Sie zuerst eine RegistryManager-Instanz, mit der Sie eine OpenId4VpRegistry-Anfrage registrieren.
// Create the registry manager
val registryManager = RegistryManager.create(context)
OpenId4VpRegistry-Anfrage erstellen
Sie müssen einen OpenId4VpRegistry registrieren, um OpenID4VP-Anfragen von Prüfern zu verarbeiten. Angenommen, Sie haben lokale Datentypen mit Ihren Wallet-Anmeldedaten geladen (z. B. sdJwtsFromStorage und mdocsFromStorage). Dann müssen Sie sie in die entsprechenden Jetpack-DigitalCredentialEntry-Äquivalente umwandeln: SdJwtEntry und MdocEntry.
SD-JWTs zur Registry hinzufügen
Ordnen Sie jeden lokalen SD-JWT-Berechtigungsnachweis einem SdJwtEntry für die Registry 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 zur Registry hinzufü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
- Verschlüsselte IDs: Eine Methode zum Konfigurieren des Felds
IDbesteht darin, eine verschlüsselte Anmeldedaten-ID zu registrieren, sodass nur Sie den Wert entschlüsseln können. - Lokalisierung: Die UI-Anzeigefelder für beide Formate sollten lokalisiert werden.
Anfrage kombinieren und 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.
)
Optional: Inline-Ausstellung
Wenn für einen Nutzer keine vorhandenen Anmeldedaten eines angeforderten Typs verfügbar sind, können Sie eine Option zum Hinzufügen von Anmeldedaten zu Ihrem Inhaber anzeigen. Fügen Sie dazu Ihrer OpenId4VpRegistry eine inlineIssuanceEntries-Liste mit den unterstützten Anmeldedaten hinzu:
val openidRegistryRequest = OpenId4VpRegistry(
credentialEntries = credentialEntries,
inlineIssuanceEntries = emptyList(),
listOf(
MdocInlineIssuanceEntry(
id = "Issuance",
display = InlineIssuanceEntry.InlineIssuanceDisplayProperties(
subtitle = "Mobile Drivers License, State Id, and Others",
),
supportedMdocs = setOf(
MdocInlineIssuanceEntry.SupportedMdoc(
"eu.europa.ec.eudi.pid.1"
),
MdocInlineIssuanceEntry.SupportedMdoc(
"org.iso.18013.5.1.mDL1"
),
)
),
SdJwtInlineIssuanceEntry(
id = "sd-jwt-issuance",
display = InlineIssuanceEntry.InlineIssuanceDisplayProperties(
subtitle = "Mobile Drivers License, State Id, and Others",
),
supportedSdJwts = setOf(
SdJwtInlineIssuanceEntry.SupportedSdJwt("urn:openid:interop:id:1")
)
)
),
id = "my-wallet-openid-registry-v1"
)
Registrieren Sie Ihre Anmeldedaten bei CredentialManager.
try {
val response = registryManager.registerCredentials(openidRegistryRequest)
} catch (e: Exception) {
// Handle failure
}
Verwaltung von App-Metadaten
Die Metadaten, die Ihre Inhaber-App bei CredentialManager registriert, haben die folgenden Attribute:
- Persistenz: Die Informationen werden lokal gespeichert und bleiben auch nach einem Neustart erhalten.
- Isolierter Speicher: Die Registry-Einträge jeder App werden separat gespeichert. Das bedeutet, dass eine App die Registry-Einträge einer anderen App nicht ändern kann.
- Schlüsselbasierte Aktualisierungen: Die Registrierungseinträge jeder App sind mit einer
idversehen, sodass Einträge re-identifiziert, aktualisiert oder gelöscht werden können. - Metadaten aktualisieren: Es empfiehlt sich, die gespeicherten Metadaten immer dann zu aktualisieren, wenn sich die App ändert oder zum ersten Mal geladen wird. Wenn eine Registry mehrmals unter derselben
idaufgerufen wird, überschreibt der letzte Aufruf alle vorherigen Einträge. Wenn Sie die Registrierung aktualisieren möchten, registrieren Sie sich einfach noch einmal. Sie müssen den alten Eintrag nicht löschen.
Optional: Matcher erstellen
Ein Matcher ist eine Wasm-Binärdatei, die der Credential Manager in einer Sandbox ausführt, um Ihre registrierten Ausweisdokumente anhand einer eingehenden Anfrage des Verifizierers zu filtern.
- Standard-Matcher: Die Klasse
OpenId4VpRegistryenthält beim Instanziieren automatisch den Standard-MatcherOpenId4VP(OpenId4VpDefaults.DEFAULT_MATCHER). Bei allen Standard-OpenID4VP-Anwendungsfällen übernimmt die Bibliothek den Abgleich für Sie. - Benutzerdefinierter Matcher: Sie würden einen benutzerdefinierten Matcher nur implementieren, wenn Sie ein nicht standardmäßiges Protokoll unterstützen, das eine eigene Abgleichslogik erfordert.
Ausgewählte Anmeldedaten verarbeiten
Wenn ein Nutzer einen Berechtigungsnachweis 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 reagiert.
In unserem Beispiel-Wallet wird dieses Verfahren veranschaulicht.
Der Intent startet Ihre Aktivität mit der Verifier-Anfrage und dem aufrufenden Ursprung, die Sie mit derPendingIntentHandler.retrieveProviderGetCredentialRequest-Funktion extrahieren. Dadurch wird ein ProviderGetCredentialRequest mit allen Informationen zurückgegeben, die mit der Anfrage des Verifizierers verknüpft sind. Es gibt drei Hauptkomponenten:
- Die aufrufende App: Die App, die die Anfrage gestellt hat, abrufbar mit
getCallingAppInfo. Damit kannst du die Identität des Prüfers überprüfen. - Der ausgewählte Berechtigungsnachweis: Informationen darüber, welchen Kandidaten der Nutzer ausgewählt hat, abgerufen über die
selectedCredentialSet extension method. Diese entsprechen der von Ihnen registrierten Berechtigungsnachweis-ID. - Spezifische Anfragen: Die spezifische Anfrage des Verifizierers, die mit der Methode
getCredentialOptionsabgerufen wird. Bei einem Anfrageablauf für digitale Ausweise finden Sie in dieser Liste voraussichtlich einen einzelnenGetDigitalCredentialOption.
Sie können die Präsentationsanfrage mit dem folgenden Code verarbeiten:
request.credentialOptions.forEach { option ->
if (option is GetDigitalCredentialOption) {
Log.i(TAG, "Got DC request: ${option.requestJson}")
processRequest(option.requestJson)
}
}
Ein Beispiel hierfür finden Sie im Beispiel-Wallet.
Identität des Prüfers überprüfen
Extrahieren Sie zuerst die ProviderGetCredentialRequest aus dem Intent:
val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
Nach privilegierten Apps suchen: Privilegierte Apps (z. B. Webbrowser) können im Namen anderer Prüfer Aufrufe ausführen, indem sie den Parameter „origin“ festlegen. Um diesen Ursprung abzurufen, übergeben Sie eine Liste privilegierter und vertrauenswürdiger Aufrufer (eine Zulassungsliste im JSON-Format) an die getOrigin-API von CallingAppInfo.
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 Zertifikat-Fingerabdrücke mit einer App auf Ihrer Zulassungsliste übereinstimmen. Betrachten Sie dies als privilegierten Aufruf und legen Sie diesen Ursprung in der OpenID4VP-Antwort fest, anstatt den Ursprung mithilfe der Signatur der Anruf-App zu berechnen.
Wenn origin leer ist: Die Anfrage des Verifizierers stammt von einer Android-App. Der Ursprung, der der OpenID4VP-Antwort zugeordnet ist, sollte als android:apk-key-hash:<encoded SHA 256 fingerprint> berechnet werden.
val appSigningInfo =
request?.callingAppInfo?.signingInfoCompat?.signingCertificateHistory?.get(0)?.toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val certHash =
Base64.encodeToString(md.digest(appSigningInfo), Base64.NO_WRAP or Base64.NO_PADDING)
val computedOrigin = "android:apk-key-hash:$certHash"
Inhaber-UI rendern
Wenn ein Ausweis ausgewählt wird, wird die Inhaber-App aufgerufen und der Nutzer durch die Benutzeroberfläche der App geführt. Es gibt zwei Standardmethoden, um diesen Workflow zu verarbeiten:
- Wenn für die Freigabe der Anmeldedaten eine zusätzliche Nutzerauthentifizierung erforderlich ist, verwende die BiometricPrompt API. Dies wird im Beispiel veranschaulicht.
- Andernfalls entscheiden sich viele Wallets für eine unauffällige Rückgabe, indem sie eine leere Aktivität rendern, die die Daten sofort an die aufrufende App zurückgibt. Dadurch werden die Klicks der Nutzer minimiert und die Nutzung wird nahtloser.
Anmeldedatenantwort zurückgeben
Sobald Ihre Inhaber-App bereit ist, das Ergebnis zurückzusenden, beenden Sie die Aktivität mit der Antwort auf die Anmeldedaten:
PendingIntentHandler.setGetCredentialResponse(
resultData,
GetCredentialResponse(DigitalCredential(response.responseJson))
)
setResult(RESULT_OK, resultData)
finish()
Wenn eine Ausnahme vorliegt, können Sie die Ausnahme für die Anmeldedaten 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 Antwort mit den Anmeldedaten im Kontext finden Sie in der Beispiel-App.