3388 字
17 分钟
从 mTLS 到冷钱包:私钥安全的全链路分析与硬件信任边界

一次 mTLS 认证被攻破的故事#

曾经我开发了一个客户端接口,为了防止被爬虫,加上了mTLS认证:

Mutual authentication
en.wikipedia.org
后台客户端(二进制)客户端(Java)后台客户端(二进制)客户端(Java)请求证书客户端RPC请求[Server HTTPS] 请求证书验证Header入参及MD5(requestBody)生成客户端私钥,并签发客户端证书返回AES(客户端私钥, key1)、AES(客户端证书, key1)透传服务器返回的内容给native返回客户端本地保存的内容AES(客户端私钥, key2)、AES(客户端证书, key2)客户端保存本地加密的私钥和证书获取客户端私钥和证书返回客户端私钥与证书用客户端私钥、证书创建一个新的AndroidKeyStore让RPC请求使用新的KeyStore[mTLS]请求接口验证客户端证书合法性执行接口内部逻辑返回接口结果

我本来以为这个认证流程已经相对安全了,直到最近这个接口的访问量触发了告警,并且是同一个用户的行为,说明接口又被非预期访问了。问题出在哪呢?

问题分析#

好在,这名用户非常勇敢,把破解流程放在了Github上:

  1. 攻陷手机设备,使用Frida接管
  2. 使用Frida去hook Android KeyStore有关接口。
  3. 在客户端发送mTLS RPC请求前,需要从Android KeyStore获取私钥去签名。而因为Android KeyStore的接口都已被接管,输入和输出的内容可被用户截获。
  4. 结果:客户端证书私钥泄漏。

所以问题就在,私钥就不应该出现在用户态的内存上,甚至最好都不要出现在内核态里。而mTLS的加密通信的核心是客户端私钥签名,又必须用到客户端私钥。有没有什么办法,让这个签名的过程与用户态隔离开来呢?

有的兄弟,有的。

硬件密钥#

设备上有一个单独的元器件,负责私钥存储,对外提供签名、加密功能。一般而言,除非硬件设备因存在漏洞被攻陷,硬件私钥无法被提取。

对于iOS来说,整个元器件被称为

The Secure Enclave
Secure Enclave is a dedicated secure subsystem in the latest versions of iPad, iPhone, Mac, Apple TV, Apple Vision Pro, Apple Watch, and HomePod.
support.apple.com
The Secure Enclave
Secure Enclave is a dedicated secure subsystem in the latest versions of iPad, iPhone, Mac, Apple TV, Apple Vision Pro, Apple Watch, and HomePod.
support.apple.com

Security Enclave(SEP)是单独的元器件,有自己的SoC、内存,甚至有自己的操作系统。iOS内核跟SEP只能通过Mailbox通信,iOS内核无法直接操作SEP。并且在与内核的通信过程,还有很多验证方法防止各种攻击,确保SEP内存空间是可信的。

对于安卓,现代的安卓设备,一般会有TEE或StrongBox。实现原理和iOS的SEP可能不相同,但大体原理是一致的。

שיטות מומלצות לאבטחת חומרה  |  Android Open Source Project
source.android.com

那为什么需要硬件密钥呢?硬件密钥是一切生物识别支付的基石。以FaceID支付为例,简单来说,以下是支付过程:

  1. 后台下发支付信息与Challenge(随机值)。
  2. 客户端调用系统FaceID API,传入Challenge。
  3. 系统开启FaceID硬件,对用户进行生物识别。
  4. 如果识别成功,FaceID API返回Challenge对应的硬件公钥证书链签名
  5. 客户端将Challenge证书签名传给后台。
  6. 后台检测Challenge的签名、证书链是否有效。如果有效,则支付成功。

上面过程的硬件公钥证书链签名,是由SEP完成的。如果没有SEP,用户设备被攻陷后,攻击者很容易就拿到私钥去伪造签名。正是因为硬件密钥足够安全,生物识别支付技术才在全球流行起来。不然,我们现在要手动输入密码才能完成支付。

修改流程#

既然硬件密钥这么安全,那我们mTLS证书的私钥能不能存在硬件密钥里呢?给攻击者一点颜色瞧瞧!

Android#

后台客户端(KeyStore)客户端(Java)后台客户端(KeyStore)客户端(Java)请求Challenge申请证书[Server HTTPS] 请求Challenge验证Header入参及MD5(requestBody)生成Challenge(C1),扔redis里,时间窗口定5分钟返回C1用C1生成硬件密钥对pubK, priK返回密钥对ID,Key1=handle(pubK, priK)获取Key1硬件证明返回硬件证明Proof1Proof是GoogleRootCertificate对PubK1签发的证书链,里面包含C1准备生成证书申请(CSR1),里面包含pubK使用Key1,请求用priK对CSR1签名返回签名后的证书(CSR2)[Server HTTPS] 将Proof、CSR2发送给后台验证Header入参及MD5(requestBody)根据公开的GoogleRootCertificate判断Proof是否合法从Proof里取出Challenge,判定是否有效Proof里的公钥跟CSR2里的公钥是否一致签发客户端证书CCRT返回CCRT

