硬编码的加密密钥
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
OWASP 类别:MASVS-CRYPTO:加密
概览
开发者会利用加密技术,通过可靠的算法保护数据的机密性和完整性。然而密钥存储的使用偏少,并且通常通过代码或资源文件(如 strings.xml
)以字符串或字节数组的形式硬编码到应用中。如果在应用的任何文件中公开 Secret,就会违反 Kerchoff 的原则,并且安全模型可能会被视为已损坏。
影响
掌握逆向工程工具的攻击者可以非常轻松地检索硬编码的 Secret。具体影响可能会因情况而异,但在很多情况下,此漏洞可能会导致重大的安全问题,例如对敏感数据的不当访问。
缓解措施
您可以通过以下方法缓解此问题:如果您希望采用系统级凭据,可考虑使用 KeyChain API;如果您想让单个应用可以存储自己的凭据,并且只有该应用本身可以访问相应凭据,可使用 Android 密钥库提供程序。
以下代码段展示了如何利用 KeyStore
存储和使用对称密钥:
Kotlin
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())
}
Java
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());
}
资源
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2023-12-13。
[null,null,["最后更新时间 (UTC):2023-12-13。"],[],[],null,["# Hardcoded Cryptographic Secrets\n\n\u003cbr /\u003e\n\n**OWASP category:** [MASVS-CRYPTO: Cryptography](https://mas.owasp.org/MASVS/06-MASVS-CRYPTO)\n\nOverview\n--------\n\n| **Note:** This article isn't focused on how to protect API keys.\n\nDevelopers use cryptography to protect confidentiality and integrity of data using robust algorithms. However, the key storage is often underused, and it's common to find them hardcoded into the application as a string or byte array in the code or in an asset file such as `strings.xml`. If secrets are exposed in any files of the app, this goes against [Kerchoff's principle](https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle) and the security model can be considered as broken.\n\nImpact\n------\n\nAn attacker with access to reverse engineering tools can retrieve a hard-coded secret very easily. Depending on conditions the impact might vary, but in many cases it leads to major security issues, such as access to sensitive data.\n\nMitigations\n-----------\n\nTo mitigate this issue, consider using the [KeyChain](/reference/android/security/KeyChain) API when you want system-wide credentials, or the [Android Keystore](/training/articles/keystore) provider to let an individual app store its own credentials that only the app itself can access.\n\nThe following code snippet shows how to store and use a symmetric key using\n`KeyStore`: \n\n### Kotlin\n\n private val ANDROID_KEY_STORE_PROVIDER = \"AndroidKeyStore\"\n private val ANDROID_KEY_STORE_ALIAS = \"AES_KEY_DEMO\"\n\n @Throws(\n KeyStoreException::class,\n NoSuchAlgorithmException::class,\n NoSuchProviderException::class,\n InvalidAlgorithmParameterException::class\n )\n private fun createAndStoreSecretKey() {\n val builder: KeyGenParameterSpec.Builder = KeyGenParameterSpec.Builder(\n ANDROID_KEY_STORE_ALIAS,\n KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT\n )\n val keySpec: KeyGenParameterSpec = builder\n .setKeySize(256)\n .setBlockModes(KeyProperties.BLOCK_MODE_GCM)\n .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)\n .setRandomizedEncryptionRequired(true)\n .build()\n val aesKeyGenerator: KeyGenerator =\n KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER)\n aesKeyGenerator.init(keySpec)\n val key: SecretKey = aesKeyGenerator.generateKey()\n }\n\n @Throws(\n KeyStoreException::class,\n UnrecoverableEntryException::class,\n NoSuchAlgorithmException::class,\n CertificateException::class,\n IOException::class,\n NoSuchPaddingException::class,\n InvalidKeyException::class,\n IllegalBlockSizeException::class,\n BadPaddingException::class\n )\n private fun encryptWithKeyStore(plainText: String): ByteArray? {\n // Initialize KeyStore\n val keyStore: KeyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER)\n keyStore.load(null)\n // Retrieve the key with alias androidKeyStoreAlias created before\n val keyEntry: KeyStore.SecretKeyEntry =\n keyStore.getEntry(ANDROID_KEY_STORE_ALIAS, null) as KeyStore.SecretKeyEntry\n val key: SecretKey = keyEntry.secretKey\n // Use the secret key at your convenience\n val cipher: Cipher = Cipher.getInstance(\"AES/GCM/NoPadding\")\n cipher.init(Cipher.ENCRYPT_MODE, key)\n return cipher.doFinal(plainText.toByteArray())\n }\n\n### Java\n\n static private final String ANDROID_KEY_STORE_PROVIDER = \"AndroidKeyStore\";\n static private final String ANDROID_KEY_STORE_ALIAS = \"AES_KEY_DEMO\";\n\n private void createAndStoreSecretKey() throws KeyStoreException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {\n KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(\n ANDROID_KEY_STORE_ALIAS,\n KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);\n KeyGenParameterSpec keySpec = builder\n .setKeySize(256)\n .setBlockModes(KeyProperties.BLOCK_MODE_GCM)\n .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)\n .setRandomizedEncryptionRequired(true)\n .build();\n KeyGenerator aesKeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER);\n aesKeyGenerator.init(keySpec);\n SecretKey key = aesKeyGenerator.generateKey();\n }\n\n private byte[] encryptWithKeyStore(final String plainText) throws KeyStoreException, UnrecoverableEntryException, NoSuchAlgorithmException, CertificateException, IOException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {\n // Initialize KeyStore\n KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);\n keyStore.load(null);\n // Retrieve the key with alias ANDROID_KEY_STORE_ALIAS created before\n KeyStore.SecretKeyEntry keyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ANDROID_KEY_STORE_ALIAS, null);\n SecretKey key = keyEntry.getSecretKey();\n // Use the secret key at your convenience\n final Cipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n cipher.init(Cipher.ENCRYPT_MODE, key);\n return cipher.doFinal(plainText.getBytes());\n }\n\nResources\n---------\n\n- [Android KeyStore system](/training/articles/keystore)\n\n- [KeyStore documentation](/reference/java/security/KeyStore)\n\n- [KeyChain documentation](/reference/android/security/KeyChain)\n\n- [CWE-321: Use of Hard-coded Cryptographic Key](https://cwe.mitre.org/data/definitions/321.html)"]]