本文介绍了在FingerprintManager.authenticate中,以下使用情况是否需要CryptoObject对象或null?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我们打电话

  mFingerprintManager 
.authenticate(cryptoObject,0 / *标志* /,mCancellationSignal,this , 空值);

我注意到传递 null 表示 cryptoObject 。根据






这是源代码。目前,我正在使用 CryptoObject 。但是,根据我的用户反馈,其中只有少数人面临此新功能的应用程序问题。尽管我们在Google Play控制台中没有看到任何崩溃报告,但我们怀疑在生成 CryptoObject 时出现了问题。



因此,如果 CryptoObject 可以替换为null,我们很乐意这样做以简化代码。 / p>

在FingerprintManager.authenticate期间,以下用例是否需要CryptoObject对象或null?






  / ** 
*小助手类,用于管理指纹认证UI周围的文本/图标。
* /
公共类FingerprintUiHelper扩展了FingerprintManagerCompat.AuthenticationCallback {
private static final String TAG = FingerprintUiHelper;

private static final String ANDROID_KEY_STORE = AndroidKeyStore;
private static final String DEFAULT_KEY_NAME = hello world key name;

private int configShortAnimTime;

私人最终FingerprintManagerCompat mFingerprintManager;
私人最终ImageView图标;
私人最终Callback mCallback;
私人CancellationSignal mCancellationSignal;

私有布尔值mSelfCancelled;

私人最终ResetErrorRunnable resetErrorRunnable = new ResetErrorRunnable();

private final int mSuccessColor;
private final int mAlertColor;

私有类ResetErrorRunnable实现Runnable {

@Override
public void run(){
resetError();
}
}

public static FingerprintUiHelper newInstance(ImageView icon,Callback callback,int successColor,int alertColor){
FingerprintManagerCompat FingerprintManagerCompat = FingerprintManagerCompat.from(WeNoteApplication.instance ());
返回新的FingerprintUiHelper(fingerprintManagerCompat,icon,callback,successColor,alertColor);
}

私有无效initResource(){
configShortAnimTime = WeNoteApplication.instance()。getResources()。getInteger(android.R.integer.config_shortAnimTime);
}

/ **
* {@link FingerprintUiHelper}的构造方法。
* /
private FingerprintUiHelper(FingerprintManagerCompat FingerprintManager,
ImageView icon,Callback callback,int successColor,int alertColor){
initResource();

mFingerprintManager = FingerprintManager;
mIcon =图标;
mCallback =回调;
mSuccessColor = successColor;
mAlertColor = alertColor;
}

public boolean isFingerprintAuthAvailable(){
//以下行可防止Android Studio的误报检查
// noinspection ResourceType
返回mFingerprintManager .isHardwareDetected()
&& mFingerprintManager.hasEnrolledFingerprints();
}

/ **
*使用
中创建的密钥初始化{@link Cipher}实例* {@link #createKey(String,boolean) } 方法。
*
* @param keyName初始化密码的密钥名称
* @return如果初始化成功,则返回{@code true},如果锁定屏幕上有$ b $,则返回{@code false} b *在生成密钥后被禁用或重置,或者如果在
*之后注册了指纹*则生成密钥。
* /
私有布尔值initCipher(密码,字符串keyName){
if(Build.VERSION.SDK_INT&Build.VERSION_CODES.M){
返回false;
}

KeyStore keyStore;
KeyGenerator keyGenerator;

试试{
keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
} catch(KeyStoreException e){
Log.e(TAG,,e);
返回false;
}

试试{
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,ANDROID_KEY_STORE);
} catch(NoSuchAlgorithmException | NoSuchProviderException e){
Log.e(TAG,,e);
返回false;
}

//指纹的注册流程。在这里,您要求用户为流程设置指纹
//。如果您需要了解
注册指纹集是否已更改,则必须使用密钥。
try {
keyStore.load(null);
//在Android KeyStore中设置项的别名,其中密钥将出现
//和构建器的构造函数中的约束(目的)

KeyGenParameterSpec.Builder构建器= new KeyGenParameterSpec.Builder(keyName,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
//要求用户使用指纹进行身份验证授权密钥
的每次使用
// .setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);

//这是一种避免API级为<的设备崩溃的解决方法。 24
//因为在API级别+24上可见KeyGenParameterSpec.Builder#setInvalidatedByBiometricEnrollment仅
//。
//理想情况下应该为KeyGenParameterSpec.Builder提供一个兼容库,但是
//尚不可用。
if(Build.VERSION.SDK_INT> = Build.VERSION_CODES.N){
builder.setInvalidatedByBiometricEnrollment(true);
}
keyGenerator.init(builder.build());
keyGenerator.generateKey();
} catch(NoSuchAlgorithmException | InvalidAlgorithmParameterException
| CertificateException | IOException e){
Log.e(TAG,,e);
返回false;
}

试试{
keyStore.load(null);
SecretKey key =(SecretKey)keyStore.getKey(keyName,null);
cipher.init(Cipher.ENCRYPT_MODE,键);
返回true;
} catch(异常e){
Log.e(TAG,,e);
返回false;
}
}

public void startListening(){
if(!isFingerprintAuthAvailable()){
return;
}

密码defaultCipher;
try {
defaultCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + /
+ KeyProperties.BLOCK_MODE_CBC + /
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch(NoSuchAlgorithmException | NoSuchPaddingException e){
Log.e(TAG,,e);
的回报;
}

if(false == initCipher(defaultCipher,DEFAULT_KEY_NAME)){
return;
}

FingerprintManagerCompat.CryptoObject cryptoObject = new FingerprintManagerCompat.CryptoObject(defaultCipher);

startListening(cryptoObject);

showIcon();
}

private void startListening(FingerprintManagerCompat.CryptoObject cryptoObject){
if(!isFingerprintAuthAvailable()){
return;
}
mCancellationSignal = new CancellationSignal();
mSelfCancelled = false;

//下面的行防止了来自Android Studio的误报检查
// noinspection ResourceType
mFingerprintManager
.authenticate(cryptoObject,0 / *标志* /, mCancellationSignal,此为null);
}

public void stopListening(){
if(mCancellationSignal!= null){
mSelfCancelled = true;
mCancellationSignal.cancel();
mCancellationSignal = null;
}
}

@Override
public void onAuthenticationError(int errMsgId,CharSequence errString){
if(!mSelfCancelled){
if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT){
mIcon.removeCallbacks(resetErrorRunnable);
showError();
的回报;
}

if(errMsgId == FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST){
return;
}

showError();
mIcon.postDelayed(resetErrorRunnable,configShortAnimTime);
}
}

@Override
public void onAuthenticationHelp(int helpMsgId,CharSequence helpString){
showError();
mIcon.postDelayed(resetErrorRunnable,configShortAnimTime);
}

@Override
public void onAuthenticationFailed(){
showError();
mIcon.postDelayed(resetErrorRunnable,configShortAnimTime);
}

@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result){
mIcon.setColorFilter(mSuccessColor);

mIcon.postDelayed(()-> mCallback.onAuthenticated(),configShortAnimTime);
}

private void showIcon(){
mIcon.setVisibility(View.VISIBLE);
}

private void showError(){
mIcon.setColorFilter(mAlertColor);
}

private void resetError(){
mIcon.clearColorFilter();
}

公共接口回调{

void onAuthenticated();
}
}


