В какой-то момент времени мы все хотели обезопасить ваши данные от взлома/обратного проектирования. Основные механизмы безопасности, такие как,
a. Защита, Сокращение ресурсов и минификация b. Скрывается в манифесте c. Скрывается в сборке. Степень d. Хранение в базе данных MySQL/корневой базе данных/SharedPreference e. Прячась в Strings.xml .
Все эти методы не обеспечивают максимальной безопасности. Наиболее распространенную и известную логику защиты ключа можно обойти либо путем изменения и извлечения его из файла dex->jar, либо путем укоренения устройства и доступа к хранилищу устройства. Но есть еще один метод, который превосходит их все. Механизм, который обычно используется приложениями для хранения очень конфиденциальных данных, таких как данные кредитной карты, банковский счет и тому подобное.
Хранилище ключей привязано к аппаратной безопасности, которая обычно используется для хранения криптографических ключей. Хакерам становится невероятно сложно получить к нему доступ, поскольку хранилище ключей очень специфично для каждого приложения. С введением покончено, теперь давайте перейдем к коду –
Объявить несколько переменных в Cryptor.java
частная статическая конечная строка ПРЕОБРАЗОВАНИЕ ; частная статическая конечная строка ANDROID_KEY_STORE ; закрытый байт[] iv ; частное хранилище ключей Хранилище ключей ; частная статическая конечная строка SAMPLE_ALIAS ;
a. ПРЕОБРАЗОВАНИЕ используется для настройки алгоритма, который будет использоваться для кодирования. b. iv известен как вектор инициализации, который представляет собой произвольное число, используемое вместе с секретным ключом для шифрования. (Он может храниться в общедоступном хранилище, таком как SharedPreference, Room DB или MySQL DB). c. SAMPLE_ALIAS используется для доступа к сущности, хранящейся внутри хранилища ключей.
Для шифрования значения с помощью хранилища ключей мы можем сделать это с помощью,
a. Создайте объект класса Cryptor. b. Используйте метод set для инициализации шифрования с использованием секретного ключа в классе Crypto. c. Зашифруйте текст с помощью функции шифрования, определенной в классе Cryptor. d. Сохраните Iv и зашифрованный текст (Iv может быть общедоступным, и это не вызывает никаких проблем) в базе данных SharedPreference или Room.
В RegistrationActivity.java
Cryptor cryptor = new Cryptor(); try { cryptor.setIv(); prefs.edit().putString("encryptedKey", cryptor.encryptText("text_to_be_encrypted")).apply(); prefs.edit().putString("keyIv", cryptor.getIv_string()).apply(); Intent intent = new Intent(RegistrationActivity.this, HomeScreen.class); startActivity(intent); finish(); } catch (NoSuchPaddingException e) { unexpectedError(); e.printStackTrace(); } catch (NoSuchAlgorithmException e) { unexpectedError(); e.printStackTrace(); } catch (NoSuchProviderException e) { unexpectedError(); e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { unexpectedError(); e.printStackTrace(); } catch (InvalidKeyException e) { unexpectedError(); e.printStackTrace(); }
В Cryptor.java , мы определяем следующие функции
1. метод set():
public void setIv() throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException { Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey_en()); iv = cipher.getIV(); }
2. Метод getSecretKey_en():
@NonNull private SecretKey getSecretKey_en() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { final KeyGenerator keyGenerator; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE); keyGenerator.init(new KeyGenParameterSpec.Builder(Cryptor.SAMPLE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); return keyGenerator.generateKey(); } else { keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE); keyGenerator.init(new KeyGenParameterSpec.Builder(Cryptor.SAMPLE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); return keyGenerator.generateKey(); } }
3. Зашифрованный текст(строка string_to_encrypt):
public String encryptText(String string_to_encrypt) { try { final byte[] encryptedText = encryptData(string_to_encrypt); return Base64.encodeToString(encryptedText, Base64.DEFAULT); } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); } return ""; }
4. Encryptdata(Строка text_to_encrypt):
private byte[] encryptData(final String textToEncrypt) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException { final Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey_en()); iv = cipher.getIV(); return (cipher.doFinal(textToEncrypt.getBytes(StandardCharsets.UTF_8))); }
5. getIv_string():
public String getIv_string() { return Base64.encodeToString(iv, Base64.DEFAULT); }
Пояснение: Мы генерируем секретный ключ, используя хранилище ключей с определенными алгоритмами и ПСЕВДОНИМОМ. сгенерированный секретный ключ используется для инициализации шифрования и получения IV. Функция шифрования текста использует текст и iv для шифрования текста в хранилище ключей и выдает зашифрованный текст, который может быть сохранен на любом общем носителе данных.
Для расшифровки значения с помощью хранилища ключей мы можем сделать это с помощью: a. Создайте объект класса Cryptor. b. Инициализируйте экземпляр хранилища ключей. c. Используйте функцию расшифровки, передав зашифрованный текст и iv (сохраненный в базе данных SharedPreference или Room).
В В
String iv = prefs.getString("keyIv", "null"); String encrypted = prefs.getString("encryptedKey", ""); try { Cryptor cryptor = new Cryptor(); cryptor.initKeyStore(); String decrypted = cryptor.decryptText(encrypted, iv); } catch (KeyStoreException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
В Cryptor.java , добавьте следующие функции
1.инициализировать хранилище ключей():
public void initKeyStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); }
2. Расшифровываемый текст(Строка encrypted_string, Строка iv):
public String decryptText(String encrypted, String iv) { try { return decryptData(encrypted, Base64.decode(iv,Base64.DEFAULT)); } catch (UnrecoverableEntryException | NoSuchAlgorithmException | KeyStoreException | NoSuchPaddingException | InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) { e.printStackTrace(); } return ""; }
3. Расшифровать данные(Строка encrypted_string, байт[] Iv):
private String decryptData(String encrypted, final byte[] encryptionIv) throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException { final Cipher cipher = Cipher.getInstance(TRANSFORMATION); final GCMParameterSpec spec = new GCMParameterSpec(128, encryptionIv); cipher.init(Cipher.DECRYPT_MODE, getSecretKey_de(), spec); byte[] encryptedData = Base64.decode(encrypted,Base64.DEFAULT); return new String(cipher.doFinal(encryptedData), StandardCharsets.UTF_8); }
Пояснение: При расшифровке мы получаем сохраненный Iv и зашифрованный текст, хранящиеся на нашем одном из носителей данных. Мы инициализируем хранилище ключей с помощью ANDROID_KEY_STORE и расшифровываем текст с помощью Iv и методом init и doFinal Шифра.
Итак, благодаря вышеупомянутой реализации секреты теперь находятся в безопасности в хранилище ключей. Почему это, вероятно, лучший метод, заключается в том, что хранилище ключей очень специфично для приложения. Он не может быть извлечен, и, следовательно, текст не может быть расшифрован без него. Многие приложения, в которых хранятся кредитные карты и другие конфиденциальные данные пользователей, используют этот метод шифрования для обеспечения их безопасности. Для получения полного кода вы можете заглянуть в мой репозиторий GitHub. Объясняя код, я определил такие файлы, как — Cryptor.java , RegistrationActivity.java и HomeScreen.java . Я также использовал базу данных Room Database (запущенную Google в 2018 году), которая обеспечивает более высокий уровень безопасности, чем SQLLite (доступ к ней возможен, если устройство укоренено), для хранения имени пользователя и пароля для аутентификации зарегистрированных пользователей.
Я также использовал базу данных Room Database (запущенную Google в 2018 году), которая обеспечивает более высокий уровень безопасности, чем SQLLite (доступ к ней возможен, если устройство укоренено), для хранения имени пользователя и пароля для аутентификации зарегистрированных пользователей.
Оригинал: “https://dev.to/varundwarkani10/securing-secret-key-in-android-using-keystore-1kj6”