12. Keystore密钥用途:签名、加密、解密、密钥协商、密钥派生,多用途组合策略

各位好,今天我们来聊聊 Keystore 里密钥的「用途」问题。说白了,就是一把密钥能干什么、不能干什么,以及怎么组合着用。

我刚开始接触 Android Keystore 时,觉得这东西就是个保险箱——把密钥扔进去就完事了。后来踩了不少坑才明白,密钥的用途(KeyPurpose)才是真正决定安全策略的核心。你想想看,如果一把万能钥匙既能开门又能当螺丝刀用,那安全性从何谈起?

12.1 密钥用途的本质

Keystore 里的每个密钥都有一个「用途标签」。这个标签不是摆设,它直接决定了密钥能参与哪些密码学操作。说白了,就是给密钥上了一道紧箍咒。

Android 支持以下几种基本用途:

  • 签名(SIGN):用于生成数字签名,比如 APK 签名、数据完整性校验
  • 验证(VERIFY):验证签名是否有效,通常与 SIGN 成对出现
  • 加密(ENCRYPT):用公钥或对称密钥加密数据
  • 解密(DECRYPT):解密被加密的数据
  • 密钥协商(KEY_AGREEMENT):比如 ECDH,双方交换密钥材料
  • 密钥派生(KEY_DERIVATION):从一个主密钥派生出子密钥

核心原则:每个密钥只做它该做的事。签名密钥别拿去加密,加密密钥别拿去签名。这是安全设计的基本功。

12.2 单用途 vs 多用途

我在项目中遇到过不少团队,图省事把密钥用途设成「万能」的。嗯,这里要注意——虽然技术上允许,但安全上是大忌。

举个例子:

// 不推荐:一把密钥什么都能干
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
    "my_key", 
    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY |
    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
);

// 推荐:分开用途,各司其职
KeyGenParameterSpec signKeySpec = new KeyGenParameterSpec.Builder(
    "sign_key", 
    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
).build();

KeyGenParameterSpec encryptKeySpec = new KeyGenParameterSpec.Builder(
    "encrypt_key", 
    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
).build();

为什么要分开?我跟你讲个真实案例。曾经有个金融类 App,把用户身份签名密钥和数据加密密钥混用。结果签名算法有个侧信道漏洞,攻击者通过分析签名时间,硬是把加密密钥给还原了。你说冤不冤?

12.3 密钥协商与派生

这两个用途比较特殊,我单独拿出来说。

密钥协商(KEY_AGREEMENT):主要用于 ECDH 这类协议。双方各自生成密钥对,通过交换公钥来计算出共享密钥。我在做即时通讯 SDK 时,就用这个来建立端到端加密通道。

密钥派生(KEY_DERIVATION):从一个根密钥派生出多个子密钥。比如你有一个主密钥,可以派生出加密密钥、认证密钥、会话密钥等。Android 9 以上支持 HKDF 派生。

我的习惯:密钥协商和派生通常配合使用。先协商出一个共享密钥,再用它派生出具体用途的子密钥。这样即使某个子密钥泄露,也不会影响主密钥的安全。

12.4 多用途组合策略

实际项目中,很少只用单一用途。我总结了一套组合策略,供你参考:

场景 推荐用途组合 说明
用户身份认证 SIGN + VERIFY 签名用于证明身份,验证用于检查
数据加密存储 ENCRYPT + DECRYPT 对称密钥或非对称密钥对
端到端加密 KEY_AGREEMENT + KEY_DERIVATION 先协商再派生
混合场景 SIGN + ENCRYPT(不同密钥) 签名和加密用不同密钥

我曾经犯过一个错误:在同一个密钥上同时设置了 SIGN 和 ENCRYPT。结果密钥被导出后,攻击者既能伪造签名,又能解密数据。从那以后,我坚持「一密钥一用途」的原则。

12.5 避坑指南

我曾经踩过的坑:

  • 别把密钥用途设成「ALL」——虽然方便,但安全风险极高
  • 密钥派生时,注意指定派生算法和盐值,否则每次派生结果一样
  • 密钥协商后,一定要做「密钥确认」步骤,防止中间人攻击
  • Android 版本不同,支持的用途组合有差异,记得做兼容性测试

12.6 知识体系总览

下面这张图是我自己整理的密钥用途知识体系,你可以对照着理解:

密钥用途知识体系 密钥用途 基本用途 SIGN / VERIFY ENCRYPT / DECRYPT 高级用途 KEY_AGREEMENT KEY_DERIVATION 多用途组合策略 身份认证:SIGN+VERIFY 数据加密:ENCRYPT+DECRYPT 端到端:AGREEMENT+DERIVATION 核心原则:一密钥一用途,组合使用不同密钥

12.7 实战建议

最后,给你几个实在的建议:

  • 先规划再动手:设计密钥体系时,先想清楚每个密钥的用途边界
  • 用途越少越安全:能只签名就别加加密,能只加密就别加派生
  • 版本兼容要留意:Android 6 以下不支持 KEY_AGREEMENT,8 以下不支持 KEY_DERIVATION
  • 测试要覆盖:每个用途组合都要写单元测试,确保行为符合预期

嗯,关于密钥用途就聊这么多。记住一句话:密钥用途不是功能列表,而是安全边界。边界划得越清楚,系统就越安全。


公众号:蓝海资料掘金营,微信deep3321