解决方案

是否是否需要 CryptoObject 取决于您是否要执行要求用户使用其指纹进行身份验证的加密操作。






例如,假设您的应用程序与服务器通信,并且在某些情况下您想向服务器证明用户已经使用您的应用程序中的指纹进行了身份验证。



您可能要做的就是在用户第一次使用在应用程序中注册(无论如何完成),创建需要指纹认证的RSA密钥对,并与服务器共享公钥。



稍后,当您想向服务器证明用户已通过身份验证,则可以要求服务器提供一些数据进行签名。然后,使用RSA私钥创建签名,并将其包装到 CryptoObject 中。用户通过身份验证后,您可以对从服务器获取的数据进行签名,然后将签名发送到服务器,服务器可以使用公钥来验证签名。



除了说 指纹身份验证成功 外,还具有更高的安全级别,因为-除非设备上存在一些严重的安全漏洞-私钥在用户通过身份验证之前是不可用的,即使在root用户的设备上也是如此


When we call

mFingerprintManager
            .authenticate(cryptoObject, 0 /* flags */, mCancellationSignal, this, null);

I notice that it is completely fine to pass null for cryptoObject. According to FingerprintManager documentation


According to https://github.com/googlesamples/android-FingerprintDialog, it shows a lengthy step to create CryptoObject.


