如何仅对解密而不对加密要求用户身份验证

时间:2019-06-12 14:41:05

标签: android android-keystore

我在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()

我希望每次使用私钥时都需要生物识别身份验证,但是在使用公钥加密时,我不需要生物识别提示。但是,在使用时,我在KeyGeneratior中使用setUserAuthenticationRequired(true),然后尝试不先显示BiometricPrompt进行加密,却收到android.security.KeyStoreException并显示以下消息:Key user not authenticated

我如何要求对 de 加密进行身份验证,而不要求对 en 加密进行身份验证?

1 个答案:

答案 0 :(得分:1)

您必须在运行Android 6,棉花糖的设备上进行测试。这是该版本中的known issue,已在Android 7中修复。

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

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

这将适用于所有版本。

请注意,还可以创建在解密时需要身份验证但在加密时不需要身份验证的AES密钥,这很酷(AES比RSA快得多,快得多)。诀窍是在AndroidKeyStore外部生成密钥,然后将其导入两次,一次使用PURPOSE_ENCRYPT,一次使用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。 / p>

此方法的缺点是该秘密短暂存在于非安全存储器中。实际上,这仅在创建密钥时攻击者已经破坏了设备的情况下才重要,在这种情况下,您很可能已经丢失了。