Mengintegrasikan aplikasi Pemegang dengan Credential Manager

Credential Manager Holder API memungkinkan aplikasi pemegang (juga disebut "dompet") Android Anda untuk mengelola dan menampilkan kredensial digital kepada verifier.

Gambar yang menampilkan UI kredensial digital di Pengelola Kredensial
Gambar 1. UI pemilih kredensial digital.

Konsep inti

Penting untuk memahami konsep berikut sebelum menggunakan Holder API.

Format kredensial

Kredensial dapat disimpan di aplikasi pemegang dalam format kredensial yang berbeda. Format ini adalah spesifikasi tentang cara kredensial harus ditampilkan, dan setiap format berisi informasi berikut tentang kredensial:

  • Jenis: Kategori seperti gelar universitas atau surat izin mengemudi seluler.
  • Properti: Atribut seperti nama depan dan belakang.
  • Encoding: Cara kredensial disusun, misalnya SD-JWT atau mdoc
  • Validitas: Metode untuk memverifikasi keaslian kredensial secara kriptografis.

Setiap format kredensial melakukan encoding dan validasi dengan sedikit berbeda, tetapi secara fungsional, keduanya sama.

Registri mendukung dua format:

Verifier dapat membuat permintaan OpenID4VP untuk SD-JWT dan mdoc saat menggunakan Credential Manager. Pilihannya bervariasi, bergantung pada kasus penggunaan dan pilihan industri.

Pendaftaran metadata kredensial

Pengelola Kredensial tidak menyimpan kredensial pemegang secara langsung, tetapi menyimpan metadata kredensial. Aplikasi pemegang harus mendaftarkan metadata kredensial terlebih dahulu dengan Credential Manager menggunakan RegistryManager. Proses pendaftaran ini membuat catatan registri yang memiliki dua tujuan utama:

  • Pencocokan: Metadata kredensial terdaftar digunakan untuk mencocokkan dengan permintaan verifier mendatang.
  • Tampilan: Elemen UI yang disesuaikan ditampilkan kepada pengguna di antarmuka pemilih kredensial.

Anda akan menggunakan class OpenId4VpRegistry untuk mendaftarkan kredensial digital Anda, karena class ini mendukung format kredensial mdoc dan SD-JWT. Verifikator akan mengirim permintaan OpenID4VP untuk meminta kredensial ini.

Mendaftarkan kredensial aplikasi Anda

Untuk menggunakan Credential Manager Holder API, tambahkan dependensi berikut ke skrip build modul aplikasi Anda:

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")

}

Buat RegistryManager

Buat instance RegistryManager dan daftarkan permintaan OpenId4VpRegistry dengannya.

// 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
}

Membangun permintaan OpenId4VpRegistry

Seperti yang disebutkan sebelumnya, Anda harus mendaftarkan OpenId4VpRegistry untuk menangani permintaan OpenID4VP dari verifier. Kami akan mengasumsikan bahwa Anda telah memuat beberapa jenis data lokal dengan kredensial wallet Anda (misalnya, sdJwtsFromStorage). Sekarang Anda akan mengonversinya menjadi padanan DigitalCredentialEntry Jetpack kami berdasarkan formatnya - SdJwtEntry atau MdocEntry untuk SD-JWT atau mdoc.

Menambahkan Sd-JWT ke dalam registry

Petakan setiap kredensial SD-JWT lokal ke SdJwtEntry untuk registry:

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
}

Menambahkan mdocs ke Registry

Petakan kredensial mdoc lokal Anda ke jenis Jetpack MdocEntry:

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
}

Poin penting tentang kode

  • Salah satu metode untuk mengonfigurasi kolom id adalah dengan mendaftarkan ID kredensial terenkripsi, sehingga hanya Anda yang dapat mendekripsi nilai tersebut.
  • Kolom tampilan UI untuk kedua format harus dilokalkan.

Mendaftarkan kredensial Anda

Gabungkan entri yang dikonversi dan daftarkan permintaan dengan 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.
)

Sekarang, kita siap mendaftarkan kredensial Anda dengan CredentialManager.

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

Anda kini telah mendaftarkan kredensial Anda dengan Pengelola Kredensial.

Pengelolaan metadata aplikasi

Metadata yang didaftarkan aplikasi pemegang Anda dengan CredentialManager memiliki properti berikut:

  • Persistensi: Informasi disimpan secara lokal dan tetap ada setelah perangkat di-reboot.
  • Penyimpanan yang Terpisah: Setiap catatan registri aplikasi disimpan secara terpisah, yang berarti satu aplikasi tidak dapat mengubah catatan registri aplikasi lain.
  • Pembaruan dengan Kunci: Setiap catatan registri aplikasi diberi kunci oleh id, yang memungkinkan identifikasi ulang, pembaruan, atau penghapusan catatan.
  • Memperbarui Metadata: Sebaiknya perbarui metadata yang dipertahankan setiap kali aplikasi Anda berubah atau dimuat pertama kali. Jika registri dipanggil beberapa kali dalam id yang sama, panggilan terakhir akan menggantikan semua catatan sebelumnya. Untuk memperbarui, daftar ulang tanpa perlu menghapus data lama terlebih dahulu.

