簡體   English   中英

是否可以生成64字節(256位)密鑰並使用AndroidKeyStore存儲/檢索它?

[英]Is it possible to generate a 64-byte (256-bit) key and store/retrieve it with AndroidKeyStore?

在我的Android應用程序中,我需要一種對存儲在本地數據庫中的數據進行加密的方法。 我選擇Realm DB是因為它提供了與加密的無縫集成。 我只需要在初始化Realm實例時傳遞密鑰。 該密鑰必須為64字節大小。

出於安全原因,我發現最好的存儲方式是在AndroidKeyStore中。 我正在努力尋找一種方法來生成具有該大小的密鑰(使用任何算法),並將其放入64字節數組中。 我正在嘗試保留API 19的minSdk,但我相信如果需要可以將其提高到23(這兩個版本之間對AndroidKeyStore進行了許多更改)。

有人有主意嗎? 這是我的代碼:

類Encryption.java

private static KeyStore ks = null;
private static String ALIAS = "com.oi.pap";

public static byte[] loadkey(Context context) {

    byte[] content = new byte[64];
    try {
        if (ks == null) {
            createNewKeys(context);
        }

        ks = KeyStore.getInstance("AndroidKeyStore");
        ks.load(null);

        content= ks.getCertificate(ALIAS).getEncoded(); //<----- HERE, I GET SIZE GREATER THAN 64
        Log.e(TAG, "original key :" + Arrays.toString(content));
    } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    content = Arrays.copyOfRange(content, 0, 64); //<---- I would like to remove this part.
    return content;
}

private static void createNewKeys(Context context) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {

    ks = KeyStore.getInstance("AndroidKeyStore");
    ks.load(null);
    try {
        // Create new key if needed
        if (!ks.containsAlias(ALIAS)) {
            Calendar start = Calendar.getInstance();
            Calendar end = Calendar.getInstance();
            end.add(Calendar.YEAR, 1);
            KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
                    .setAlias(ALIAS)
                    .setSubject(new X500Principal("CN=PapRealmKey, O=oipap"))
                    .setSerialNumber(BigInteger.ONE)
                    .setStartDate(start.getTime())
                    .setEndDate(end.getTime())
                    .setKeySize(256)
                    .setKeyType(KeyProperties.KEY_ALGORITHM_EC)
                    .build();
            KeyPairGenerator generator = KeyPairGenerator
                    .getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
            generator.initialize(spec);

            KeyPair keyPair = generator.generateKeyPair();
            Log.e(TAG, "generated key :" + Arrays.toString(keyPair.getPrivate().getEncoded()));

        }
    } catch (Exception e) {
        Log.e(TAG, Log.getStackTraceString(e));
    }
}

AndroidKeyStore的目的是將敏感密鑰材料從您的應用程序,操作系統中移出,並移入安全的硬件中,絕不會泄漏或受到損害。 因此,根據設計,如果您在AndroidKeyStore中創建密鑰,則永遠無法獲取密鑰資料。

在這種情況下,Realm DB需要密鑰材料,因此您不能為其提供AndroidKeyStore密鑰。 另外,Realm想要的是您嘗試生成的兩個AES密鑰,而不是EC密鑰。

生成所需關鍵材料的正確方法是:

byte[] dbKey = new byte[64];
Random random = new SecureRandom();
random.nextBytes(dbKey);
// Pass dbKey to Realm DB...
Arrays.fill(dbKey, 0); // Wipe key after use.

只有64個隨機字節。 但是,您將需要將這些字節存儲在某個地方。 您可以使用AndroidKeyStore創建AES密鑰,並使用它來加密dbKey 就像是:

KeyGenerator keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(
        new KeyGenParameterSpec.Builder("dbKeyWrappingKey",
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)      
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .build());
SecretKey key = keyGenerator.generateKey();

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV();
byte[] encryptedDbKey = cipher.doFinal(dbKey);

你需要節省ivencryptedDbKey的地方(而不是在數據庫!),這樣就可以恢復dbKey 然后您可以使用以下方法解密:

KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
key = (SecretKey) keyStore.getKey("dbKeyWrappingKey", null);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv));
byte[] dbKey = cipher.doFinal(encryptedDbKey);
// Pass dbKey to Realm DB and then wipe it.

但是,盡管如此,我不認為您應該做任何事情。 我認為這實際上不會為您提供任何安全性,而Android默認情況下不會為您提供任何安全性。 如果攻擊者試圖轉儲包含您的數據庫的設備存儲,則他將一無所獲,因為Android仍然會加密所有存儲。 如果攻擊者可以啟動設備,則他可以像您的應用程序一樣運行代碼,並以與您的應用程序相同的方式使用它來解密dbKey

如果您在dbKeyWrappingKey上添加一些其他保護,則AndroidKeyStore可能真正增加價值的dbKeyWrappingKey 例如,如果將其設置為要求在五分鍾之內進行用戶身份驗證,則只有當用戶在附近輸入PIN /圖案/密碼或觸摸指紋掃描器時,才可以使用dbWrappingKey解密dbKey 請注意,這僅在用戶具有PIN /圖案/密碼的情況下才有效,但是如果沒有,則您的數據庫對任何仍然接聽電話的人都是開放的。

有關限制dbKeyWrappingKey使用方式的所有操作,請參見KeyGenParameterSpec

據我所知,解決此問題的通常方法是,生成自己所需大小的隨機密鑰(主密鑰),並且可以在密鑰存儲的幫助下對該主密鑰進行加密。

  1. 生成自己所需大小的隨機主密鑰。
  2. 使用此主密鑰加密數據(例如,對稱加密)。
  3. 在密鑰存儲的幫助下加密主密鑰。
  4. 將加密的主密鑰存儲在某處。

解密數據:

  1. 讀取加密的主密鑰。
  2. 在密鑰庫的幫助下解密主密鑰。
  3. 用主密鑰解密數據。

換句話說,不是主密鑰存儲在密鑰存儲區中,但是密鑰存儲區可用於保護/加密您的主密鑰。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM