Можно ли сгенерировать 64-байтовый (256-битный) ключ и сохранить / получить его с помощью AndroidKeyStore? - PullRequest
0 голосов
/ 11 января 2019

В моем приложении для Android мне нужен способ шифрования данных, которые я храню в локальной БД. Я выбрал Realm DB, потому что предлагает бесшовную интеграцию с шифрованием. Мне просто нужно передать ключ при инициализации экземпляра Realm. Этот ключ должен иметь размер 64 байта.

По соображениям безопасности я обнаружил, что лучший способ сохранить этот ключ - это AndroidKeyStore. Я изо всех сил пытаюсь найти способ сгенерировать ключ (используя любой алгоритм) с таким размером и получить его в 64-байтовом массиве. Я пытаюсь сохранить minSdk API 19, но я считаю, что могу увеличить его до 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));
    }
}

Ответы [ 2 ]

0 голосов
/ 31 января 2019

Смысл 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 случайных байта. Однако вам нужно где-то хранить эти байты. Вы можете создать ключ AES с AndroidKeyStore и использовать его для шифрования 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);

Вам нужно будет сохранить и iv, и encryptedDbKey где-нибудь (не в базе данных!), Чтобы вы могли восстановить 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 так же, как ваше приложение.

AndroidKeyStore действительно может повысить ценность, если вы добавите некоторые дополнительные средства защиты в dbKeyWrappingKey. Например, если вы установите для него требование аутентификации пользователя в течение, скажем, пяти минут, можно будет использовать dbWrappingKey для расшифровки dbKey, когда пользователь рядом, чтобы ввести свой PIN-код / ​​шаблон / пароль или коснуться сканера отпечатков пальцев , Обратите внимание, что это работает только в том случае, если у пользователя есть PIN-код / ​​шаблон / пароль, но если они этого не делают, ваша база данных широко открыта для любого, кто все равно поднимает трубку.

См. KeyGenParameterSpec обо всех вещах, которые вы можете сделать, чтобы ограничить способы использования dbKeyWrappingKey.

0 голосов
/ 11 января 2019

Насколько я знаю, обычный способ решения этой проблемы состоит в том, что вы генерируете свой собственный случайный ключ нужного вам размера (мастер-ключ), и этот мастер-ключ может быть зашифрован с помощью ключа. магазин.

  1. Создайте свой собственный случайный мастер-ключ нужного вам размера.
  2. Шифровать данные с помощью этого мастер-ключа (например, симметричное шифрование).
  3. Зашифруйте мастер-ключ с помощью хранилища ключей.
  4. Храните зашифрованный мастер-ключ где-нибудь.

Чтобы расшифровать ваши данные:

  1. Считывание зашифрованного мастер-ключа.
  2. Расшифруйте мастер-ключ с помощью хранилища ключей.
  3. Расшифровать данные с помощью мастер-ключа.

Другими словами, это не главный ключ, который хранится в хранилище ключей, но хранилище ключей можно использовать для защиты / шифрования вашего главного ключа.

...