رده OWASP: MASVS-CRYPTO: رمزنگاری
نمای کلی
توسعهدهندگان از رمزنگاری برای محافظت از محرمانگی و یکپارچگی دادهها با استفاده از الگوریتمهای قوی استفاده میکنند. با این حال، محل ذخیرهسازی کلید اغلب به اندازه کافی استفاده نمیشود و معمولاً میتوان آنها را به صورت کدگذاری شده در برنامه به عنوان یک رشته یا آرایه بایت در کد یا در یک فایل دارایی مانند strings.xml یافت. اگر اسرار در هر فایلی از برنامه افشا شوند، این امر خلاف اصل کرچوف است و مدل امنیتی را میتوان ناقص در نظر گرفت.
تأثیر
یک مهاجم با دسترسی به ابزارهای مهندسی معکوس میتواند به راحتی یک رمز عبور کدگذاری شده را بازیابی کند. بسته به شرایط، تأثیر ممکن است متفاوت باشد، اما در بسیاری از موارد منجر به مشکلات امنیتی عمدهای مانند دسترسی به دادههای حساس میشود.
کاهشها
برای کاهش این مشکل، وقتی میخواهید اعتبارنامههای سراسری سیستم را داشته باشید، از API KeyChain استفاده کنید، یا از ارائهدهندهی Keystore اندروید استفاده کنید تا به یک برنامهی خاص اجازه دهید اعتبارنامههای خود را ذخیره کند که فقط خود برنامه بتواند به آنها دسترسی داشته باشد.
قطعه کد زیر نحوه ذخیره و استفاده از یک کلید متقارن با استفاده از KeyStore را نشان میدهد:
کاتلین
private val ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"
private val ANDROID_KEY_STORE_ALIAS = "AES_KEY_DEMO"
@Throws(
KeyStoreException::class,
NoSuchAlgorithmException::class,
NoSuchProviderException::class,
InvalidAlgorithmParameterException::class
)
private fun createAndStoreSecretKey() {
val builder: KeyGenParameterSpec.Builder = KeyGenParameterSpec.Builder(
ANDROID_KEY_STORE_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
val keySpec: KeyGenParameterSpec = builder
.setKeySize(256)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(true)
.build()
val aesKeyGenerator: KeyGenerator =
KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER)
aesKeyGenerator.init(keySpec)
val key: SecretKey = aesKeyGenerator.generateKey()
}
@Throws(
KeyStoreException::class,
UnrecoverableEntryException::class,
NoSuchAlgorithmException::class,
CertificateException::class,
IOException::class,
NoSuchPaddingException::class,
InvalidKeyException::class,
IllegalBlockSizeException::class,
BadPaddingException::class
)
private fun encryptWithKeyStore(plainText: String): ByteArray? {
// Initialize KeyStore
val keyStore: KeyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER)
keyStore.load(null)
// Retrieve the key with alias androidKeyStoreAlias created before
val keyEntry: KeyStore.SecretKeyEntry =
keyStore.getEntry(ANDROID_KEY_STORE_ALIAS, null) as KeyStore.SecretKeyEntry
val key: SecretKey = keyEntry.secretKey
// Use the secret key at your convenience
val cipher: Cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, key)
return cipher.doFinal(plainText.toByteArray())
}
جاوا
static private final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
static private final String ANDROID_KEY_STORE_ALIAS = "AES_KEY_DEMO";
private void createAndStoreSecretKey() throws KeyStoreException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
ANDROID_KEY_STORE_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
KeyGenParameterSpec keySpec = builder
.setKeySize(256)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(true)
.build();
KeyGenerator aesKeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER);
aesKeyGenerator.init(keySpec);
SecretKey key = aesKeyGenerator.generateKey();
}
private byte[] encryptWithKeyStore(final String plainText) throws KeyStoreException, UnrecoverableEntryException, NoSuchAlgorithmException, CertificateException, IOException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
// Initialize KeyStore
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
keyStore.load(null);
// Retrieve the key with alias ANDROID_KEY_STORE_ALIAS created before
KeyStore.SecretKeyEntry keyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ANDROID_KEY_STORE_ALIAS, null);
SecretKey key = keyEntry.getSecretKey();
// Use the secret key at your convenience
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainText.getBytes());
}