Android本地令牌计数器t_count的TEE安全管理实现方案咨询
Android离线场景下安全递减访问令牌计数器实现方案
关于TEE-backed密钥存储的能力边界
直接给结论:Android系统提供的TEE-backed Keystore(含StrongBox安全芯片存储)不支持直接存储任意自定义计数器。这个组件的设计目标是安全保管密钥材料、在安全环境内执行密码学运算,没有对外提供通用安全变量的读写、单调修改接口,没法直接把t_count值扔进去靠系统保证只能递减。
离线场景下的最优防护方案
因为你没法依赖在线服务端存计数器,方案分两个适配等级,根据你的安全要求选就行:
- 通用兼容方案(适配99%的Android 8.0+设备,不需要厂商合作,普通应用就能实现):用Keystore托管的不可导出签名密钥,构造计数器状态的签名链,把计数器的修改权限和TEE内的私钥绑定,哪怕用户拿到root权限也没法非法篡改计数器值、伪造合法状态。
- 最高安全等级方案(需要对接设备厂商定制):在设备TEE环境内开发专属可信应用(TA),把计数器值直接存在TA的专属安全存储分区,所有计数器读取、递减、访问凭证签名的逻辑全部在TA内部执行,普通Android系统侧(包括root后的系统进程)完全没有权限直接操作计数器,从硬件层面堵死篡改路径。
绝大多数场景下通用兼容方案足够满足安全要求,下面具体说实现逻辑。
计数器更新、校验的完整可落地流程
这个需求完全可实现,不需要特殊系统权限,具体流程和你原本的业务逻辑完全对齐:
- 初始化阶段
- 首次启动应用时,在Android Keystore中生成一套非对称签名密钥(优先选ECDSA算法,性能更好),配置密钥为不可导出、私钥永远不离开安全环境,优先开启
setIsStrongBoxBacked(true)走独立安全芯片,设备不支持再退回普通TEE-backed模式。 - 把生成的公钥提前预置到所有需要对接的离线服务器中,服务器只信任用这个公钥验签通过的访问凭证。
- 设置初始值
t_count = X,生成16字节随机数作为初始化盐值init_nonce,构造初始状态串t_count:X:nonce:[init_nonce],用TEE内的私钥对这个状态串签名,得到state_sig,把current_t = X、init_nonce、state_sig三个值存在应用私有存储目录。
- 首次启动应用时,在Android Keystore中生成一套非对称签名密钥(优先选ECDSA算法,性能更好),配置密钥为不可导出、私钥永远不离开安全环境,优先开启
- 发起服务器访问阶段
- 先做本地状态校验:从初始状态开始,沿着历史
state_sig递推,逐次验证每一次计数器变更的签名是否合法,如果签名校验不通过,直接判定计数器被篡改,锁定所有访问功能。 - 校验通过后读取
current_t,如果值为0直接拦截请求,不执行后续签名操作。 - 如果
current_t > 0,构造单令牌消耗凭证,凭证必须包含「当前请求随机nonce + 目标服务器唯一标识 + 时间戳 + current_t值」,调用TEE内的私钥对凭证签名,把签名后的凭证通过本地连接发给对应服务器。
- 先做本地状态校验:从初始状态开始,沿着历史
- 计数器递减阶段
- 收到服务器返回的「凭证有效」响应后,计算新的计数器值
new_t = current_t - 1,构造新的状态串t_count:[new_t]:prev_sig:[上一步的state_sig],调用TEE私钥对新状态串签名得到新的new_state_sig。 - 本地先验签确认新状态签名合法,再把存储的
current_t更新为new_t,替换旧的state_sig为new_state_sig,完成一次递减。
- 收到服务器返回的「凭证有效」响应后,计算新的计数器值
关键加固点(防root/防逆向绕过)
通用方案的唯一攻击面是攻击者通过Hook、逆向修改应用代码,跳过本地t_count == 0的判断直接调用签名接口,两个手段就能堵死这个路径:
- 本地代码加固:对应用做高强度混淆、控制流平坦化,把计数器校验逻辑拆成零散片段穿插在业务代码里,和签名调用逻辑深度绑定,大幅提升逆向和Hook的成本。
- 服务端离线校验:所有访问凭证必须把当前
t_count值作为签名字段,离线服务器收到凭证后,除了验签,还要本地维护对应设备的历史计数器值,要求收到的计数器值严格单调递减,一旦出现重复值、递增值直接拒绝访问。哪怕攻击者本地绕开了校验,也没法生成能通过服务器校验的超额访问凭证。
踩坑提示:不要把计数器值明文存在SharedPreferences或者普通文件里,也不要用Android的加密存储(EncryptedSharedPreferences)存计数器——这类存储的密钥虽然在Keystore里,但数据本身存在普通存储分区,root用户直接就能改明文值,没有签名链校验的话根本发现不了篡改。
内容的提问来源于stack exchange,提问作者Gabriel Rebello
相关产品推荐
相关产品推荐

