4

我在 AndroidKeyStore 中有一个公钥/私钥对,生成如下:

val spec = KeyGenParameterSpec.Builder(alias(username), KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT)
                .setKeySize(keySize)
                .setUserAuthenticationRequired(true)
                .setBlockModes(ablockMode)
                .setEncryptionPaddings(apaddingMode)
                .setCertificateSubject(X500Principal("CN=Itsami Mario, OU=Adventure Unit, O=Plumber Bros, C=US"))
                .setKeyValidityStart(Date())
                .setKeyValidityEnd(Date(Date().time + 1000 * 60 * 60 * 24 * 7))
                .setCertificateSerialNumber(BigInteger(64, SecureRandom()))
                .setDigests(digest)
                .build()

        keyPairGen.initialize(spec)
        return keyPairGen.genKeyPair()

我想在每次使用私钥时都要求进行生物特征认证,但我不想在使用公钥加密时要求生物特征提示。但是,当我setUserAuthenticationRequired(true)在 KeyGenerator 中使用时,然后我尝试在不首先显示 BiometricPrompt 的情况下进行加密,我收到一条android.security.KeyStoreException消息:Key user not authenticated

我如何要求解密而不是加密身份验证?

4

1 回答 1

4

您必须在运行 Android 6 Marshmallow 的设备上进行测试。这是该版本中的一个已知问题,已在 Android 7 中修复。

要解决此问题,您可以提取公钥的编码并从中创建一个新PublicKey对象,如下所示:

PublicKey publicKey = keyPair.getPublicKey();
PublicKey unrestrictedPublicKey =
         KeyFactory.getInstance(publicKey.getAlgorithm()).generatePublic(
                 new X509EncodedKeySpec(publicKey.getEncoded()));

这将适用于所有版本。

请注意,也可以创建在解密时需要身份验证但在加密时不需要身份验证的 AES 密钥,这非常酷(AES 比 RSA 快得多)。诀窍是在 AndroidKeyStore 之外生成密钥,然后将其导入两次,一次 withPURPOSE_ENCRYPT和一次 with PURPOSE_DECRYPT,在两个不同的别名下,并在 DECRYPT 版本上指定用户身份验证要求。就像是:

// Note that we do *not* specify "AndroidKeyStore" when we call getInstance()
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKey = keyGen.generateKey();

// This time we do specify "AndroidKeyStore".
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);

// Now we import the encryption key, with no authentication requirements.
keyStore.setEntry(
     "encrypt_key",
     new KeyStore.SecretKeyEntry(secretKey),
     new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
             .setBlockMode(KeyProperties.BLOCK_MODE_GCM)
             .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
             .build());

// And the decryption key, this time requiring user authentication.
keyStore.setEntry(
     "decrypt_key",
     new KeyStore.SecretKeyEntry(secretKey),
     new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
             .setBlockMode(KeyProperties.BLOCK_MODE_GCM)
             .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
             .setUserAuthentication(true)
             .build());

现在,您可以随时使用密钥别名“encrypt_key”进行加密,无需用户身份验证,并且您可以使用密钥别名“decrypt_key”解密,但前提是您执行此操作BiometricPrompt

这样做的缺点是秘密短暂存在于非安全内存中。在实践中,只有当攻击者在创建密钥时已经破坏了设备时,这才重要,在这种情况下,您很可能已经丢失了。

于 2019-08-24T02:56:38.617 回答