8、Cipher 模式详解:加密模式、解密模式、签名模式、验证模式
聊到 Android 生物识别,绕不开一个核心概念——Cipher(密码器)。
很多初学者一上来就被 ENCRYPT_MODE、DECRYPT_MODE 这些常量搞晕了。其实没那么复杂。我个人的理解是:Cipher 就像一把瑞士军刀,不同的模式就是不同的刀头。你用哪个模式,决定了这把刀怎么干活。
四种模式,一张图说清楚
先别急着看代码。我画了一张图,帮你快速建立整体认知。这张图我反复改了三版,就是为了让你一眼看明白四种模式分别用在哪儿。
看到这张图,你应该有个大概印象了。左边两个模式(加密、签名)是「写操作」,需要用户按指纹授权。右边两个(解密、验证)是「读操作」,一般不需要重复验证。
ENCRYPT_MODE:加密模式
加密模式,说白了就是把你的数据变成一堆看不懂的乱码。我刚开始做生物识别时,以为加密就是随便调个 API 就行。后来踩了坑才明白——加密模式的核心在于「保护密钥」。
在 Android 生物识别中,加密模式通常这样用:
// 初始化 Cipher 为加密模式
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val secretKey = keyStore.getKey("my_key", null) as SecretKey
// 关键:用加密模式初始化
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
// 加密数据
val plainText = "这是需要保护的敏感数据"
val encryptedData = cipher.doFinal(plainText.toByteArray())
这里有个细节要注意:cipher.init() 调用之后,Cipher 对象就「锁定」了。你不能换个模式继续用,必须重新初始化。
核心要点:加密模式生成的密文,只有用对应的密钥和解密模式才能还原。密钥如果存在 AndroidKeyStore 里,那只有用户通过生物识别验证后才能使用。
DECRYPT_MODE:解密模式
解密模式是加密的逆操作。把乱码还原成可读的数据。
我记得有一次,同事问我:「为什么解密的时候不需要用户再按一次指纹?」
嗯,这个问题问得好。原因其实很简单:解密操作本身不暴露密钥。密钥在 KeyStore 里,解密过程在安全环境中完成,App 拿到的只是解密后的结果。所以系统认为——既然你之前已经验证过了,这次就不必再按了。
// 解密模式初始化
cipher.init(Cipher.DECRYPT_MODE, secretKey)
// 解密数据
val decryptedData = cipher.doFinal(encryptedData)
val originalText = String(decryptedData)
避坑指南:我曾经犯过一个错误——加密和解密用了不同的 IV(初始化向量)。结果解密出来的数据全是乱码。记住:加密时生成的 IV 必须保存下来,解密时要用同一个 IV。
SIGN_MODE:签名模式
签名模式,说白了就是「盖章」。你用自己的私钥给一段数据盖个章,别人拿到后可以验证这个章是不是你盖的。
签名模式和加密模式最大的区别是什么?
加密是为了保密,签名是为了防篡改。你想想看,如果只是加密,中间人虽然看不懂内容,但他可以把密文替换掉。而签名可以保证——数据一旦被改,签名就失效了。
// 签名模式初始化
val signature = Signature.getInstance("SHA256withRSA")
signature.initSign(privateKey)
// 对数据进行签名
signature.update(dataToSign)
val signedData = signature.sign()
在生物识别场景下,签名模式常用于「证明这个操作是本人发起的」。比如支付确认、授权操作等。
注意:签名模式需要用户验证生物特征。因为签名操作会暴露私钥的使用能力,系统必须确保「是本人」。
VERIFY_MODE:验证模式
验证模式是签名的配套操作。别人拿着你的公钥,来验证签名是不是你生成的。
这里有个有意思的点:验证模式不需要生物识别。为什么?因为验证用的是公钥,公钥本来就是公开的。你不需要按指纹来证明「我有权使用公钥」。
// 验证模式初始化
val signature = Signature.getInstance("SHA256withRSA")
signature.initVerify(publicKey)
// 验证签名
signature.update(originalData)
val isValid = signature.verify(signedData)
我个人习惯把验证逻辑放在服务端。客户端只负责签名,服务端拿公钥验证。这样即使客户端被逆向,攻击者也伪造不了合法的签名。
四种模式对比一览
| 模式 | 操作类型 | 是否需要生物识别 | 典型场景 |
|---|---|---|---|
| ENCRYPT_MODE | 写操作 | 是 | 加密用户敏感数据 |
| DECRYPT_MODE | 读操作 | 否(通常) | 解密已加密的数据 |
| SIGN_MODE | 写操作 | 是 | 交易签名、身份认证 |
| VERIFY_MODE | 读操作 | 否 | 验证签名真伪 |
实际项目中的选择建议
说了这么多,到底什么时候用哪个?我根据实际项目经验,给你几个参考:
- 存储密码、Token 等敏感信息 → 用
ENCRYPT_MODE+DECRYPT_MODE - 支付确认、授权操作 → 用
SIGN_MODE+VERIFY_MODE - 既想保密又想防篡改 → 先加密,再对密文签名
- 只需要验证身份 → 只用签名模式就够了,不用加密
一句话总结:加密模式和解密模式是一对,用于保护数据机密性;签名模式和验证模式是一对,用于保证数据完整性和身份真实性。别搞混了。
好了,四种模式就讲到这里。你只要记住——左边两个要按指纹,右边两个不用。这个规律能帮你避免 90% 的初始化错误。