一文可能弄懂基于标识密码算法
本系列文章目录
1.1 传统密码体系的重资产困境
当在网上下载一个银行App时,手机是如何确认这个App就是银行官方发布,而不是钓鱼软件的呢?
在当前的互联网安全体系中,这个信任问题主要依赖公钥基础设施(PKI, Public Key Infrastructure) 来解决。实际上,PKI是一个数字世界的身份证颁发机构,它通过给每个用户和服务器颁发数字证书,来证明身份的真实性。
然而,这个看似完善的体系,在物联网、车联网等万物互联的场景中,显得比较臃肿。首先,维护PKI体系需要复杂的证书颁发、验证、撤销和更新流程,这意味着巨大的计算、存储和带宽开销;其次,PKI体系的安全性高度依赖于少数几个权威的证书颁发机构,一旦某个CA机构被攻击或出现管理失误,整个信任链条都可能断裂;最后,在加密通信前,必须先拿到对方的证书来验证其公钥。但在一个临时组网或设备初次交互的场景下,获取并验证一个陌生设备的证书,流程复杂且缓慢。
为了从根本上消除对数字证书的依赖,一种全新的密码学思想应运而生,这就是本节要介绍的基于身份的密码体系(IBC, Identity-Based Cryptography)。
说来也巧,最早提出这个想法的,正是RSA算法发明人之一的Adi Shamir。那是在1984年,RSA算法诞生才不到十年,但基于证书的公钥体系在应用中已经暴露出不少问题。Shamir的思路很直接:为什么公钥非得是证书里那一长串数字?直接用用户的电子邮箱、手机号这类身份标识来当公钥不行吗?这样加密方就不需要事先去获取和验证对方的证书了。
他在那篇开创性的论文里,提出了基于身份密码体系的思想,并且成功设计了基于身份的签名(IBS) 方案,也就是用身份信息来验证签名。不过,如何实现基于身份的加密(IBE),当时还没找到方法,这个问题在密码学界悬而未决近20年。
直到2001年,才有了突破口。这一年,美国教授Dan Boneh和Matthew K. Franklin利用椭圆曲线上一种名为双线性配对(Bilinear Pairing) 的数学工具,设计出了第一个实用且安全的基于身份加密方案(Boneh-Franklin IBE)。几乎是同时,日本KDDI研究所的研究人员Sakai、Ohgishi和Kasahara也独立提出了另一个基于配对的IBE方案,另外,英国数学家Clifford Cocks也给出了一个基于二次剩余问题的不同实现方案。
这些突破让基于身份的密码学从一个构想变成了现实,也开启了后续大量的研究和标准化工作。
1.2 IBC椭圆曲线特殊性
在传统PKI体系里,用户的公钥是一串没有意义的数字,类似于银行卡号,CA颁发公钥证书和某个用户绑定起来,也类似于银行发卡时,把账户名和卡号绑定起来,给别人转账时,需要同时输入对方的姓名和卡号。
IBC的思路完全不同:它不再需要卡号,转账时只需要输入对方的姓名,系统会自动把姓名映射成唯一的账户编码,完成整个交易。
这里的关键在于映射二字。为什么传统系统必须依赖随机卡号,而IBC可以直接使用姓名?原因就在本节要介绍的一种特殊椭圆曲线里,这种特殊曲线能把“刘备”这类有意义的字符串,直接变成一个可用的密码学公钥。
1.2.1 字符串映射
要让“刘备”变成一个公钥,第一步需要一个确定性的规则:任何人用“刘备”作为输入,都能得到同一个输出,而且这个输出必须是椭圆曲线上的一个点。这个规则就是哈希到曲线(Hash-to-Curve),用公式表示:
Q I D = H ( I D ) Q_{ID}=H(ID) QID=H(ID)
其中ID是“刘备”这类有意义的字符串,H是一个特殊的哈希函数,它的输出不是普通的二进制串,而是椭圆曲线上的一个点Q_{ID}。这个点就是刘备的公钥,任何人都能算出来,不需要CA发证书,不需要查询任何目录。
公钥有了,还缺一把配对的私钥。注意到,这个过程和普通椭圆曲线算法有差异:普通椭圆曲线算法是先生成私钥,再点乘得到公钥;而这里,是先得到公钥,再去找配对的私钥。
生成私钥由称为密钥生成中心(KGC) 的角色完成。KGC有一个系统主私钥s,以及一个系统主公钥P_{pub}=s·P(P是椭圆曲线上的一个公开基点)。
当用户“刘备”向KGC注册时,KGC做如下计算:
d I D = s ⋅ Q I D d_{ID}=s·Q_{ID} dID=s⋅QID
也就是说,KGC用自己的主私钥s去乘以刘备的公钥点Q_{ID},得到一个新的点d_{ID}。这个点就是刘备的私钥。
这个过程的逻辑和CA用私钥签名公钥完全一样,都是用机构的私钥给公钥“盖章”。唯一的区别是:CA的盖章结果是公开的证书,而KGC的盖章结果是保密的私钥。
私钥d_{ID}通过安全渠道交给刘备。此后,刘备就可以用这个私钥解密别人发给他的消息了。
现在问题来了:别人给刘备发消息时,用他的公钥Q_{ID}加密。但别人怎么确信解密这个密文需要的私钥,确实是和这个公钥匹配的?这时候需要一个工具,能在不暴露私钥的前提下,证明这个私钥确实是由系统主私钥s签名生成的,这个工具就是双线性配对。
双线性配对是一个特殊的数学映射e,它能把椭圆曲线上的两个点,映射成一个数,用数学公式表示如下:
e ( a ⋅ P , b ⋅ Q ) = e ( P , Q ) ( a ⋅ b ) e(a·P, b·Q) = e(P, Q)^{(a·b)} e(a⋅P,b⋅Q)=e(P,Q)(a⋅b)
我们可以用一个简单的类比来理解:想象两种公开的基础原料P和Q,以及两个秘密的配比a和b。
P和Q:是椭圆曲线上公开的基点,所有人都知道。a和b:是两种秘密的配比,分别由不同的人或系统掌握。
双线性配对做的事情,就是把两种“原料+配比”的组合,映射成一个公开可比的数值。而最关键的性质是:两种原料各自按某种配比混合后产生的最终成品,与两种原料先混合、再按总配比(a×b)加工出来的成品,是完全相同的。
也就是说,配比a和b的关系,最终会体现在结果的指数上,即a和b在椭圆曲线上是加了多少次,在配对结果里变成乘了多少次方。
这样一来,验证者可以通过比对右边的公开计算结果,来确认左边的秘密操作是否合法,而无需知道a和b本身。
对应到IBC中
把这个比喻套到IBC场景里:
Q_{ID}和P是公开的基础原料。s(系统主私钥)是一个秘密配比,只有KGC知道;P_{pub}=s·P是公开的系统主公钥;d_{ID}=s·Q_{ID}是刘备的私钥,是一个“秘密成品”。
现在,验证者要做的是:
- 刘备用自己的私钥
d_{ID}和公开原料P配对:
e ( d I D , P ) = e ( s ⋅ Q I D , P ) = e ( Q I D , P ) s e(d_{ID}, P) = e(s·Q_{ID}, P) = e(Q_{ID}, P)^s e(dID,P)=e(s⋅QID,P)=e(QID,P)s
- 验证者用公开的
Q_{ID}和P_{pub}配对:
e ( Q I D , P p u b ) = e ( Q I D , s ⋅ P ) = e ( Q I D , P ) s e(Q_{ID}, P_{pub}) = e(Q_{ID}, s·P) = e(Q_{ID}, P)^s e(QID,Ppub)=e(QID,s⋅P)=e(QID,P)s
两边的配对结果完全相等。这意味着:刘备手里的d_{ID},确实是用那个只有KGC知道的s算出来的。验证者虽然没有接触到s或d_{ID},但通过比对配对结果,确认了刘备确实持有合法的私钥。
这里的安全性又依赖于椭圆曲线的双线性配对单向性,也就是说,无法通过e(P, Q)^(a·b)来反推出a和b。当然,这不是声明了不能反推就不能反推,而是依赖于数学上的保证。数学上提供了两个层面的安全基础:
- 第一层:椭圆曲线离散对数问题(ECDLP)。在椭圆曲线群里,正向运算(数乘)很容易,反向运算(求离散对数)在数学上被公认为计算不可行,因此即使攻击者知道
P和a·P,也无法推算出a; - 第二层:配对运算本身不削弱这个安全性。配对把椭圆曲线上的数乘关系“翻译”成扩域上的指数关系,但攻击者在扩域里求解离散对数同样困难(FDLP)。也就是说,从
e(P, Q)^(a·b)反推a·b,和在扩域里求离散对数是等价的,同样计算不可行。
所以,椭圆曲线配对的单向性实际上是两个离散对数困难问题的叠加:一方面是椭圆曲线上的ECDLP保护了a和b不被分离提取,另一方面扩域上的FDLP保护了乘积a·b不被整体提取。两者结合,可让验证者能确认配对关系成立的同时又无法反推出私钥。
当然,我们更想知道,为什么椭圆曲线可以做这样的映射。
我们先要介绍有理函数的概念。
初中我们学过有理数(Rational Number),核心定义是能表示为p/q形式的数,其中p和q都是整数。对应的,有理函数(Rational Function) 是可以表示为两个多项式之比的形式的函数,即:
R ( x ) = P ( x ) Q ( x ) R(x) = \frac{P(x)}{Q(x)} R(x)=Q(x)P(x)
其中P(x)和Q(x)都是多项式,且Q(x)不恒等于零。
有理函数的两个关键特征,分别是零点和极点,两个点的定义如下:
- 零点:分子等于0的点。比如
(x²+1)(x-2),当分子(x²+1)=0时函数值为0。 - 极点:分母等于0的点。比如分母
(x-2)=0时,函数值趋向无穷大。
一个有理函数,完全由它的零点和极点决定:知道所有零点和极点的位置和重数,就等于知道了这个函数本身。换句话说,点的位置,决定了它对应的有理函数的零点和极点的位置。
为什么提到有理函数呢?因为曲线上的点,不管是普通的曲线还是椭圆曲线上的点,不仅仅是一个坐标(x, y),每个点都可以对应一个有理函数。
例如圆的方程是x²+y²=r²,曲线上的点P=(x_P, y_P)可以构造一个有理函数:
f ( x , y ) = x − x P y − y P f(x, y) = \frac{x - x_P}{y - y_P} f(x,y)=y−yPx−xP
- 当点
(x, y)接近P时,分子和分母都趋近于0,这个比值趋近于某个有限值; - 当点
(x, y)在P的对称点(x_P, -y_P)时,分母为0,函数趋向无穷。
法国数学家安德烈·韦伊(André Weil) 在20世纪40年代,为了攻克有限域上曲线的黎曼猜想,开始系统性地研究椭圆曲线这类代数曲线,包括深入理解曲线上点的结构。在研究过程中,韦伊发现了一个关键事实:椭圆曲线上的点,天然具有两种不同的身份。
- 第一种身份:它是一个几何点,可以在曲线上做加法运算(点加、数乘)。
- 第二种身份:点可以被翻译成一个有理函数,这个函数有自己的零点和极点,这些零点和极点的位置,和点本身有严格的数学对应关系。
有了这个双重身份,韦伊做了如下工作:
他取两个点P和Q,分别构造它们对应的有理函数f_P和f_Q。然后,他把f_P在点Q处的值和f_Q在点P处的值做了一个比较,具体来说,是看这两个值的某种组合(比如比值)。这个比较的结果,是一个数:
e ( P , Q ) = f Q ( P ) f P ( Q ) e(P, Q) = \frac{f_Q(P)}{f_P(Q)} e(P,Q)=fP(Q)fQ(P)
这个数,就是Weil配对值。
更关键的是,这个配对天然满足双线性:如果固定点Q,把第一个点从P1变成P1+P2(即两个点相加),配对值会从e(P1, Q)变成e(P1, Q)·e(P2, Q);对Q同理。也就是说:
e ( P 1 + P 2 , Q ) = e ( P 1 , Q ) ⋅ e ( P 2 , Q ) e(P_1 + P_2, Q) = e(P_1, Q) · e(P_2, Q) e(P1+P2,Q)=e(P1,Q)⋅e(P2,Q)
e ( P , Q 1 + Q 2 ) = e ( P , Q 1 ) ⋅ e ( P , Q 2 ) e(P, Q_1 + Q_2) = e(P, Q_1) · e(P, Q_2) e(P,Q1+Q2)=e(P,Q1)⋅e(P,Q2)
由此可以推导出核心性质:
e ( a ⋅ P , b ⋅ Q ) = e ( P + ⋯ + P a 个 P , Q + ⋯ + Q b 个 Q ) = e ( P , Q ) a ⋅ b e(a·P, b·Q) = e(P + \dots + P_{a个P}, Q + \dots + Q_{b个Q}) = e(P, Q)^{a·b} e(a⋅P,b⋅Q)=e(P+⋯+Pa个P,Q+⋯+Qb个Q)=e(P,Q)a⋅b
这样,就通过从有理函数的角度,把加法群的数乘关系,转换到了配对值乘法群的幂运算。
韦伊发现了双线性配对理论,当时只是作为纯数学工具,和密码学并未产生关联。大约20年后,另一位数学家约翰·泰特(John Tate) 在这个理论的基础上提出了Tate配对。Tate配对和Weil配对在数学结构上类似,都利用有理函数来构造两个点的映射,但Tate配对在定义上更灵活,计算效率更高。密码学里实际使用的配对,基本都基于Tate配对及其后续变种(如R-ate配对),因为它在工程上更快。后来密码学家在Tate配对的基础上做了进一步的工程优化,提出了Ate配对、R-ate配对等变体,它们在数学上等价,但计算速度更快。SM9标准中实际使用的就是R-ate配对。
配对运算e(P, Q)的输出,不是椭圆曲线上的点,而是一个数。这个数有一个特殊性质:它的r次方等于1,也就是回到所在乘法群的单位元。
问题是:这个r阶单位根,不一定存在于原来的数域F_q中。例如,在实数里,√-1不存在。为了让√-1有地方放,必须把实数域扩展到复数域C = R[i]。
配对的情况完全一样。为了确保配对值有地方可放,必须把原来的基域F_q扩展成更大的扩域F_{q^k}。k称为嵌入次数(可以理解为升维次数),它表示扩域是基域的几维扩展。例如k=12表示要把原来的数域扩展成一个12维的新数域,才能把配对值全部装下。
k由曲线的数学结构决定,不同的椭圆曲线有不同的嵌入次数,它是曲线的固有属性,就像人的血型一样不能更改。有些曲线的k太大,扩域大到无法计算;反之,有些曲线的k太小(比如1或2),配对值在基域里就能放,但这类曲线不安全——椭圆曲线上的离散对数问题可以被轻易破解。一般k=12是安全性和效率的平衡点。
1.3 SM9密码算法
上一节我们介绍了双线性配对的理论基础:椭圆曲线上的点具有双重身份,Weil和Tate发现了这种配对结构,R-ate配对则是工程上最优的实现方式。SM9算法正是建立在这个理论之上的一套完整的密码学方案。
SM9算法是我国独立自主研制的密码算法,于2020年正式成为国家标准,之后还成功纳入了国际ISO标准。SM9的出现,解决了传统公钥密码体系在海量设备时代“水土不服”的痛点。它的核心优势可以总结为三个词:无证书、低开销、高并发。由于它不需要复杂的证书管理,非常适合5G时代物联网领域的数据安全。
1.3.1 SM9的椭圆曲线参数
SM9使用一条特殊的椭圆曲线,称为BN曲线(Barreto-Naehrig curve)。这条曲线是为双线性配对量身定制的,它在保证安全性的同时,让配对计算足够快,能够用于实际工程。
SM9标准中推荐的曲线参数如下:
- 曲线方程:
y² = x³ + 5 - 基域大小:
q是一个256位的素数 - 嵌入次数:
k = 12 - 安全强度:128比特
SM9标准选用的配对实现方式是R-ate配对。它是在Tate配对基础上发展出来的一种优化变体,于2008年由Lee、Lee和Park三位学者提出。
在介绍具体的算法流程之前,需要先明确SM9中涉及的几个群结构,因为后面的步骤会反复用到它们。
SM9的配对运算是e: G1 × G2 → GT。这三个群各有分工:
- G1:椭圆曲线
y² = x³ + 5上坐标在基域F_q中的点构成的群。这里的坐标就是普通的整数(模q)。运算快,适合存放需要频繁使用的用户私钥和签名结果。 - G2:同一条椭圆曲线
y² = x³ + 5上坐标在扩域F_{q²}中的点构成的群。这里的坐标是二维数,比G1的整数坐标更大、运算更慢。但G2比G1更丰富,包含了G1中的所有点,还多出了许多只在扩域中才存在的额外点。 - GT:扩域
F_{q¹²}上的乘法群,配对运算的输出值落在这里,它是一个数,不是点。
把G1和G2放在一起说,方程都是y² = x³ + 5,点加法规则也完全相同,区别仅在于点的坐标取自哪个数域。就像整数只是实数的一部分,G1的点也只是G2点的子集。多出的那些点,是配对安全性所依赖的。配对运算是把一个G1中的点和一个G2中的点映射到GT中的一个数。如果只依靠G1,也能做G1 × G1 → GT的映射,但这样不安全,所有安全归约都集中到G1;而都只靠G2,在工程上实现比较慢。因此,选用两类群组合映射。
1.3.2 SM9数字签名算法
设待签名的消息为比特串M,为了获取消息M的数字签名(h, S),作为签名者的用户A的签名过程如下:
第一步:计算群GT中的元素g = e(P1, P_{pub-s}),其中P1是G1的生成元(一个公开的基点),P_{pub-s}是系统主公钥(在G2中,因为不是经常用,所以放到更大的G2里面)。配对把他们映射成GT中的一个数g。这个g是公开的,任何人可以预计算,不需要每次签名都重算;
第二步:产生随机数r ∈ [1, N-1],其中N是曲线的阶,也就是椭圆曲线上所有点(包括无穷远点)的总个数。这个随机数确保同一消息每次签名结果不同;
第三步:计算群GT中的元素w = g^r,并将w的数据类型转换为比特串,准备和消息一起做哈希;
第四步:计算整数h = H2(M || w, N),H2是SM9标准定义的一个以SM3为基础的专用杂凑函数;
第五步:计算整数l = (r - h) mod N,若l=0,意味着私钥被消掉,必须重新选随机数,这时则返回第二步,重新取随机数;
第六步:计算群G1中的元素S = [l]ds_A,过程是把私钥ds_A乘以整数l,得到签名中的S;
第七步:输出消息M的签名为(h, S)。
验签流程说明
验签的目的是验证收到的消息M'和签名(h', S')是否确实是由声称的签名者生成的,且消息未被篡改。核心逻辑是验证者用签名者的身份ID(公开信息)和系统主公钥,通过配对运算重新计算出签名过程中的w',然后重新计算哈希值h2,与收到的h'进行比较。如果相等,说明签名合法。
具体流程如下:
第一步:检查h'是否合法
将h'从字节串转成整数,检查它是否在[1, N-1]范围内。如果h'=0或h' >= N,直接判定无效。这是防止攻击者塞入非法数值的第一道防线。
第二步:检查S'是否合法
将S'从字节串转成椭圆曲线点,并检查它是否真的属于G1群。检查内容包括:点是否在曲线上、点的阶是否为N、点是否为无穷远点。如果任何一项不满足,直接判定无效。
第三步:计算g = e(P1, P_{pub-s})
这里的P_{pub-s}是系统主公钥,P1是G1的生成元。因为参数是系统公开的,任何人都可以算。这个值可以在验证前预计算并缓存,不需要每次验证都重算一遍。
第四步:计算t = g^{h'}
第五步:计算h1 = H1(ID || hid, N)
H1是另一个密码函数,和H2类似,底层调用SM3,区别在于不同的标识字节0x01。它的作用是把签名者的身份ID(比如"Alice@xxx.com")映射成一个整数h1,这个整数后续用来从系统参数中恢复出签名者的公钥。注意到,H1在签名的时候没有出现,这是因为其作用是把签名者的身份ID映射成公钥,签名者自己不需要做这个映射,他签名用的是私钥。验证者只有身份ID,必须通过H1才能恢复出对应的公钥点,才能做配对验证。
第六步:计算P = [h1]P2 + P_{pub}
这一步是把签名者的ID映射成公钥的过程。其中P2是G2的生成元,也是一个公开的基点;[h1]P2表示把P2做h1次数乘,得到一个G2中的点,再加上系统主公钥P_{pub}。这个P本质上就是签名者公钥在G2中的“投影”,即验证者用签名者的ID和系统参数重新构造出了这个点,而不需要证书来提供公钥。这是基于身份的密码体系的核心体现。
第七步:计算u = e(S', P)
这一步本质上就是验证签名用的私钥和签名者的公钥是否配对。
第八步:计算w' = u · t
把u和t在GT中相乘,得到w'。然后转成比特串,准备做哈希。这个w'如果签名合法,应该等于签名者生成签名时的w = g^r。
第九步:计算h2 = H2(M' || w', N),与h'比较
如果h2 = h',验证通过;否则不通过。这一步是最后的判决:如果w'正确(意味着S'确实是用正确的私钥生成的,且h'没有被篡改),那么重新算出的哈希值一定等于收到的h'。反之,任何环节出了问题(消息被改、签名被伪造、身份不对),哈希值都对不上。
1.3.3 SM9密钥封装流程说明
密钥封装的目的是发送方用接收方的身份ID,安全地生成一个对称密钥K,并把解密这个密钥所需的信息C一起发给接收方。只有持有合法私钥的接收方才能从C中恢复出K。
具体步骤如下:
第一步:Q_B = [H1(ID_B || hid, N)]P1 + P_{pub-e}
这是从接收方的身份ID恢复公钥点。H1把ID映射成一个整数,然后和P1做数乘,再加上系统主公钥P_{pub-e}。注意这里的P_{pub-e}在G1中,这是因为加密过程中发送方要频繁用到主公钥做配对运算,和签名算法里的P_{pub-s}在G2中刚好对调。得到的Q_B就是接收方的公钥点。发送方不需要证书,只需要知道接收方的ID就能算出这个公钥。
第二步:产生随机数r ∈ [1, N-1]
产生一个随机数r,同样是确保每次封装的结果都不同,这是为了防止攻击者通过观察多次封装的规律来反推信息。
第三步:C = [r]Q_B
把接收方的公钥点Q_B做r次数乘,得到C。它就是封装密文,接收方用私钥可以从C中恢复出共享秘密。注意这里的[r]Q_B类似Diffie-Hellman密钥交换中的g^r,但DH中输出是数,这里是椭圆曲线点。
第四步:g = e(P_{pub-e}, P2)
把加密主公钥P_{pub-e}(在G1中)和P2(在G2中)配对,得到GT中的一个数g。这个值不依赖消息和随机数,可以预计算并缓存,后续封装时直接用。
第五步:w = g^r
把g做r次幂,得到w。这是发送方的共享秘密中间材料,它包含了随机数r和系统主公钥的信息。
第六步:K = KDF(C || w || ID_B, klen)
用密钥派生函数KDF把C、w和接收方ID_B一起散列成所需长度的密钥K。C和w分别从两个不同的群中取值,因此即使其中一个被攻破,另一个仍然提供独立的安全保护,增加了攻击者恢复K的难度。
如果K是全0比特串(概率极低),则返回第二步重新选随机数,确保输出不为全0。
第七步:输出K, C
输出封装密钥K和封装密文C。发送方用K加密实际数据,然后把(C, 密文)一起发给接收方。
解密方怎么恢复K?
接收方收到C后,用自己的私钥d_B计算:
w ′ = e ( C , d B ) w' = e(C, d_B) w′=e(C,dB)
因为C = [r]Q_B,而d_B是Q_B对应的私钥(由KGC生成),配对运算保证:
e ( C , d B ) = e ( [ r ] Q B , d B ) = e ( Q B , d B ) r = g r = w e(C, d_B) = e([r]Q_B, d_B) = e(Q_B, d_B)^r = g^r = w e(C,dB)=e([r]QB,dB)=e(QB,dB)r=gr=w
所以接收方算出的w'等于发送方算出的w,再用同样的KDF(C || w' || ID_B, klen)就能得到同一个K,解出密文。
加密流程中的配对g = e(P_{pub-e}, P2)和验签流程的配对u = e(S', P)用的是同一套配对引擎,只是输入参数不同:一个用来封装密钥,一个用来验证签名。
1.3.4 SM9密钥加密流程
SM9公钥加密和前面讲的密钥封装(KEM)是同一套底层机制,区别在于加密的是固定长度的密钥还是任意长度的消息。密钥封装只输出一个固定长度的对称密钥,而公钥加密可以加密任意长度的消息,并且在输出中额外包含了校验值C3。
加密流程中的参数说明
M:待加密的消息(任意比特长度mlen)K1_len:对称加密算法需要的密钥长度(比如SM4需要128位)K2_len:MAC校验算法需要的密钥长度
具体步骤如下:
第一步:Q_B = [H1(ID_B || hid, N)]P1 + P_{pub-e}
从接收方的身份ID_B恢复出公钥点Q_B。这一步和密钥封装的第一步完全一样,因为发送方都是根据接收方的ID计算公钥。
第二步:产生随机数r ∈ [1, N-1]
产生一个随机数r,确保每次加密结果不同。
第三步:C1 = [r]Q_B
计算封装密文C1,在G1中。接收方用私钥可以从C1恢复出共享秘密。这一步对应密钥封装的第三步。
第四步:g = e(P_{pub-e}, P2)
预计算配对值g(可缓存)。
第五步:w = g^r
计算共享秘密w。
第六步:按加密方式分类派生密钥
这里分为两种模式,但核心逻辑相同:用KDF从C1 || w || ID_B派生出两个密钥K1和K2。
| 模式 | klen | K1用途 | K2用途 |
|---|---|---|---|
| 序列密码模式 | mlen + K2_len |
作为密钥流,与明文M异或得到C2 |
MAC校验密钥 |
| 分组密码模式 | K1_len + K2_len |
作为分组密码(如SM4)的密钥,加密M得到C2 |
MAC校验密钥 |
如果K1为全0比特串,则返回第二步重新选随机数(因为全0密钥会削弱加密强度)。
第七步:计算C3 = MAC(K2, C2)
用K2对C2做MAC校验,得到C3。接收方解密后会用同样的方式重新计算MAC,如果和收到的C3不匹配,说明密文在传输中被篡改了。
第八步:输出密文C = C1 || C3 || C2
输出由三部分拼接而成:C1(椭圆曲线点,用于恢复共享秘密)、C3(校验值,用于防篡改)、C2(加密后的消息)。顺序是C1 || C3 || C2而不是C1 || C2 || C3,是为了方便接收方按固定顺序解析:先解封装得到密钥,再用C3验证C2是否被篡改,最后才解密。
在密钥封装流程中,只算密钥,封装好给接收方,应用层自己决定这个密钥怎么用。而公钥加密流程中,算密钥的同时用这个密钥把消息加密好,输出完整的密文,应用层拿到后直接解密就行。
1.3.5 SM9密钥交换协议流程说明
密钥交换协议的目的是让发起方A和响应方B在公开信道上,通过对方的身份ID和自己的私钥,共同协商出一个只有他们两人知道的共享密钥。这个密钥后续可以用于对称加密(如SM4)保护通信数据。虽然在密钥封装里也有对称密钥的传递,但那是一方生成密钥、一方接受;密钥交换是双方共同计算出共享密钥。
整个流程可以分成“发起方A的计算”、“响应方B的计算”、“交换临时公钥”、“各自计算共享密钥”四个阶段。核心思路和Diffie-Hellman密钥交换一样:双方各选一个随机数作为临时私钥,算出临时公钥发给对方,然后分别用自己的私钥和对方的临时公钥计算出同一个共享秘密。但SM9多了ID的参与,所以不需要证书,并且因为配对的引入,能让双方的临时公钥在配对运算中“对称地”结合起来。
(1)第一阶段:发起方A的计算
第一步:Q_B = [H1(ID_B || hid, N)]P1 + P_{pub-e},用响应方B的身份ID_B恢复B的公钥点Q_B;
第二步:产生随机数r_A ∈ [1, N-1],发起方A产生一个临时随机数r_A,作为本次协商的临时私钥,用完即弃;
第三步:R_A = [r_A]Q_B,用B的公钥点Q_B做r_A次数乘,得到R_A。R_A相当于A的临时公钥,但它是用B的公钥“加密”过的,所以只有B能从中提取有效信息;
第四步:将R_A发送给用户B,开启协商。
(2)第二阶段:响应方B的计算
第一步:Q_A = [H1(ID_A || hid, N)]P1 + P_{pub-e},用发起方A的身份ID_A恢复A的公钥点Q_A;
第二步:产生随机数r_B ∈ [1, N-1],B产生自己的临时随机数r_B;
第三步:R_B = [r_B]Q_A,用A的公钥点Q_A做r_B次数乘,得到R_B,B的临时公钥;
第四步:验证R_A ∈ G1,方法是计算三个配对值。完成检查A发来的R_A是否合法(在曲线上、阶为N等)后,计算:
g1 = e(R_A, de_B),用B的私钥和A的临时公钥配对,这是B侧恢复共享秘密的关键;g2 = e(P_{pub-e}, P2)^{r_B},B用自己的临时随机数放大系统主公钥和基点的配对值;g3 = g1^{r_B},把两个临时秘密(A的r_A和B的r_B)通过配对折叠到一起;
第五步:计算共享密钥SK_B = KDF(ID_A || ID_B || R_A || R_B || g1 || g2 || g3, klen),B把双方ID、两个临时公钥、三个配对值一起输入KDF,派生出共享密钥;
第六步(可选):计算S_B = Hash(0x82 || g1 || Hash(g2 || g3 || ID_A || ID_B || R_A || R_B)),B生成一个密钥确认值S_B,用于让A确认B确实算出了正确的密钥。这可以防止中间人攻击;
第七步:将R_B(以及可选的S_B)发送给A。
(3)第三阶段:发起方A接收并计算
第一步:验证R_B ∈ G1,通过后计算三个配对值:
g1' = e(P_{pub-e}, P2)^{r_A},A用自己的临时随机数放大系统参数,对应B的g2;g2' = e(R_B, de_A),用A的私钥和B的临时公钥配对,对应B的g1;g3' = (g2')^{r_A},对应B的g3;
第二步(可选):如果B发来了校验S_B,A计算:
S 1 = H a s h ( 0 x 82 ∣ ∣ g 1 ′ ∣ ∣ H a s h ( g 2 ′ ∣ ∣ g 3 ′ ∣ ∣ I D A ∣ ∣ I D B ∣ ∣ R A ∣ ∣ R B ) ) S1 = Hash(0x82 || g1' || Hash(g2' || g3' || ID_A || ID_B || R_A || R_B)) S1=Hash(0x82∣∣g1′∣∣Hash(g2′∣∣g3′∣∣IDA∣∣IDB∣∣RA∣∣RB))
比较是否等于S_B。如果不等,说明B没有算出正确的密钥(可能是中间人攻击),从B到A的密钥确认失败。
第三步:计算SK_A = KDF(ID_A || ID_B || R_A || R_B || g1' || g2' || g3', klen),A用同样的输入派生共享密钥。配对的双线性性质保证SK_A == SK_B;
第四步(可选):计算密钥确认值:
S A = H a s h ( 0 x 83 ∣ ∣ g 1 ′ ∣ ∣ H a s h ( g 2 ′ ∣ ∣ g 3 ′ ∣ ∣ I D A ∣ ∣ I D B ∣ ∣ R A ∣ ∣ R B ) ) S_A = Hash(0x83 || g1' || Hash(g2' || g3' || ID_A || ID_B || R_A || R_B)) SA=Hash(0x83∣∣g1′∣∣Hash(g2′∣∣g3′∣∣IDA∣∣IDB∣∣RA∣∣RB))
发送给B。注意这里的标识字节是0x83,和B使用的0x82不同,这是用于区分方向,避免重放攻击。
(4)第四阶段:B完成校验
如果A发来了S_A,B计算:
S 2 = H a s h ( 0 x 83 ∣ ∣ g 1 ∣ ∣ H a s h ( g 2 ∣ ∣ g 3 ∣ ∣ I D A ∣ ∣ I D B ∣ ∣ R A ∣ ∣ R B ) ) S2 = Hash(0x83 || g1 || Hash(g2 || g3 || ID_A || ID_B || R_A || R_B)) S2=Hash(0x83∣∣g1∣∣Hash(g2∣∣g3∣∣IDA∣∣IDB∣∣RA∣∣RB))
比较是否等于S_A。如果不等,说明A没有算出正确的密钥,从A到B的密钥确认失败。
在这个协议里,配对运算e是核心。它的作用可以用一个公式来概括:
因为g3 = g1^{r_B},而g1 = e(R_A, de_B) = e([r_A]Q_B, de_B) = e(Q_B, de_B)^{r_A}。同时,B的私钥de_B与Q_B是配对的(e(Q_B, de_B) = e(P_{pub-e}, P2)),所以B算出的g2和g3与A算出的g1'和g3'在数学上完全相等。
这就是双线性配对最核心的作用:它让两个不同用户的临时秘密r_A和r_B,通过配对的“加法→乘法”性质,在GT这个乘法群里乘到了一起,变成g^{r_A·r_B}的形式。双方各自用对方的临时公钥和自己的私钥,加上自己选的随机数,从不同路径进入同一个配对结构,最后落在同一个数上。这样不需要交换密钥本身,只需要交换临时公钥。
最终,双方各自得出:
S K = K D F ( I D A ∣ ∣ I D B ∣ ∣ R A ∣ ∣ R B ∣ ∣ g 1 ∣ ∣ g 2 ∣ ∣ g 3 , k l e n ) SK = KDF(ID_A || ID_B || R_A || R_B || g1 || g2 || g3, klen) SK=KDF(IDA∣∣IDB∣∣RA∣∣RB∣∣g1∣∣g2∣∣g3,klen)
其中g1、g2、g3在A侧和B侧的值完全相等(注意A侧标了撇号,是同一套数值的不同计算路径),所以算出的SK也完全相同。
本章总结
本章围绕基于标识的密码算法(IBC) 及其国标SM9算法,系统地介绍了从理论思想到工程落地的完整知识体系。
首先,从传统PKI体系在万物互联时代面临问题,引出了IBC诞生的背景与核心思想:直接用用户的身份标识(如邮箱、手机号)作为公钥,从而消除对数字证书的依赖。我们回顾了Shamir在1984年提出这一构想,直到2001年Boneh-Franklin等人利用双线性配对才将其真正实现。
随后,我们深入探讨了IBC背后的数学基础。重点介绍了“哈希到曲线”如何将身份字符串映射为椭圆曲线上的公钥点,以及KGC如何用系统主私钥为用户生成对应的私钥,我们详细剖析了双线性配对这个核心工具,从有理函数的零点和极点出发,到Weil配对和Tate配对的数学构造,再到SM9实际采用的R-ate配对。
最后,我们全面介绍了SM9密码算法的四个核心功能模块:
数字签名算法:签名者用私钥对消息签名,验证者仅凭签名者的身份ID和系统主公钥,通过配对运算即可验证签名有效性。
密钥封装机制:发送方用接收方的身份ID生成一个对称密钥并将其封装,只有持有对应私钥的接收方才能解封得到该密钥。
公钥加密算法:在密钥封装的基础上,直接完成任意长度消息的加密,并附带MAC校验值以确保密文完整性。
密钥交换协议:通信双方通过对方的身份ID和各自的私钥,在公开信道上协同计算出仅双方知晓的共享会话密钥,整个过程无需证书参与。
通过本章的介绍可以看出,SM9算法以双线性配对为数学基石,将“身份即公钥”的构想落地为一套无证书、低开销、高并发的完整密码体系,为物联网、车联网等海量设备场景下的身份认证与数据安全提供了自主可控的解决方案。
更多推荐



所有评论(0)