You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中如何安全存储SHA-1等证书哈希值

Android敏感信息(SHA哈希、API密钥)安全存储方案

为什么values.xml不安全

把google_api、Firebase配置或SHA哈希这类敏感信息放在values.xml里,会直接被打包进APK,通过反编译工具就能轻松提取,完全没有安全性可言,必须改用加密存储方案。

核心方案:Android Keystore系统

Android Keystore是系统提供的安全密钥管理服务,生成的密钥会被存储在设备的安全硬件(如TEE、Secure Element)中,密钥本身不会离开安全环境,无法被提取,非常适合用来加密敏感数据。

实现步骤

1. 创建Keystore密钥对

首先要在Keystore中生成一对RSA密钥,公钥用于加密数据,私钥用于解密(私钥由系统托管,无法直接获取)。

import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyPairGenerator
import java.security.KeyStore
import java.security.spec.MGF1ParameterSpec
import javax.crypto.spec.OAEPParameterSpec
import javax.crypto.spec.PSource

private const val KEYSTORE_ALIAS = "my_app_secure_key"
private const val KEYSTORE_PROVIDER = "AndroidKeyStore"

fun createKeystoreKeyPair() {
    val keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER).apply { load(null) }

    // 如果密钥已存在,无需重复创建
    if (!keyStore.containsAlias(KEYSTORE_ALIAS)) {
        val keyPairGenerator = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_RSA,
            KEYSTORE_PROVIDER
        )

        val keyGenSpec = KeyGenParameterSpec.Builder(
            KEYSTORE_ALIAS,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
        )
            .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
            .setDigests(KeyProperties.DIGEST_SHA256)
            .build()

        keyPairGenerator.initialize(keyGenSpec)
        keyPairGenerator.generateKeyPair()
    }
}

2. 加密敏感数据

用生成的公钥对明文敏感数据(如SHA-1、Google API密钥)进行加密,加密后的结果可以转成Base64字符串,存储到SharedPreferences或其他本地存储中。

import android.util.Base64
import java.security.KeyStore
import javax.crypto.Cipher

fun encryptData(plainText: String): String {
    val keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER).apply { load(null) }
    val publicKey = keyStore.getCertificate(KEYSTORE_ALIAS).publicKey

    val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding").apply {
        init(Cipher.ENCRYPT_MODE, publicKey, OAEPParameterSpec(
            "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT
        ))
    }

    val encryptedBytes = cipher.doFinal(plainText.toByteArray(Charsets.UTF_8))
    return Base64.encodeToString(encryptedBytes, Base64.DEFAULT)
}

3. 解密数据

在需要使用敏感数据时,用Keystore中的私钥解密加密后的字符串,得到明文后仅在内存中使用,不要持久化明文。

fun decryptData(encryptedText: String): String {
    val keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER).apply { load(null) }
    val privateKey = keyStore.getKey(KEYSTORE_ALIAS, null) as java.security.PrivateKey

    val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding").apply {
        init(Cipher.DECRYPT_MODE, privateKey, OAEPParameterSpec(
            "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT
        ))
    }

    val encryptedBytes = Base64.decode(encryptedText, Base64.DEFAULT)
    val decryptedBytes = cipher.doFinal(encryptedBytes)
    return String(decryptedBytes, Charsets.UTF_8)
}

4. 替换values.xml的存储流程

  1. 提前在本地运行加密代码,把SHA-1、Google API密钥、Firebase配置等敏感信息加密成Base64字符串。
  2. 将加密后的字符串存储到SharedPreferences(首次启动时写入),或者打包到assets目录的加密配置文件中。
  3. 在App需要使用这些信息时,调用解密方法获取明文,比如初始化Firebase时:
// 从SharedPreferences获取加密后的Firebase配置
val encryptedFirebaseConfig = sharedPreferences.getString("firebase_config", "")
val firebaseConfig = decryptData(encryptedFirebaseConfig)
// 解析配置并初始化Firebase

关键注意事项

  • 密钥别名KEYSTORE_ALIAS建议用包名拼接固定字符串,避免和其他应用冲突。
  • 仅在App首次启动时创建Keystore密钥对,后续启动直接使用已存在的密钥。
  • 解密后的明文仅在内存中使用,不要写入任何持久化存储。
  • 对于Android 6.0(API 23)以下的设备,Keystore的加密算法支持有限,建议针对低版本设备添加兼容逻辑,或提示用户升级系统。

内容的提问来源于stack exchange,提问作者Abm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:05:27