使用指纹进行加密(结合密码)

Using fingerprints for encryption (in combination with a password)

如标​​题所述,我想使用指纹来加密我应用程序中的一些数据。具体来说,我希望用户能够设置密码,然后允许他们使用指纹(可能通过在设置中启用它等)再次解密。

我在 Alexander Malikov 的 Private notepad 应用程序中看到过这个。我已经研究过 Android 上的指纹,但我不知道如何用它加密字符串等。下载了已经提到的应用程序后,我的问题是:你怎么能只用指纹解密数据?(因为必须有某种方式来访问实际密码/个人识别码/模式设置解密笔记,但你永远不必手动设置或输入密码,你只需要扫描你的指纹)。

我的 phone 已 root,所以我决定查看此应用程序使用的数据库。它也存储密码或其他锁定方法的散列值以及加密的笔记,但没有指纹散列值的迹象。我知道如何使用指纹传感器验证用户的身份(无需手动散列任何内容,这说明散列未存储在任何地方)。因此,虽然我可以说(使用布尔值)如果用户已通过身份验证,我如何仅使用指纹并设置密码来访问密钥来解​​密我的数据?(例如,如果我将哈希存储在某处以供检查)

另外:OS/指纹API是否使用散列来验证用户?

(顺便说一句:我对加密和安全性了解不多,所以请原谅我使用了错误的术语等)

我希望有人能帮我解决这个问题:)

所以,如果我没理解错的话,你想在不扫描指纹的情况下加密数据(密码),但在解密数据时需要指纹。

首先您需要为 encryption/decryption 生成一个密钥对。这可以按如下方式完成:

private static final String KEY_STORE_ID = "AndroidKeyStore";
private static final String KEY_PAIR_ALIAS = "MyKeyPair"

private PrivateKey getPrivateKey() {
    KeyStore keyStore = getKeyStore();
    try {
        return (PrivateKey) keyStore.getKey(KEY_PAIR_ALIAS, null);
    } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
        throw new RuntimeException(e);
    }
}

private PublicKey getPublicKey() {
    KeyStore keyStore = getKeyStore();
    Certificate certificate;
    try {
        certificate = keyStore.getCertificate(KEY_PAIR_ALIAS);
    } catch (KeyStoreException e) {
        throw new RuntimeException(e);
    }
    if (certificate == null) {
        throw new RuntimeException("Key pair not found");
    }
    PublicKey publicKey = certificate.getPublicKey();

    // This conversion is currently needed on API Level 23 (Android M) due to a platform bug which prevents the
    // use of Android Keystore public keys when their private keys require user authentication. This conversion
    // creates a new public key which is not backed by Android Keystore and thus is not affected by the bug.
    // See https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html
    try {
        KeyFactory keyFactory = KeyFactory.getInstance(publicKey.getAlgorithm());
        return keyFactory.generatePublic(new X509EncodedKeySpec(publicKey.getEncoded()));
    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
        throw new RuntimeException("Failed to copy public key.", e);
    }
}

private KeyStore getKeyStore() {
    try {
        KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ID);
        keyStore.load(null);
        if (!keyStore.containsAlias(KEY_PAIR_ALIAS)) {
            resetKeyPair();
        }
        return keyStore;
    } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
        throw new RuntimeException(e);
    }
}

private void resetKeyPair() {
    try {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEY_STORE_ID);
        keyPairGenerator.initialize(new KeyGenParameterSpec.Builder(KEY_PAIR_ALIAS, KeyProperties.PURPOSE_DECRYPT)
                .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
                .setUserAuthenticationRequired(true).build());
        keyPairGenerator.generateKeyPair();
    } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
        throw new RuntimeException("Could not generate key pair", e);
    }
}

现在您可以使用 getPrivateKey() 获取解密密钥(需要身份验证)和 getPublicKey() 获取加密密钥(不需要身份验证)。您可以像这样使用它们:

public byte[] encrypt(byte[] input) throws KeyPermanentlyInvalidatedException, BadPaddingException, IllegalBlockSizeException {
    Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, getPublicKey());
    return cipher.doFinal(input);
}

public interface Callback {
    void done(byte[] result);
}

public CancellationSignal decrypt(Context context, final byte[] input, final Callback callback) throws KeyPermanentlyInvalidatedException {
    CancellationSignal cancellationSignal = new CancellationSignal();
    FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
    FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(getCipher(Cipher.DECRYPT_MODE, getPrivateKey()));
    fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, new FingerprintManager.AuthenticationCallback() {
        @Override
        public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
            Cipher cipher = result.getCryptoObject().getCipher();
            byte[] output;
            try {
                output = cipher.doFinal(input);
            } catch (IllegalBlockSizeException | BadPaddingException e) {
                throw new RuntimeException(e);
            }
            callback.done(output);
        }
        // Override other methods as well
    }, null);
    return cancellationSignal;
}

private Cipher getCipher(int mode, Key key) throws KeyPermanentlyInvalidatedException {
    Cipher cipher;
    try {
        cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_RSA + "/" + KeyProperties.BLOCK_MODE_ECB + "/" + "OAEPWithSHA-256AndMGF1Padding");
    } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
        throw new RuntimeException(e);
    }
    OAEPParameterSpec algorithmParameterSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
    try {
        cipher.init(mode, key, algorithmParameterSpec);
    } catch (KeyPermanentlyInvalidatedException e) {
        // The key pair has been invalidated!
        throw e;
    } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
        throw new RuntimeException(e);
    }
    return cipher;
}

请注意,这只是一个示例。一些建议的改进:

  • 改进错误处理。
  • 不要扔 RuntimeException.
  • 覆盖FingerprintManager.AuthenticationCallback的更多方法。
  • 如果你不希望密钥对在用户注册新指纹或删除旧指纹时失效,你可以在KeyGenParameterSpec.BuilderresetKeyPair() 中。这会降低安全性但会提高可用性。

不得不说我的措辞有点乱。。。最后只是研究不够的问题。

我假设我可以像 AES 那样使用对称加密。但是当我做了更多的研究后,我发现我完全误解了我想做的事情。

基本上我在想的是密码和指纹会以某种方式以 same 散列结束,这意味着它们实际上需要是相同的东西。我认为那是不可能的,而且显然不是,因此我提出了关于该主题的问题。

这也是为什么我想知道在我引用的应用程序中它是如何实现的,因为正如我所说,我从未在文件中看到任何哈希。 但是我就是不明白密​​码和指纹只是为了确认用户身份,我还需要用其他东西来加密/解密数据。

正如我所说,我对加密和安全性没有很好的理解(也许我的思考过程有点太复杂了,因为我错过了密码的基本用途)。 :)