java - 初始化 FingerpringManager.Crypto 对象,获取不受 AndroidKeyStore 提供程序支持的 Crypto 原语?

标签 java android encryption android-keystore android-fingerprint-api

我正在使用 Android FingerPrintManager API 并使用 KeyPairGenerator 创建 key 对,我想使用公钥加密密码,然后在用户通过输入 fingerPrint 进行身份验证时解密,但是一旦我运行我的项目它崩溃并给予

Caused by: java.lang.IllegalArgumentException: Crypto primitive not backed by AndroidKeyStore provider

我使用了这里的代码:Android Fingerprint API Encryption and Decryption 这篇文章说他能够进行加密和解密,并且 di 遵循相同的代码和步骤。 这是我的代码

public KeyStore getKeyStore() {
    try {
        return KeyStore.getInstance("AndroidKeyStore");
    } catch (KeyStoreException exception) {
        throw new RuntimeException("Failed to get an instance of KeyStore", exception);
    }
}

public KeyPairGenerator getKeyPairGenerator() {
    try {
        return KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
    } catch (NoSuchAlgorithmException | NoSuchProviderException exception) {
        throw new RuntimeException("Failed to get an instance of KeyPairGenerator", exception);
    }
}

public Cipher getCipher() {
    try {
        return Cipher.getInstance("RSA");
    } catch (NoSuchAlgorithmException | NoSuchPaddingException exception) {
        throw new RuntimeException("Failed to get an instance of Cipher", exception);
    }
}

private void createKeyPair() {
    try {
        mKeyPairGenerator = getKeyPairGenerator();
        mKeyPairGenerator.initialize(
                new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_DECRYPT)
                        .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
                        .setUserAuthenticationRequired(true)
                        .build());
        mKeyPairGenerator.generateKeyPair();
    } catch (InvalidAlgorithmParameterException exception) {
        throw new RuntimeException(exception);
    }
}

private boolean initCipher(int opmode) {
    try {
        mKeyStore = getKeyStore();
        mKeyStore.load(null);

        mCipher = getCipher();

        if (opmode == Cipher.ENCRYPT_MODE) {

            PublicKey key = mKeyStore.getCertificate(KEY_NAME).getPublicKey();

            PublicKey unrestricted = KeyFactory.getInstance(key.getAlgorithm())
                    .generatePublic(new X509EncodedKeySpec(key.getEncoded()));

            OAEPParameterSpec spec = new OAEPParameterSpec(
                    "SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
            mCipher.init(opmode, unrestricted, spec);
        } else {
            PrivateKey key = (PrivateKey) mKeyStore.getKey(KEY_NAME, null);
            mCipher.init(opmode, key);
        }

        return true;
    } catch (KeyPermanentlyInvalidatedException exception) {
        return false;
    } catch (KeyStoreException | CertificateException | UnrecoverableKeyException
            | IOException | NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | InvalidAlgorithmParameterException exception) {
        throw new RuntimeException("Failed to initialize Cipher", exception);
    }
}


private void encrypt(String password) {
    try {
        initCipher(Cipher.ENCRYPT_MODE);
        byte[] bytes = mCipher.doFinal(password.getBytes());
        enrcyptedPassword = Base64.encodeToString(bytes, Base64.NO_WRAP);
        Log.d("EncryptedText", enrcyptedPassword);
    } catch (IllegalBlockSizeException | BadPaddingException exception) {
        throw new RuntimeException("Failed to encrypt password", exception);
    }
}

private String decryptPassword(Cipher cipher) {
    try {
        initCipher(Cipher.DECRYPT_MODE);
        byte[] bytes = Base64.decode(enrcyptedPassword, Base64.NO_WRAP);
        return new String(cipher.doFinal(bytes));
    } catch (IllegalBlockSizeException | BadPaddingException | RuntimeException exception) {
        throw new RuntimeException("Failed to decrypt password", exception);
    }
}

我从这里开始初始化我的 CryptoObject:

createKeyPair();
    if (initCipher(Cipher.ENCRYPT_MODE)) {
        mCryptoObject = new FingerprintManager.CryptoObject
                (mCipher);
        encrypt("1111");
        if (!isFingerprintAuthAvailable()) {
            return;
        }
        mCancellationSignal = new CancellationSignal();
        mSelfCancelled = false;
        mFingerprintManager.authenticate(mCryptoObject, mCancellationSignal, 0 /* flags */, this, null);

我在这一行遇到异常:

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

最佳答案

@AlexKlyubin 是对的,您不需要使用指纹管理器进行加密,只需解密即可。为了加密文本,您需要做的就是调用上面的 encrypt(String password) 方法。

对于解密,您应该使用 FingerprintManagerCompat 而不是 FingerprintManager。为了监听指纹事件和解密密码,您需要扩展 FingerprintManagerCompat.AuthenticationCallback。我扩展了这个类,并实现了一个回调接口(interface):

public class FingerprintAuthentication extends FingerprintManagerCompat.AuthenticationCallback {

    private final Callback mCallback;

    public FingerprintCallback(Callback callback) {
        mCallback = callback;
    }

    @Override
    public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
        mCallback.onAuthenticationSucceeded(result);
    }

    @Override
    public void onAuthenticationHelp(int messageId, CharSequence message) {
        mCallback.onAuthenticationHelp(messageId, message);
    }

    @Override
    public void onAuthenticationError(int messageId, CharSequence message) {
        mCallback.onAuthenticationError(messageId, message);
    }

    @Override
    public void onAuthenticationFailed() {
        mCallback.onAuthenticationFailed();
    }

    public interface Callback {

        void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result);

        void onAuthenticationHelp(int messageId, CharSequence message);

        void onAuthenticationError(int messageId, CharSequence message);

        void onAuthenticationFailed();
    }
}

