Java、Android 密钥库 - 提供未加密的 API 密钥或加密密钥

Java, Android Keystore - supply unencrypted API key or secret for encrypting

由于我是 Android 的新手,我正在努力隐藏我的 API 密钥并找到了 Android 密钥库。但是当我看到如何使用 Android Keystore 的示例时,我不明白的一件事是如何提供未加密的原始密钥进行加密?如果我存储在代码中,那不会超出使用 Android Keystore 的目的吗?

来自一篇关于存储秘密的文章: https://medium.com/@ericfu/securely-storing-secrets-in-an-android-application-501f030ae5a3

  1. Generate a random key when the app runs the first time;
  2. When you want to store a secret, retrieve the key from KeyStore, encrypt the data with it, and then store the encrypted data in Preferences.
  3. When you want to read a secret, read the encrypted data from Preferences, get the key from KeyStore and then use the key to decrypt the data

第二点,它说用它加密数据。如何在不暴露给 code/application 的情况下提供数据? 如果有人回答,我深表歉意。

谢谢

private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
private static final String AES_MODE = "AES/GCM/NoPadding";
private static final String KEY_ALIAS = "MyNiceKey";

加载默认的 AndroidKeyStore:

KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
keyStore.load(null);

在KeyStore中生成AES密钥,在最新版本android中,它是hardware-backed密钥库;这意味着很难从中提取密钥的字节:

if (!keyStore.containsAlias(KEY_ALIAS)) {
    KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_PROVIDER);
    keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_ALIAS,
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)                   
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            .setRandomizedEncryptionRequired(false) 
            .build());
    keyGenerator.generateKey();
}

无论如何你应该使用.setRandomizedEncryptionRequired(true)。没有必要设置错误的协议。否则,如果您只需要加密几个字节(您的 API 密钥),您可以创建一个非对称 public/private 密钥并使用 RSA 加密它,这样您甚至不需要提供 IV。

Haing 说,当您从 KeyStore 中获取密钥时:

 public static SecretKey getKeyStoreSecretKeyEntry(final String entryAlias)
            throws GeneralSecurityException, IOException {
        return ((KeyStore.SecretKeyEntry) getKeyStore().getEntry(entryAlias, null)).getSecretKey();
    }

返回的 SecretKey 不包含密钥 Material(密钥的实际字节),仅包含其引用。所以你可以在 Cipher 旁边自由使用它来加密和解密你想要的东西。在任何情况下,如果您使用它直接向您的服务发出 http 请求,您的 API 密钥将以任何方式暴露。在你的情况下最好的方法是使用像 Google Firebase

这样的服务器

P.s。 google 中有一个非常简单的库,可以节省您的时间和头痛: https://developer.android.com/jetpack/androidx/releases/security

https://developer.android.com/topic/security/data

结论:您在 android 密钥库中生成的密钥是用户的 属性,应该用于保护用户的私人数据。因此,使用用户密钥加密 API 密钥(这是开发人员的私有数据)并不是一个好习惯。使用服务器保护 API 密钥。