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

如何基于用户自定义密码在Android实现合规SCA认证?

基于应用内自定义密码的Android SCA合规实现方案

核心思路

SCA要求覆盖三类认证因子,我们需要把应用内自定义密码(所知信息)和设备安全硬件(所持物品)绑定,同时预留生物特征认证的扩展能力。核心目标是让私钥始终留在Android安全硬件中,用应用密码作为解锁私钥的前置验证因子,而非直接用密码保护密钥库。


方案一:AndroidKeyStore + 应用密码派生密钥(推荐)

AndroidKeyStore不支持直接用应用密码加载,但可以通过密码派生对称密钥(KEK,密钥加密密钥),结合硬件存储的业务密钥实现安全保护。

实现步骤

  1. 从用户6位密码派生对称密钥(KEK),用于加密密钥备份(可选)或作为认证凭证
  2. 在AndroidKeyStore生成EC私钥(完全存于硬件,不可导出)
  3. 用KEK加密私钥备份(若需跨设备同步),存储到应用私有目录

代码示例:

// 1. 从用户密码派生对称KEK
val userPassword = "123456".toCharArray()
val salt = SecureRandom.getInstanceStrong().generateSeed(16)
val pbkdfSpec = PBEKeySpec(userPassword, salt, 10000, 256)
val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val kek = keyFactory.generateSecret(pbkdfSpec).encoded

// 2. 在AndroidKeyStore生成硬件存储的EC私钥
val keyPairGenerator = KeyPairGenerator.getInstance(
    KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"
)
val keyGenSpec = KeyGenParameterSpec.Builder(
    "sca_ec_key",
    KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
)
    .setAlgorithmParameterSpec(ECGenParameterSpec("secp256r1"))
    .setUserAuthenticationRequired(false) // 不依赖系统锁屏,改用应用密码验证
    .setIsStrongBoxBacked(true) // 优先使用StrongBox硬件(若设备支持)
    .build()

keyPairGenerator.initialize(keyGenSpec)
val hardwareKeyPair = keyPairGenerator.generateKeyPair()

// 3. 可选:用KEK加密私钥备份(仅当需要跨设备同步时)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(kek, "AES"))
val encryptedPrivateKey = cipher.doFinal(hardwareKeyPair.private.encoded)
// 将encryptedPrivateKey、salt存储到应用私有目录

使用流程

  • 用户输入6位密码,重新派生KEK
  • 验证密码合法性(对比本地存储的加盐哈希值)
  • 直接使用AndroidKeyStore中的私钥完成签名/认证操作
  • 可选:替换密码验证为BiometricPrompt,满足生物特征因子要求

方案二:AndroidKeyStore + 密码绑定的密钥使用限制

生成硬件密钥时,设置每次使用都需前置验证,将应用密码作为验证环节,确保只有密码正确才能调用密钥。

代码示例:

// 生成绑定认证要求的硬件密钥
val keyPairGenerator = KeyPairGenerator.getInstance(
    KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"
)
val keyGenSpec = KeyGenParameterSpec.Builder(
    "sca_ec_key",
    KeyProperties.PURPOSE_SIGN
)
    .setAlgorithmParameterSpec(ECGenParameterSpec("secp256r1"))
    .setUserAuthenticationRequired(true)
    .setUserAuthenticationValidityDurationSeconds(0) // 每次使用都需认证
    .build()

keyPairGenerator.initialize(keyGenSpec)
val hardwareKeyPair = keyPairGenerator.generateKeyPair()

// 使用密钥前的密码验证逻辑
fun signWithKey(data: ByteArray, inputPassword: String): ByteArray {
    // 验证输入密码与存储的加盐哈希是否匹配
    if (!validatePassword(inputPassword)) {
        throw IllegalArgumentException("Invalid password")
    }
    // 验证通过后调用硬件密钥签名
    val signature = Signature.getInstance("SHA256withECDSA")
    signature.initSign(hardwareKeyPair.private)
    signature.update(data)
    return signature.sign()
}

// 密码存储与验证:仅存加盐哈希,不存明文
private fun validatePassword(input: String): Boolean {
    val storedHash = getStoredPasswordHash() // 从SharedPreferences获取
    val storedSalt = getStoredSalt()
    val inputHash = hashPassword(input.toCharArray(), storedSalt)
    return MessageDigest.isEqual(storedHash, inputHash)
}

此方案中私钥完全隔离在硬件中,无法导出,同时应用密码作为前置验证,可无缝替换为生物特征认证,完全覆盖SCA三类因子。


方案三:自定义密钥库的合规优化(不推荐)

若必须使用自定义密钥库,需通过以下方式提升安全性以满足SCA要求:

  • 用AndroidKeyStore生成的对称密钥加密自定义密钥库文件
  • 禁止私钥导出,仅在内存中完成加密/签名操作
  • 增加设备安全检查(如检测root状态、验证系统完整性)

但该方案私钥不在硬件中存储,安全性弱于前两种,不符合SCA对"所持物品"的强安全要求,仅作为极端场景下的备选。


SCA合规性确认

确保实现覆盖三类认证因子:

  • 所知信息:应用内6位密码,独立于系统锁屏的验证逻辑
  • 所持物品:密钥存储在AndroidKeyStore(StrongBox/TEE),无法被导出到设备外部
  • 生物特征:可将密码验证环节替换为BiometricPrompt,支持指纹/面容识别

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:15:43