So, I'm not sure, whether I should use a CryptoObject, or null for my use case. I had read Why crypto object is needed for Android fingerprint authentication? but still couldn't completely understand and decide for my case.

My use case is as follow.

I'm having a startup locking screen for a note taking app. Usually, when user enables startup locking screen, he needs to setup pattern drawing. In case he forgets his pattern drawing, he can use his fingerprint as alternative. The app looks as following


This is the source code. Currently, I'm using a CryptoObject. However, according to my users feedback, a minority of them are facing some app issue for this new feature. Although we don't see any crash reports in Google Play Console, we suspect something went wrong during CryptoObject generation.

So, if the CryptoObject can be replaced with null, we would happily do that to simplify our code.

Do I need CryptoObject object, or null for the following use case during FingerprintManager.authenticate


/**
 * Small helper class to manage text/icon around fingerprint authentication UI.
 */
public class FingerprintUiHelper extends FingerprintManagerCompat.AuthenticationCallback {
    private static final String TAG = "FingerprintUiHelper";

    private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
    private static final String DEFAULT_KEY_NAME = "hello world key name";

    private int configShortAnimTime;

    private final FingerprintManagerCompat mFingerprintManager;
    private final ImageView mIcon;
    private final Callback mCallback;
    private CancellationSignal mCancellationSignal;

    private boolean mSelfCancelled;

    private final ResetErrorRunnable resetErrorRunnable = new ResetErrorRunnable();

    private final int mSuccessColor;
    private final int mAlertColor;

    private class ResetErrorRunnable implements Runnable {

        @Override
        public void run() {
            resetError();
        }
    }

    public static FingerprintUiHelper newInstance(ImageView icon, Callback callback, int successColor, int alertColor) {
        FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(WeNoteApplication.instance());
        return new FingerprintUiHelper(fingerprintManagerCompat, icon, callback, successColor, alertColor);
    }

    private void initResource() {
        configShortAnimTime = WeNoteApplication.instance().getResources().getInteger(android.R.integer.config_shortAnimTime);
    }

    /**
     * Constructor for {@link FingerprintUiHelper}.
     */
    private FingerprintUiHelper(FingerprintManagerCompat fingerprintManager,
                        ImageView icon, Callback callback, int successColor, int alertColor) {
        initResource();

        mFingerprintManager = fingerprintManager;
        mIcon = icon;
        mCallback = callback;
        mSuccessColor = successColor;
        mAlertColor = alertColor;
    }

    public boolean isFingerprintAuthAvailable() {
        // The line below prevents the false positive inspection from Android Studio
        // noinspection ResourceType
        return mFingerprintManager.isHardwareDetected()
                && mFingerprintManager.hasEnrolledFingerprints();
    }

