Weryfikator kluczy na Androida zapewnia użytkownikom ujednolicony i bezpieczny sposób weryfikacji, czy komunikują się z właściwą osobą w pełni szyfrowanej aplikacji. Chroni użytkowników przed atakami typu „man-in-the-middle”, umożliwiając im potwierdzenie autentyczności publicznych kluczy szyfrujących kontaktu za pomocą zaufanego i spójnego interfejsu systemu.
Ta funkcja jest udostępniana przez Weryfikator kluczy, czyli usługę systemową, która jest częścią Usług systemowych Google i jest rozpowszechniana za pomocą Sklepu Play. Pełni funkcję centralnego repozytorium kluczy publicznych E2EE na urządzeniu.
Dlaczego warto przeprowadzić integrację z weryfikatorem kluczy
- Zapewnij spójny interfejs: zamiast tworzyć własny proces weryfikacji, możesz uruchomić standardowy interfejs systemu, dzięki czemu użytkownicy będą mieli spójne i wiarygodne środowisko we wszystkich aplikacjach.
- Zwiększanie zaufania użytkowników: jasny status weryfikacji potwierdzony przez system zapewnia użytkowników, że ich rozmowy są bezpieczne i prywatne.
- Zmniejszenie kosztów rozwoju: przekazanie złożoności interfejsu weryfikacji kluczy, przechowywania i zarządzania stanem usłudze systemowej.
Kluczowe terminy
- lookupKey: nieprzezroczysty, trwały identyfikator kontaktu przechowywany w kolumnie LOOKUP_KEY dostawcy kontaktów. W przeciwieństwie do
contact ID
,lookupKey
pozostaje stabilny nawet wtedy, gdy podstawowe informacje kontaktowe zostaną zmienione lub połączone. Dlatego jest to zalecany sposób odwoływania się do kontaktu. - accountId: identyfikator konta użytkownika na urządzeniu, który jest specyficzny dla aplikacji. Ten identyfikator jest zdefiniowany przez Twoją aplikację i pomaga odróżniać od siebie różne konta, które może mieć jeden użytkownik. Jest on wyświetlany użytkownikowi w interfejsie. Zalecamy użycie czegoś znaczącego, np. numeru telefonu, adresu e-mail lub nazwy użytkownika.
- deviceId: unikalny identyfikator konkretnego urządzenia powiązanego z kontem użytkownika. Dzięki temu użytkownik może mieć wiele urządzeń, z których każde ma własny zestaw kluczy kryptograficznych. Nie musi reprezentować urządzenia fizycznego, ale może być sposobem na rozróżnienie wielu kluczy używanych na tym samym koncie.
Pierwsze kroki
Zanim zaczniesz, skonfiguruj aplikację tak, aby komunikowała się z usługą KeyVerifier.
Zadeklaruj uprawnienia: w pliku AndroidManifest.xml zadeklaruj te uprawnienia: Musisz też poprosić o nie użytkownika w czasie działania aplikacji.
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
Uzyskaj instancję klienta: uzyskaj instancję ContactKeys
, która jest punktem wejścia do interfejsu API.
import com.google.android.gms.contactkeys.ContactKeys
val contactKeyClient = ContactKeys.getClient(context)
Wskazówki dla deweloperów aplikacji do przesyłania wiadomości
Jako deweloper aplikacji do przesyłania wiadomości Twoim głównym zadaniem jest publikowanie kluczy publicznych użytkowników i kluczy ich kontaktów w usłudze weryfikacji kluczy.
Publikowanie kluczy publicznych użytkownika
Aby umożliwić innym użytkownikom znalezienie i zweryfikowanie Twojego konta, opublikuj ich klucz publiczny w repozytorium na urządzeniu. Aby zwiększyć bezpieczeństwo, rozważ utworzenie kluczy w Android Keystore.
import com.google.android.gms.contactkeys.ContactKeyClient
import com.google.android.gms.tasks.Tasks
suspend fun publishSelfKey(
contactKeyClient: ContactKeyClient,
accountId: String,
deviceId: String,
publicKey: ByteArray
) {
try {
Tasks.await(
contactKeyClient.updateOrInsertE2eeSelfKey(
deviceId,
accountId,
publicKey
)
)
// Self key published successfully.
} catch (e: Exception) {
// Handle error.
}
}
Powiązywanie kluczy publicznych z kontaktami
Gdy aplikacja otrzyma klucz publiczny jednego z kontaktów użytkownika, musi go zapisać i powiązać z tym kontaktem w centralnym repozytorium. Dzięki temu klucz może zostać zweryfikowany, a inne aplikacje mogą wyświetlać stan weryfikacji kontaktu. Aby to zrobić, potrzebujesz lookupKey kontaktu z dostawcy kontaktów na Androidzie. Zwykle dzieje się to podczas pobierania klucza z serwera dystrybucji kluczy lub podczas okresowej synchronizacji kluczy lokalnych.
import com.google.android.gms.contactkeys.ContactKeyClient
import com.google.android.gms.tasks.Tasks
suspend fun storeContactKey(
contactKeyClient: ContactKeyClient,
contactLookupKey: String,
contactAccountId: String,
contactDeviceId: String,
contactPublicKey: ByteArray
) {
try {
Tasks.await(
contactKeyClient.updateOrInsertE2eeContactKey(
contactLookupKey,
contactDeviceId,
contactAccountId,
contactPublicKey
)
)
// Contact's key stored successfully.
} catch (e: Exception) {
// Handle error.
}
}
Pobieranie kluczy i stanu weryfikacji
Po opublikowaniu kluczy użytkownicy mogą je zweryfikować, skanując kod QR osobiście. Interfejs aplikacji powinien wskazywać, czy w rozmowie używany jest zweryfikowany klucz. Każdy klucz ma stan weryfikacji, który możesz wykorzystać w interfejsie.
Stany weryfikacji:
UNVERIFIED
: to stan domyślny każdego nowego klucza. Oznacza to, że klucz istnieje, ale użytkownik nie potwierdził jeszcze jego autentyczności. W interfejsie użytkownika należy traktować ten stan jako neutralny i zwykle nie wyświetlać żadnego specjalnego wskaźnika.VERIFIED
: ten stan oznacza wysoki poziom zaufania. Oznacza to, że użytkownik przeszedł proces weryfikacji (np. skanowanie kodu QR) i potwierdził, że klucz należy do wybranej osoby. W interfejsie użytkownika należy wyświetlać wyraźny, pozytywny wskaźnik, np. zielony znacznik wyboru lub tarczę.VERIFICATION_FAILED
: jest to stan ostrzegawczy. Oznacza to, że klucz powiązany z kontaktem nie pasuje do klucza, który został wcześniej zweryfikowany. Może się tak zdarzyć, jeśli kontakt zacznie korzystać z nowego urządzenia, ale może to też wskazywać na potencjalne zagrożenie bezpieczeństwa. W interfejsie wyświetl użytkownikowi wyraźne ostrzeżenie i zaproponuj ponowną weryfikację przed wysłaniem informacji poufnych.
Możesz pobrać zbiorczy stan wszystkich kluczy powiązanych z kontaktem. Jeśli występuje wiele kluczy, zalecamy używanie symbolu VerificationState.leastVerifiedFrom()
do określania stanu, ponieważ prawidłowo nadaje on priorytet symbolowi VERIFICATION_FAILED
przed symbolem VERIFIED
.
- Uzyskiwanie zbiorczego stanu na poziomie kontaktu
import com.google.android.gms.contactkeys.ContactKeyClient
import com.google.android.gms.contactkeys.constants.VerificationState
import com.google.android.gms.tasks.Tasks
suspend fun displayContactVerificationStatus(
contactKeyClient: ContactKeyClient,
contactLookupKey: String
) {
try {
val keysResult = Tasks.await(contactKeyClient.getAllE2eeContactKeys(contactLookupKey))
val states =
keysResult.keys.map { VerificationState.fromState(it.localVerificationState) }
val contactStatus = VerificationState.leastVerifiedFrom(states)
updateUi(contactLookupKey, contactStatus)
} catch (e: Exception) {
// Handle error.
}
}
- Uzyskiwanie zbiorczego stanu na poziomie konta
import com.google.android.gms.contactkeys.ContactKeyClient
import com.google.android.gms.contactkeys.constants.VerificationState
import com.google.android.gms.tasks.Tasks
suspend fun displayAccountVerificationStatus(
contactKeyClient: ContactKeyClient,
accountId: String
) {
try {
val keys = Tasks.await(contactKeyClient.getE2eeAccountKeysForAccount(accountId))
val states = keys.map { VerificationState.fromState(it.localVerificationState) }
val accountStatus = VerificationState.leastVerifiedFrom(states)
updateUi(accountId, accountStatus)
} catch (e: Exception) {
// Handle error.
}
}
Obserwowanie kluczowych zmian w czasie rzeczywistym
Aby mieć pewność, że interfejs aplikacji zawsze pokazuje prawidłowy stan zaufania, musisz nasłuchiwać aktualizacji. Zalecamy używanie interfejsu API opartego na przepływie, który emituje nową listę kluczy za każdym razem, gdy klucz do subskrybowanego konta zostanie dodany, usunięty lub zmieni się jego stan weryfikacji. Jest to szczególnie przydatne, gdy chcesz aktualizować listę uczestników rozmowy grupowej. Stan weryfikacji klucza może się zmienić, gdy:
- Użytkownik pomyślnie przechodzi proces weryfikacji (np. skanowanie kodu QR).
- Klucz kontaktu został zmodyfikowany, więc nie pasuje już do wcześniej zweryfikowanej wartości.
fun observeKeyUpdates(contactKeyClient: ContactKeyClient, accountIds: List<String>) {
lifecycleScope.launch {
contactKeyClient.getAccountContactKeysFlow(accountIds)
.collect { updatedKeys ->
// A key was added, removed, or updated.
// Refresh your app's UI and internal state.
refreshUi(updatedKeys)
}
}
}
Przeprowadzanie weryfikacji klucza osobiście
Najbezpieczniejszym sposobem weryfikacji klucza przez użytkowników jest weryfikacja osobista, często poprzez zeskanowanie kodu QR lub porównanie ciągu cyfr. Aplikacja Key Verifier udostępnia standardowe ścieżki interfejsu użytkownika dla tego procesu, które Twoja aplikacja może uruchamiać. Po próbie weryfikacji interfejs API automatycznie aktualizuje stan weryfikacji klucza, a aplikacja otrzyma powiadomienie, jeśli obserwujesz aktualizacje klucza.
- Rozpocznij proces weryfikacji klucza dla wybranego kontaktu
Uruchom
PendingIntent
dostarczony przezgetScanQrCodeIntent
, używająclookupKey
wybranego kontaktu. Interfejs umożliwia użytkownikowi weryfikację wszystkich kluczy danego kontaktu.
import android.app.ActivityOptions
import android.app.PendingIntent
import com.google.android.gms.contactkeys.ContactKeyClient
import com.google.android.gms.tasks.Tasks
suspend fun initiateVerification(contactKeyClient: ContactKeyClient, lookupKey: String) {
try {
val pendingIntent = Tasks.await(contactKeyClient.getScanQrCodeIntent(lookupKey))
val options =
ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
)
.toBundle()
pendingIntent.send(options)
} catch (e: Exception) {
// Handle error.
}
}
- Rozpocznij proces weryfikacji klucza dla konta wybranego przez użytkownika
Jeśli użytkownik chce zweryfikować konto, które nie jest bezpośrednio połączone z kontaktem (lub konkretne konto kontaktu), możesz uruchomić
PendingIntent
dostarczony przezgetScanQrCodeIntentForAccount
. Zwykle jest używany w przypadku nazwy pakietu i identyfikatora konta Twojej aplikacji.
import android.app.ActivityOptions
import android.app.PendingIntent
import com.google.android.gms.contactkeys.ContactKeyClient
import com.google.android.gms.tasks.Tasks
suspend fun initiateVerification(contactKeyClient: ContactKeyClient, packageName: String, accountId: String) {
try {
val pendingIntent = Tasks.await(contactKeyClient.getScanQrCodeIntentForAccount(packageName, accountId))
// Allow activity start from background on Android SDK34+
val options =
ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
)
.toBundle()
pendingIntent.send(options)
} catch (e: Exception) {
// Handle error.
}
}