代码之家  ›  专栏  ›  技术社区  ›  Cheok Yan Cheng

我是否需要CryptoObject对象,或者在Fingerprintmanager期间为以下用例使用空值。

  •  0
  • Cheok Yan Cheng  · 技术社区  · 7 年前

    当我们呼唤

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

    我注意到完全可以通过 null 对于 cryptoObject . 根据 FingerprintManager documentation

    Fingerprintmanager.CryptoObject:与调用或 如果不需要,则为空。


    根据 https://github.com/googlesamples/android-FingerprintDialog ,它显示了创建 CryptoObject .


    所以,我不确定是否应该使用 密码对象 ,或 无效的 用于我的用例。我读过 Why crypto object is needed for Android fingerprint authentication? 但仍然不能完全理解和决定我的案件。

    我的用例如下。

    我有一个记录程序的启动锁定屏幕。通常,当用户启用启动锁定屏幕时,他需要设置图案绘制。万一他忘了画图案,他可以用他的指纹作为替代品。应用程序如下所示

    enter image description here


    这是源代码。目前,我正在使用 密码对象 . 但是,根据我的用户反馈,他们中的一小部分人正面临着这个新功能的一些应用程序问题。虽然我们在Google Play控制台上看不到任何崩溃报告,但我们怀疑在 密码对象 世代。

    所以,如果 密码对象 可以用空替换,我们很乐意这样做来简化代码。

    我是否需要CryptoObject对象,或者在Fingerprintmanager期间为以下用例使用空值。


    /**
     * 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();
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   Michael    7 年前

    是否需要 CryptoObject 归根结底,您是否希望执行要求用户使用其指纹进行身份验证的加密操作。只有你知道答案。


    例如,假设您的应用程序与服务器通信,并且在某个时刻,您希望向服务器证明用户已使用应用程序中的指纹进行了身份验证。

    这样做的方法是,当用户在应用程序中首次“注册”时(不管完成了什么),您创建一个需要指纹认证的RSA密钥对,然后与服务器共享公钥。

    稍后,当您想向服务器证明用户已经过身份验证时,可以要求服务器签署一些数据。然后创建一个 Signature 使用RSA私钥,并将其包装为 密码对象 . 用户通过身份验证后,您可以对从服务器获得的数据进行签名,并将签名发送到服务器,服务器可以使用公钥验证签名。

    这增加了一个额外的安全级别,而不仅仅是说 “指纹验证成功” ,因为-除非设备上存在一些严重的安全缺陷-在用户进行身份验证之前,私钥是不可用的,即使是在根设备上。