    /**
     * Initialize the {@link Cipher} instance with the created key in the
     * {@link #createKey(String, boolean)} method.
     *
     * @param keyName the key name to init the cipher
     * @return {@code true} if initialization is successful, {@code false} if the lock screen has
     * been disabled or reset after the key was generated, or if a fingerprint got enrolled after
     * the key was generated.
     */
    private boolean initCipher(Cipher cipher, String keyName) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return false;
        }

        KeyStore keyStore;
        KeyGenerator keyGenerator;

        try {
            keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
        } catch (KeyStoreException e) {
            Log.e(TAG, "", e);
            return false;
        }

        try {
            keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            Log.e(TAG, "", e);
            return false;
        }

        // The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
        // for your flow. Use of keys is necessary if you need to know if the set of
        // enrolled fingerprints has changed.
        try {
            keyStore.load(null);
            // Set the alias of the entry in Android KeyStore where the key will appear
            // and the constrains (purposes) in the constructor of the Builder

            KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName,
                    KeyProperties.PURPOSE_ENCRYPT |
                            KeyProperties.PURPOSE_DECRYPT)
                    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                    // Require the user to authenticate with a fingerprint to authorize every use
                    // of the key
                    .setUserAuthenticationRequired(true)
                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);

            // This is a workaround to avoid crashes on devices whose API level is < 24
            // because KeyGenParameterSpec.Builder#setInvalidatedByBiometricEnrollment is only
            // visible on API level +24.
            // Ideally there should be a compat library for KeyGenParameterSpec.Builder but
            // which isn't available yet.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                builder.setInvalidatedByBiometricEnrollment(true);
            }
            keyGenerator.init(builder.build());
            keyGenerator.generateKey();
        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
                | CertificateException | IOException e) {
            Log.e(TAG, "", e);
            return false;
        }

        try {
            keyStore.load(null);
            SecretKey key = (SecretKey) keyStore.getKey(keyName, null);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return true;
        } catch (Exception e) {
            Log.e(TAG, "", e);
            return false;
        }
    }

    public void startListening() {
        if (!isFingerprintAuthAvailable()) {
            return;
        }

        Cipher defaultCipher;
        try {
            defaultCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                    + KeyProperties.BLOCK_MODE_CBC + "/"
                    + KeyProperties.ENCRYPTION_PADDING_PKCS7);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            Log.e(TAG, "", e);
            return;
        }

        if (false == initCipher(defaultCipher, DEFAULT_KEY_NAME)) {
            return;
        }

        FingerprintManagerCompat.CryptoObject cryptoObject = new FingerprintManagerCompat.CryptoObject(defaultCipher);

        startListening(cryptoObject);

        showIcon();
    }

    private void startListening(FingerprintManagerCompat.CryptoObject cryptoObject) {
        if (!isFingerprintAuthAvailable()) {
            return;
        }
        mCancellationSignal = new CancellationSignal();
        mSelfCancelled = false;

        // The line below prevents the false positive inspection from Android Studio
        // noinspection ResourceType
        mFingerprintManager
                .authenticate(cryptoObject, 0 /* flags */, mCancellationSignal, this, null);
    }

    public void stopListening() {
        if (mCancellationSignal != null) {
            mSelfCancelled = true;
            mCancellationSignal.cancel();
            mCancellationSignal = null;
        }
    }

    @Override
    public void onAuthenticationError(int errMsgId, CharSequence errString) {
        if (!mSelfCancelled) {
            if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) {
                mIcon.removeCallbacks(resetErrorRunnable);
                showError();
                return;
            }

            if (errMsgId == FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST) {
                return;
            }

            showError();
            mIcon.postDelayed(resetErrorRunnable, configShortAnimTime);
        }
    }

    @Override
    public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
        showError();
        mIcon.postDelayed(resetErrorRunnable, configShortAnimTime);
    }

    @Override
    public void onAuthenticationFailed() {
        showError();
        mIcon.postDelayed(resetErrorRunnable, configShortAnimTime);
    }

    @Override
    public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
        mIcon.setColorFilter(mSuccessColor);

        mIcon.postDelayed(() -> mCallback.onAuthenticated(), configShortAnimTime);
    }

    private void showIcon() {
        mIcon.setVisibility(View.VISIBLE);
    }

    private void showError() {
        mIcon.setColorFilter(mAlertColor);
    }

    private void resetError() {
        mIcon.clearColorFilter();
    }

    public interface Callback {

        void onAuthenticated();
    }
}
解决方案

Whether or not you need a CryptoObject comes down to whether you want to perform a cryptographic operation that requires the user to authenticate with their fingerprint. Only you knows the answer to that.


For example, let's say that your app communicates with a server, and at some point you want to prove to the server that the user has authenticated with their fingerprint in your app.

The way you might go about doing that is that, when the user first "registers" in your app (however that's done), you create an RSA keypair that requires fingerprint authentication, and you share the public key with the server.

Later, when you want to prove to the server that the user has authenticated, you could ask the server for some data to sign. You then create a Signature with the RSA private key, and wrap that into a CryptoObject. After the user has authenticated you can sign the data you got from the server, and send the signature to the server which can verify the signature using the public key.

This adds an additional level of security over just saying "the fingerprint authentication succeeded", because - unless there's some critical security flaw on the device - the private key is unusable until the user has authenticated, even on a rooted device.

这篇关于在FingerprintManager.authenticate中,以下使用情况是否需要CryptoObject对象或null?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-15 07:21