簡體   English   中英

ANDROID - Biometric_Not_Enrolled 在我的測試設備上不起作用,但在模擬器上起作用

[英]ANDROID - Biometric_Not_Enrolled not working on my test device but works on emulator

我的生物識別功能在模擬器上運行良好,但是當我在 Galaxy A7 2017 上對其進行測試時,它無法正常工作,而不是識別為 BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,即使沒有配置生物識別,它也會識別為 BiometricManager.BIOMETRIC_SUCCESS,我剛剛刪除了我的舊配置為對其進行測試的生物特征。

import android.content.Context


import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import android.os.Build
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import android.util.Log
import android.widget.Toast
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import java.security.KeyStore
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec




fun createBiometricCallback (
    onAuthenticationError: () -> Unit = {},
    onAuthenticationFailed: () -> Unit = {},
    onAuthenticationSuccess: (result: BiometricPrompt.AuthenticationResult) -> Unit
) = @RequiresApi(Build.VERSION_CODES.P)
object : BiometricPrompt.AuthenticationCallback() {
    override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
        super.onAuthenticationError(errorCode, errString)
        onAuthenticationError.invoke()
    }

    override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
        super.onAuthenticationSucceeded(result)
        onAuthenticationSuccess.invoke(result)
    }

    override fun onAuthenticationFailed() {
        super.onAuthenticationFailed()
        onAuthenticationFailed.invoke()
    }
}


@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M)
fun canUseBiometricAuthentication(
    context: Context,
    canUseBiometricSuccess: () -> Unit = {},
    canUseBiometricErrorNoHardware: () -> Unit = {},
    canUseBiometricErrorHwUnavailable: () -> Unit = {},
    canUseBiometricErrorNoneEnrolled: () -> Unit = {},
    canUseBiometricErrorSecurityUpdateRequired: () -> Unit = {},
    canUseBiometricErrorUnsupported: () -> Unit = {},
    canUseBiometricStatusUnknown: () -> Unit = {},
) {
    context?.let { context ->
        val biometricManager = BiometricManager.from(context)
        when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) {
            BiometricManager.BIOMETRIC_SUCCESS -> {
                Log.d("MY_APP_TAG", "App can authenticate using biometrics.")
                Toast.makeText(context,"App can authenticate using biometrics.", Toast.LENGTH_LONG).show()
                canUseBiometricSuccess.invoke()
            }
            BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
                Log.e("MY_APP_TAG", "No biometric features available on this device.")
                Toast.makeText(context,"No biometric features available on this device.", Toast.LENGTH_LONG).show()
                canUseBiometricErrorNoHardware.invoke()
            }
            BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
                Log.e("MY_APP_TAG", "Biometric features are currently unavailable.")
                Toast.makeText(context,"Biometric features are currently unavailable.", Toast.LENGTH_LONG).show()
                canUseBiometricErrorHwUnavailable.invoke()
            }
            BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
                Log.e("MY_APP_TAG", "Biometric features are not configured.")
                Toast.makeText(context,"Biometric features are not configured.", Toast.LENGTH_LONG).show()
                canUseBiometricErrorNoneEnrolled.invoke()
            }
            BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> {
                Log.e("MY_APP_TAG", "Security Update Required Error.")
                Toast.makeText(context,"Security Update Required Error.", Toast.LENGTH_LONG).show()
                canUseBiometricErrorSecurityUpdateRequired.invoke()
            }
            BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> {
                Log.e("MY_APP_TAG", "Error Unsupported.")
                Toast.makeText(context,"Error Unsupported.", Toast.LENGTH_LONG).show()
                canUseBiometricErrorUnsupported.invoke()
            }
            BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> {
                Log.e("MY_APP_TAG", "Status Unknown.")
                Toast.makeText(context,"Status Unknown.", Toast.LENGTH_LONG).show()
                canUseBiometricStatusUnknown.invoke()
            }
            else -> {canUseBiometricStatusUnknown.invoke()}
        }
    }
}


fun getBiometricPromptBuilder(context: Context) = context.run {
    BiometricPrompt.PromptInfo.Builder()
        .setTitle("Biometric")
        .setConfirmationRequired(true)
        .setNegativeButtonText("Cancel")
        .build()
}


fun createBiometricPrompt(activity: FragmentActivity,context: Context,authCallBack: BiometricPrompt.AuthenticationCallback) =
    BiometricPrompt(
        activity,
        ContextCompat.getMainExecutor(context!!),
        authCallBack
    )


fun BiometricPrompt.authenticateWithBiometric(context: Context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (isKeyAlreadyCreated().not()) {
            generateSecretKey()
        }

        val ivParameterSpec = IvParameterSpec(Base64.decode(BuildConfig.CIPHER_IV, Base64.DEFAULT))
        val cipher = getCipher()
        val secretKey = getSecretKey()
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec)
        authenticate(
            getBiometricPromptBuilder(context),
            BiometricPrompt.CryptoObject(cipher)
        )
    }
}