Opsional: Buat pencocok

Pencocok adalah biner Wasm yang dijalankan Credential Manager di sandbox untuk memfilter kredensial terdaftar Anda terhadap permintaan Verifier yang masuk.

  • Pencocok default: Class OpenId4VpRegistry secara otomatis menyertakan pencocok OpenId4VP default (OpenId4VpDefaults.DEFAULT_MATCHER) saat Anda membuat instance-nya. Untuk semua kasus penggunaan OpenID4VP standar, library menangani pencocokan untuk Anda.
  • Pencocok kustom: Anda hanya akan menerapkan pencocok kustom jika mendukung protokol non-standar yang memerlukan logika pencocokan sendiri.

Menangani kredensial yang dipilih

Saat pengguna memilih kredensial, aplikasi pemegang Anda perlu menangani permintaan tersebut. Anda harus menentukan Aktivitas yang memproses filter intent androidx.credentials.registry.provider.action.GET_CREDENTIAL. Dompet contoh kami menunjukkan prosedur ini.

Intent meluncurkan aktivitas Anda dengan permintaan Verifier dan asal panggilan, yang Anda ekstrak dengan fungsi PendingIntentHandler.retrieveProviderGetCredentialRequest. Metode ini menampilkan ProviderGetCredentialRequest yang berisi semua informasi yang terkait dengan permintaan verifikasi. Ada tiga komponen utama:

  • Aplikasi pemanggil: Aplikasi yang membuat permintaan, dapat diambil dengan getCallingAppInfo.
  • Kredensial yang dipilih: Informasi tentang kandidat yang dipilih pengguna, yang diambil melalui selectedCredentialSet extension method; ini akan cocok dengan ID kredensial yang Anda daftarkan.
  • Permintaan spesifik: Permintaan spesifik yang dibuat oleh verifikator, diambil dari metode getCredentialOptions. Untuk alur permintaan Kredensial Digital, Anda dapat menemukan satu GetDigitalCredentialOption dalam daftar ini.

Biasanya, pemverifikasi membuat permintaan presentasi kredensial digital, yang dapat Anda proses dengan contoh kode berikut:

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

Contohnya dapat dilihat di dompet contoh.

Periksa identitas verifikator

  1. Ekstrak ProviderGetCredentialRequest dari intent:
val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
  1. Periksa Origin dengan Hak Istimewa: Aplikasi dengan hak istimewa (seperti browser web) dapat melakukan panggilan atas nama verifier lain dengan menetapkan parameter origin. Untuk mengambil asal ini, Anda harus meneruskan daftar pemanggil dengan hak istimewa dan tepercaya (daftar yang diizinkan dalam format JSON) ke getOrigin() API CallingAppInfo.
val origin = request?.callingAppInfo?.getOrigin(
    privilegedAppsJson // Your allow list JSON
)

Jika origin tidak kosong: Origin akan ditampilkan jika packageName dan sidik jari sertifikat yang didapatkan dari signingInfo cocok dengan aplikasi yang ditemukan di daftar yang diizinkan yang diteruskan ke getOrigin() API. Setelah nilai asal diperoleh, aplikasi penyedia harus menganggapnya sebagai panggilan dengan hak istimewa dan menyetel asal ini pada respons OpenID4VP, bukan menghitung asal menggunakan tanda tangan aplikasi panggilan.

Pengelola Sandi Google menggunakan daftar yang diizinkan yang tersedia secara terbuka untuk panggilan ke getOrigin(). Sebagai penyedia kredensial, Anda dapat menggunakan daftar ini atau memberikan daftar Anda sendiri dalam format JSON yang dijelaskan oleh API. Penyedia dapat memilih daftar mana yang akan digunakan. Untuk mendapatkan akses hak istimewa dengan penyedia kredensial pihak ketiga, lihat dokumentasi yang disediakan oleh pihak ketiga.

Jika origin kosong, permintaan verifikasi berasal dari aplikasi Android. Asal aplikasi yang akan dimasukkan dalam respons OpenID4VP harus dihitung sebagai android:apk-key-hash:<encoded SHA 256 fingerprint>.

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"

Merender UI Pemegang

Saat kredensial dipilih, aplikasi pemegang akan dipanggil, memandu pengguna melalui UI aplikasi. Ada dua cara standar untuk menangani alur kerja ini:

  • Jika autentikasi pengguna tambahan diperlukan untuk merilis kredensial, gunakan BiometricPrompt API. Hal ini ditunjukkan dalam contoh.
  • Jika tidak, banyak dompet memilih pengembalian senyap dengan merender aktivitas kosong yang segera meneruskan data kembali ke aplikasi yang memanggil. Hal ini meminimalkan klik pengguna dan memberikan pengalaman yang lebih lancar.

Menampilkan respons kredensial

Setelah aplikasi pemegang Anda siap mengirimkan hasilnya kembali, selesaikan aktivitas dengan respons kredensial:

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

Jika ada pengecualian, Anda dapat mengirim pengecualian kredensial dengan cara yang sama:

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

Lihat aplikasi contoh untuk contoh lengkap menampilkan respons kredensial dalam konteks.