这样你就可以在你的FragmentActivity中实现Callback接口(interface),然后开始监听事件:

private void startListening(boolean cipher) {
    Timber.v("Start listening for fingerprint input");
    mCancellationSignal = new CancellationSignal();
    if(cipher) {
        mFingerprintManager.authenticate(new FingerprintManagerCompat.CryptoObject(mCipher),
                0, mCancellationSignal, new FingerprintAuthentication(this), null);
    } else {
        setStage(Stage.CREDENTIALS);
    }
}

最后,指纹认证成功后才能解密密码:

@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
    try {
        mPassword = decryptPassword(result.getCryptoObject().getCipher());
    } catch (IllegalBlockSizeException | BadPaddingException exception) {
        exception.printStackTrace();
    }
}

基本上,当用户首次登录时,您希望为他们显示一个“将来使用指纹”的选项:

enter image description here

如果用户选择此选项并单击登录,这就是您调用 encrypt() 的时间。然后,下次要求用户登录时,您将显示指纹对话框:

enter image description here

此时调用 startListening(initializeCipher(Cipher.DECRYPT_MODE))

关于java - 初始化 FingerpringManager.Crypto 对象,获取不受 AndroidKeyStore 提供程序支持的 Crypto 原语?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38016968/

相关文章:

java - 无法在 Android 中解密加密的短信(在 2.2 操作系统中加密)...(尝试在 2.3 操作系统中解密)

Java JNA native 库调用 NoClassDefFoundError

android - 无法使用 volley 库比较 android 中 “if” 语句中的 JSON 字符串响应

Android 通知 MediaStyle 忽略正在进行的标志

android - 为什么我的图像/视频元数据被删除?如何在我的文件中保存自定义信息?

java - 在 Android 中使用 AES/GCM/NoPadding 加密的消息解密时出错

java - JMenuItem 设置对齐方式和最大尺寸

java - 自动清除 Java Web Start 缓存

java - 读取实体时更改 DAO/Repository 中的特定值

javascript - 有没有办法用Python解密AES?