缺陷:Android有的硬件证书链已泄漏,后台需及时更新证书列表。

iOS#

后台客户端(DCAppAttestService)客户端(Security Enclave)客户端(Swift)后台客户端(DCAppAttestService)客户端(Security Enclave)客户端(Swift)请求Challenge申请证书[Server HTTPS] 请求Challenge验证Header入参及MD5(requestBody)生成Challenge(C1),扔redis里,时间窗口定5分钟返回C1生成硬件私钥返回硬件私钥priK的句柄priKID传入priKID,获取硬件公钥pubK返回硬件公钥pubK传入data=(C1, pubK),获取设备认证返回设备认证签名结果D=((C1, pubK), sign(C1, pubK))传入priKID,获取证书请求返回证书请求CSR[Server HTTPS]发送D、CSR至后台验证Header入参及MD5(requestBody)验证设备认证签名D判断D里的pubK与CSR里的公钥是否一致签发客户端证书CCRT返回CCRT

缺陷:SEP并不提供设备认证签名(但是,FaceID API提供),后台无法确保客户端传入的公钥是SE生成的。为了弥补这个缺陷,上述流程用到了AppAttestService,因为AppAttestService提供设备认证签名。

回顾区块链#

简介 - 区块链教程 - 廖雪峰的官方网站
廖雪峰的官方网站 (liaoxuefeng.com) 研究互联网产品和技术,提供原创中文精品教程
liaoxuefeng.com

假如,Bob要给Alice转账1BTC,那么需要什么呢?

首先,Bob需要生成钱包,并且需要知道Alice的地址

数学基础#

Elliptic curve
en.wikipedia.org
Elliptic-curve cryptography
en.wikipedia.org

已知,椭圆曲线方程:

y2=x3+ax+by^2=x^3+ax+b

加法#

已知,椭圆曲线是一条连续光滑的曲线。现在有三个点P、Q、R,P、Q在曲线上,有P+Q=R,则定义以下规则:

  1. 若P ≠ Q,则画一条直线连接P、Q,交曲线于第三点R‘,取R’关于X轴轴对称的点,该点定义为R。
  2. 若P = Q,则过P点画一条椭圆曲线的切线,交曲线于第三点R’。R‘关于X轴轴对称的点定义为R。

应用在密码学里#

x, y的值域是实数域,无法应用在计算机上。为了转换到有限域上,方程改为:

y2=x3+ax+b(modp)y^2=x^3+ax+b \pmod p

曲线上找一点G,定义为基点

因为曲线后面带modp,则必有一点n,使得nG = 0。该点称为

公钥与私钥#

取一正整数d,该整数为私钥

计算Q=dG,Q点为公钥

椭圆曲线的加法必须使用累加法计算。已知d和G,计算公钥时,可以使用倍点加法计算节约计算时间。而只知道Q和G,必须要穷举才能知道私钥d。

签名#

已知消息哈希z:

  1. 取随机正整数k,1≤k<n

  2. 计算R=kG

  3. 计算R=kG

  4. 取r=Rx mod n

  5. 计算签名:

s=k1(z+rd)modns=k^{-1}(z+r⋅d)\mod n

  1. 签名为r、s、v。恢复的公钥包含多个可能值,v指定某个公钥的值。

怎么从签名获得公钥Q?

s=k1(z+rd)modn =>sk=z+rd <=>skG=zG+rQ <=>Q=r1(sRzG)s=k^{-1}(z+r⋅d)\mod n\ => sk=z+r·d\ <=> skG=zG+rQ\ <=> Q=r^{-1}(sR-zG)

r=Rxmodn =>xr=rxr=r+nr=R_{x}\mod n\ => x_{r}=r \text{或} x_{r}=r+n

带入恢复公式验算,公钥的点必在曲线上,得到准确的xr

而y有两个解(参考椭圆曲线方程)。后面加了modp,所以有y1+y2=p。因为p是奇质数,会根据v的奇偶性,确定取y1还是y2。

验证签名:

  1. 计算:

w=s1modn u1=zwmodn u2=rwmodnw=s^{-1}\mod n\ u1=z⋅w\mod n\ u2=r⋅w\mod n

  1. 取曲线点:

(X,Y)=u1G+u2Q(X,Y)=u1G+u2Q

v=Xmodnv=X\mod n

  1. 比较

v==rv == r

为什么成立?

s=k1(z+rd)modn =>ks=z+rd <=>k=s1(z+rd)s=k^{-1}(z+r⋅d)\mod n\ => ks=z+r·d \ <=> k=s^{-1}(z+rd)

u1G+u2Q=(zs1)G+(rs1)(dG)=(kG)u1G+u2Q=(zs^{-1})G+(rs^{-1})(dG)=(kG)

钱包与地址#

Cryptocurrency wallet
en.wikipedia.org

钱包:由ECDSA(椭圆曲线数字签名算法)生成的私钥集合,每一条链对应一把私钥。在链上交易时,需要用你的私钥签名交易信息。

Hierarchal Deterministic (HD) Wallet:钱包里只存一个根私钥,由此根私钥可以派生出其它私钥。助记词种子的一种表现形式,种子可以生成根私钥,并派生其它私钥。

Seed phrase - Bitcoin Wiki
en.bitcoin.it

地址:公钥的哈希再哈希。

在一个链里,椭圆曲线的参数(p、n、G)是公开的。例如在比特币主链,用的是secp256k1 曲线,其参数为:

y2x3+7modpy^2≡x^3+7\mod p

p=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
Gx=79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Gy=483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
n=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

区块与区块链#

区块链上有若干区块,因为每个区块都记录着上一个区块的哈希,每个区块就像被系在一起,因此称为“区块链”。

区块记录交易信息,而不同的链有不同的交易信息记录方式。比特币用的是UTXO模型,而以太坊用的是账户余额模型。

Unspent transaction output
en.wikipedia.org

比如,Bob之前收到Tom的1BTC,Mary的1BTC,交易记录分别记录不同的区块,那么现在Bob给Alice转账1BTC,区块的交易信息记录:

  • 输入:Tom→Bob的区块哈希、Mary→Bob的区块哈希、Tom私钥对该交易信息的签名
  • 输出:Alice的地址、交易金额

记账#

区块链是去中心化的,换言而之没有一个固定的记账对象,记账对象是一个自由加入的计算机集群

在Bob与Alice的交易发生时,会发生:

  1. 该交易被发送至未处理池里(内存池)。
  2. 由链的算法在集群里选择一台机器进行记账。在比特币主链,用的是工作量证明法。
  3. 被选中的机器构造新的区块,并广播给其它所有机器,获得奖励(挖矿)。
  4. 其它机器收到新的区块,验证无误后将这个区块加入到自己的本地区块链里。
  5. 交易完成。

安全性#

区块链本身安全吗?安全。

  1. 单个区块需要验证发送者签名才会被加到链上,而签名在公私钥理论上就是安全的,无法被伪造。
  2. 每个区块都记录着上一个区块的哈希值。要想改变链上某个区块的信息,就必须要改变这个区块后面所有的区块。

但是,安全分为两种:密码学安全和结构安全,区块链只能保证前者。如果私钥泄漏了,那么就算区块链本身再安全也无能为力。而大部分钱包都是在软件层面生成的。这也就意味着,私钥会出现在用户态的内存环境里。按照第1节讲的,在设备被攻陷、或操作系统出现漏洞时(比如某红色三字APP用的提权漏洞)都会导致私钥被直接提取。

那能否使用硬件密钥保存钱包私钥呢?很遗憾,目前并不支持。以Security Enclave为例,并不支持硬件私钥用比特币主链采用的secp256k1曲线进行签名。Android 的 StrongBox 也不支持,仅有三星支持该曲线。

What makes Samsung Blockchain Keystore Unique? | Samsung Developer
The world runs on you.
developer.samsung.com

既然OS的密钥保护存在天然缺陷,那么安全的答案只能回到硬件层面:冷钱包。

回到冷钱包#

既然Security Enclave不支持特定secp256k1曲线签名,那能不能开发一个外部硬件来解决?这个硬件在内部生成密钥,并只对外界提供secp256k1曲线签名功能,通过类似蓝牙、NFC、二维码或者USB等方式把签名发送给设备上完成交易。

没错,这就是冷钱包。冷钱包属于硬件私钥签名设备。除了冷钱包,U盾、FIFO2设备都属于这类。

但是,冷钱包就一定能够确保私钥安全了?U盾之所以安全,是因为银行既是U盾的硬件厂商,又是交易场所,本身就有保证交易安全的义务,在U盾上开后门没意义。而对于FIFO2,它仅仅是一个认证设备,并不理解用户具体的认证场所,所以硬件厂商不知道私钥的具体含义,在上面做手脚也没意义。

但是对于冷钱包而言,冷钱包厂商知道硬件里的私钥是钱包。并且它也没义务去确保交易的安全,因为这是区块链本身该做的。所以冷钱包里是否存在厂商添加的后门,全凭厂商道德自觉。更可怕的是,一般的冷钱包只能连接硬件厂商提供的APP,那么冷钱包有没有可能把私钥传输到APP里,APP再把私钥传到后台呢?用户不得而知。

从 mTLS 到冷钱包:私钥安全的全链路分析与硬件信任边界
https://blog.nowcent.cn/posts/dev/从mtls到冷钱包私钥安全的全链路分析与硬件信任/
作者
orangeboy
发布于
2025-10-30
许可协议
CC BY-NC-SA 4.0