@RequiresApi(Build.VERSION_CODES.M)
private fun generateSecretKey() {
    val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
    keyGenerator.init(
        KeyGenParameterSpec.Builder(
            BuildConfig.KeyStoreAlias,
            KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
            .setRandomizedEncryptionRequired(false)
            .build()
    )
    keyGenerator.generateKey()
}

@RequiresApi(Build.VERSION_CODES.M)
fun getSecretKey(): SecretKey {
    val keyStore = KeyStore.getInstance("AndroidKeyStore")
    keyStore.load(null)
    return keyStore.getKey(BuildConfig.KeyStoreAlias, null) as SecretKey
}

@RequiresApi(Build.VERSION_CODES.M)
fun getCipher(): Cipher {
    return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
            + KeyProperties.BLOCK_MODE_CBC + "/"
            + KeyProperties.ENCRYPTION_PADDING_PKCS7)
}

@RequiresApi(Build.VERSION_CODES.M)
fun isKeyAlreadyCreated(): Boolean {
    return try {
        getSecretKey()
        true
    } catch (e: Exception) {
        false
    }
}


import android.app.Activity
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.util.Base64
import android.widget.Toast
import androidx.biometric.BiometricManager
import androidx.core.app.ActivityCompat
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModel
import br.com.px.commons.authenticateWithBiometric
import br.com.px.commons.canUseBiometricAuthentication
import br.com.px.commons.createBiometricCallback
import br.com.px.commons.createBiometricPrompt
import br.com.px.uikit.utils.extensions.*
import exceptions.*


class BiometricViewModel : ViewModel() {

    private val _biometricAuthenticationState by viewState<Unit>()
    val biometricAuthenticationState = _biometricAuthenticationState.asLiveData()


    fun canBiometricHardwareAuthentication(context: Context) {
    canUseBiometricAuthentication(
        context,
        canUseBiometricSuccess = {_biometricAuthenticationState.postSuccess(Unit)},
        canUseBiometricErrorNoHardware = {_biometricAuthenticationState.postError(BiometricNoHardwareException())},
        canUseBiometricErrorHwUnavailable = {_biometricAuthenticationState.postError(BiometricHwUnavailableException())},
        canUseBiometricErrorNoneEnrolled = {_biometricAuthenticationState.postError(BiometricNoneEnrolledException())},
        canUseBiometricErrorSecurityUpdateRequired = {_biometricAuthenticationState.postError(BiometricSecurityUpdateRequiredException())},
        canUseBiometricErrorUnsupported = {_biometricAuthenticationState.postError(BiometricUnsupportedException())},
        canUseBiometricStatusUnknown = {_biometricAuthenticationState.postError(BiometricStatusUnknownException())}
        )
    }

    private fun createAuthenticationCallback(userPassword: String, context: Context) = createBiometricCallback (
        onAuthenticationSuccess = { result ->
            val encryptedPassword = Base64.encodeToString(
                result.cryptoObject?.cipher?.doFinal(
                    (userPassword ?: "").toByteArray(Charsets.UTF_8)
                ),
                Base64.DEFAULT
            )
            Toast.makeText(context,"Biometric Authentication Success.",Toast.LENGTH_LONG).show()
            Toast.makeText(context,"Encrypted Password: $encryptedPassword",Toast.LENGTH_LONG).show()
        },
        onAuthenticationError = {
            Toast.makeText(context,"Biometric Authentication Errorrrrrr.",Toast.LENGTH_LONG).show()
        },
        onAuthenticationFailed = {
            Toast.makeText(context,"Biometric Authentication Failed.",Toast.LENGTH_LONG).show()
        }
    )

    fun showBiometricPrompt (context: Context, fragmentActivity: FragmentActivity){
        createBiometricPrompt(fragmentActivity,context,
            createAuthenticationCallback("userPassword",context)
        ).authenticateWithBiometric(
            context = context
        )
    }

    fun openBiometricConfiguration(activity: Activity){
        val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {
            putExtra(
                Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
                BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL
            )
        }
        ActivityCompat.startActivityForResult(activity, enrollIntent, 0, null)
    }
}

我的假設是它按預期工作。

您已在biometricManager.canAuthenticate調用中包含or BiometricManager.Authenticators.DEVICE_CREDENTIAL

它是成功的,因為您在設備上也設置了設備憑據(PIN/模式/密碼)。

如果您僅請求BiometricManager.Authenticators.BIOMETRIC_STRONG它將返回您所追求的BIOMETRIC_ERROR_NONE_ENROLLED

來自developer.android.com BiometricManager.canAuthenticate

例如,如果 Authenticators#DEVICE_CREDENTIAL | Authenticators#BIOMETRIC_STRONG 被查詢並且只設置了 Authenticators#DEVICE_CREDENTIAL,這個 API 將返回 BIOMETRIC_SUCCESS

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM