A - 数字签名
数字签名
原书:《Understanding Cryptography: A Text book for Students and Practitioners》
数字签名在今天被广泛使用。对数字签名的应用有安全电子商务的数字证书以及安全软件升级的合法签名。
数字签名与手写签名具有一些共性。其中之一,提供消息来自某个用户的认证。数字签名相较于手写签名还能够提供更多功能。
10.1 介绍
10.1.1 为什么对称加密是不充分的
我们前面介绍的算法主要集中在两个目标:要么加密数据,要么建立共享密钥。我们是否可以说安全大厦已经建成了呢?让我们看下面的情景。
假设我们有通讯双方,A 与 B,他们共享密钥。用块运算进行加密。当 A 接收到消息并解密消息,得到的解密消息是有意义的消息,那么他可以得出结论,这条消息是来自那个与他共享密钥的人。如果只有 A 与 B 知道这个密钥,他们可以确定消息在传递过程中没有被篡改。在此之前,我们总是假设,坏人来自外界。然而实际情况下,总是自己人希望欺骗另一方。这就导致对称密钥的持有者双方并不能保护自己不被对方欺骗。
假设 B 是消费者,A 是线上汽车经销商,A 与 B 使用 Diffie-Hellman 密钥交换建立了共享密钥 \(K_{AB}\),B 选中了他喜欢的一辆汽车,内部粉色外部橙色,这个选择是大多数消费者不会选择的颜色,将订单使用 AES 加密给到了 A。A 解密这个订单,并很高兴地将那辆汽车送给了 B,然而,当 B 收到货之后,B 发现他并不喜欢这种颜色搭配,A 的线上商城并没有提供退货的选项。因此 B 宣称他并没有下这个订单给 A,于是 A 向法院起诉了 B。在法官面前,A 的律师声明 B 一定下了这个订单,因为只有 A 与 B 共享那个密钥。B 的律师则辩驳道,这个订单也可能是 A 假造的。法官并不知道这个订单究竟是谁生成的。这种情况下,在大部分国家,B 都不会为他的不诚实付出代价。
在很多情况下,需要一个置信第三方充当裁判,来判定一条消息是由通讯双方的某一方生成。而且这个置信第三方是不容置疑的。我们不能使用对称密钥策略来实现这个目标,通讯双方具有相同的知识具备相同的能力,因此,置信第三方并不能区分某个操作是由通讯双方的哪一个进行的。一般而言,这个问题可以使用公钥算法解决,公钥算法的非对称性可以支持这样的置信第三方来判定某些行为只能由其中一个实现,某些行为双方都可以进行。
10.1.2 数字签名原则
在现实世界中,显然也需要证明某条消息是由某个人生成的,以前这是通过在纸面上手写签名实现的。比如,如果我们手签一个合同,那么置信第三方可以依据这个签名判定这个合同是有效的。就像手写签名一样,在数字签名中,只有创建数字消息的那一方才能生成有效的签名。为了实现这个目的,我们需要依赖公钥策略。基本思想是,发送方使用私钥 \(k_{pr}\) 签名,接收方使用公钥 \(k_{pub}\) 验证。流程如下:
B 先将公钥 \(k_{pub}\) 发送给 A,使用 \(k_{pr}\) 对消息 \(x\) 进行签名得到签名 \(s\),将签名附加到 \(x\) 后面,组成 \((x,s)\) 消息,并将 \((x,s)\) 发送给 A,A 接收到消息后,使用 \(k_{pub}\) 验证这条消息与签名是否匹配。因为只有 B 持有私钥,只有 B 有能力对消息 \(x\) 进行签名。需要注意的是,数字签名本身是没有意义的,只有与消息一起才有作用。
数字签名本身只是一个大的整数值,比如,一个 2048 比特位的数字。A 只有在有能力校验签名是否有效时,签名才有意义。这就需要一个校验函数,以 \(x\) 与 \(s\) 做为输入,为了将签名与 B 联系起来,这个函数还需要他的公钥。虽然校验函数的输入很长,它的输出仅仅为 "是" 或 "否"。如果消息确实是由公钥对应的那个私钥签名,那么输出则是 "true",否则返回 "false"。
现在,我们可以提出一般数字签名协议:
基础数字签名协议:
B 生成公私对 \(k_{pr,B}\)、\(k_{pub,B}\),并将公钥 \(k_{pub,B}\) 发布
A 拿到公钥 \(k_{pub,B}\)
B 使用私钥对数据 \(x\) 签名得到 \(s = sig_{k_{pr}}(x)\),将数据+签名 \((x,s)\) 发送给 A
A 收到 \((x,s)\) 之后,校验签名 \(k_{pr,B}(x,s) = true/false\)
这样,就可以判定这个消息确实是由 B 发送的了。但是需要注意的是,上面的方法并不能提供消息的机密性,因为前面的消息都是以明文发送的,我们可以使用 AES/3DES 加密这些数据,来保证消息的机密性。
三种流行的公钥算法,都支持我们构建数字签名。
10.1.3 安全服务
存在很多安全服务,最重要的几个列出如下:
- 机密性,消息对其他人保密
- 完整性,消息在传递过程中没有被篡改
- 消息认证,消息来源可确定
- 不可抵赖性,消息的发送者不能否认发送了这条消息
不同的应该需要不同的安全服务。比如,对于私人邮件需要满足前三个安全服务,而商业邮件则还需要满足第四条安全服务。
上面四个安全服务,可以使用本书中介绍过的算法实现。比如,机密性可以使用对称加密/非对称加密实现,完整性与消息认证可以通过数字签名与消息认证码实现,不可抵赖性可以通过数字签名实现。
除了上面提到的四个安全服务,还需要下面的几个安全服务:
- 身份/实体认证,建立并验证一个实体的身份,比如是一个人、电脑、信用卡
- 访问权限控制,限制受限实体的访问权限
- 可用性,确保电子系统是可以被安全访问的
- 审计,提供安全相关的活动的记录,记录特定事件的日志
- 物理安全,保护物理安全
- 匿名性,保护个人的隐私安全
安全服务是与应用强相关的,比如匿名性在电子邮件系统中是不需要的,因为电子邮件需要确定发送者的身份。另一方面,在车与车之间避免碰撞的系统中,则需要确保车辆与车内人员的匿名性,以防止车辆/人员被跟踪。再比如,为了保证操作系统的安全性,需要做到对访问者访问权限的控制。
10.2 RSA 签名策略
10.2.1 RSA 数字签名
假设 B 想要发送一个带签名的消息 \(x\) 给 A。
RSA 密钥
B 的私钥 \(k_{pr} = d\)
B 的公钥 \(k_{pub} = (n,e)\)
实际的签名协议如下,消息 \(x\) 在 \((1,2,...,n-1)\) 范围内。
基础 RSA 数字签名协议
B 首先生成公私钥对 \(k_{pr} = d,k_{pub} = (n,e)\),并将公钥 \((n,e)\) 发布给 A
B 计算签名 \(s = sig_{k_{pr}}(x) \equiv x^d \mod n\),并将 \((x,s)\) 发送给 A
A 拿到消息后,进行消息验证 \(ver_{k_{pub}}(x,s)\),\(x' \equiv s^e \ mod \ n\),若 \(x' \equiv x \ mod \ n\) 则是有效签名,否则签名无效。
这个过程能够进行消息认证并保证消息完整性。
证明
因为私钥与公钥之间的数学关系,有:
例 10.1 假设 B 希望发送 \((x = 4)\) 给 A:
B 选择质数 \(p = 3\),\(q = 11\),\(n = p\cdot q = 33\),\(\Phi(n) = (3-1)(11-1) = 20\),选择 \(e = 3\),\(d \equiv e^{-1} \equiv 7 \ mod \ 20\),将 \((33,3)\) 发送给 A
B 计算消息签名 \(x = 4\),\(s = x^d = x^7 \equiv 16 \mod 33\),将 \((4,16)\) 发送给 A
A 验证 \(x' = s^e = 16^3 \equiv 4 \ mod \ 33\),因为 \(x' \equiv x \ mod \ 33\),校验有效
10.2.2 计算方面
在实际场景中,消息只会签名一次,而会校验多次,因此快速校验是十分重要的,这可以使用短公钥实现。
10.2.3 安全性
必须确保公钥是认证的,这意味着校验运算的一方拥有的公钥确实是私钥拥有者发布的公钥。如果攻击者提供了自己公私对的公钥,那么可以伪装自己的身份。为了防止这样的攻击,我们需要使用证书,这会在第 13 章介绍。
算法攻击
对算法的攻击需要计算私钥 \(d\),这需要对大数的质因数分解。
存在性伪造攻击
攻击者 O 知道 \((n,e)\),选择签名 \(s \in Z_n\),计算消息 \(x \equiv s^e \ mod \ n\),得到 \((x,s)\),接收者 A 接收到消息 \((x,s)\),进行校验 \(s^e \equiv x' \ mod \ n\),因为 \(x'= x\) 校验成功。这个过程看起来很奇怪,攻击者首先确定签名,之后生成对应的消息,这样他就不能够控制消息内容 \(x\)。
RSA填充:概率签名标准 PSS: Probabilistic Signature Standard
前面提到的攻击方式都可以通过特定的消息格式来避免。比如,必须要求所有的消息 \(x\) 的结尾 100 位全部为 0。如果攻击者 O 选择签名之 \(s\) 并计算对应的消息 \(x \equiv s^e \ mod \ n\),那么生成的消息 \(x\) 大概率不具备这样的格式。
让我们看一下 RSA-PSS,几乎所有的实现中,并不会直接对消息直接签名,而是对它们的哈希值做签名。哈希函数计算消息的数字指纹,这个数字指纹的长度是固定为 160 位或 256 位,消息的长度是随意的。关于哈希函数的详细消息可以看第 11 章。
下面我们介绍 EMSA: Encoding Method for Signature with Appendix
的 PSS,并使用 M 表示消息:
EMSA PSS 编码
令 \(|n|\) 表示为 RSA 模数的比特位长。编码消息 \(EM\) 长度为 \((|n| - 1)/8\) 字节。
- 生成一个随机数 \(salt\)
- 将一个固定的头部\(padding_1\) 与消息 \(M\) 的哈希值 \(mHash = h(M)\) 以及随机数 \(salt\) 串联到一起 \(M' = padding_1 - mHash - salt\)
- 计算 \(M'\) 的哈希值 \(H\)
- 将一个固定头部 \(padding_2\) 以及 \(salt\) 串联到一起生成 \(DB = padding_2 - salt\)
- 对 \(M'\) 使用掩码生成函数,生成掩码值 \(dbMask\),在实践中,通常使用 SHA-1 做为掩码生成函数
MGF: Mask Generation Function
- 将 \(DB\) 与 \(dbMask\) 做异或生成 \(maskedDB\)
- 将 \(maskedDB\)、哈希值 \(H\) 与一个固定的填充 \(bc\) 串联,形成 \(EM = maskedDB - H - bc\)
在得到上面的编码后实际的签名操作是对这个编码消息 \(EM\) 进行的:
接收者是知道这个标准中使用的 \(padding_1\) 与 \(padding_2\) 的。
10.3 Elgamal 数字签名策略
Elgamal 数字签名策略,发布于 1985 年,是基于离散对数问题的单向性提出的。在 RSA 算法中,加密与数字签名几乎是一样的操作。但是 Elgamal 数字签名与加密策略就有很大不同。
10.3.1 Elgamal 数字签名
Elgamal 数字签名密钥生成
- 选择一个大的质数 \(p\)
- 选择群 \(Z_p^*\) 或子群 \(Z_p^*\) 的一个原始元素 \(\alpha\)
- 选择一个随机整数 \(d\in \{2,3,...,p-2\}\)
- 计算 \(\beta = \alpha^d \ mod \ p\)
公钥 \(k_{pub} = (p,\alpha,\beta)\),私钥为 \(k_{pr} = d\)。
Elgamal 签名生成
使用私钥与公钥的参数,签名为:
签名由两个整数 \(r\) 与 \(s\) 组成。签名由两个主要步骤,第一步选择一个随机值 \(k_E\),这个值用作临时的私钥,第二步计算 \(x\) 的签名。
-
选择随机的临时密钥 \(k_E \in \{0,1,2,...,p-2\}\),满足 \(gcd(k_E,p-1) = 1\)
-
计算签名参数
\[r \equiv \alpha^{k_E} \ mod \ p \\ s \equiv (x - d\cdot r)k_E^{-1} \ mod \ p - 1 \]
Elgamal 签名校验
在接收一侧,签名使用公钥,签名与消息做 \(ver_{k_{pub}}(x,(r,s))\) 校验:
-
计算值 \(t \equiv \beta^r \cdot r^s \ mod \ p\)
-
校验遵循:
\[t = \begin{cases} \equiv \alpha^x \ mod \ p --> 有效签名\\ !\equiv \alpha^x \ mod \ p --> 无效签名 \end{cases} \]
简而言之若 \(\beta^r \cdot r^s \equiv \alpha^x \ mod \ p\) 成立,签名 \((r,s)\) 有效,否则签名无效。
证明
为校验签名有效,需要证明上面的表达式等效于 \(\alpha^x\):
使用费马小定理,上面的关系只有在表达式两侧的指数存在下面的关系时成立:
签名参数 \(s\) 遵循:
我们需要 \(gcd(k_E,p-1) = 1\) 这个条件,因为我们必须在计算 \(s\) 的时候,需要临时密钥对 \(p-1\) 的逆。
例 10.2
B 选择 \(p = 29,\alpha = 2,d = 12,\beta = \alpha^d \equiv 7 \ mod \ 29\),并将 \((p,\alpha,\beta)\) 发布给 A
B 计算消息 \(x\) 的签名:选择 \(k_E = 5,gcd(5,28) = 1\),\(r = \alpha^{k_E} \equiv 2^5 \equiv 3 \ mod \ 29\),\(s = (x - dr)k_E^{-1} \equiv (-10)\cdot 17 \equiv 26 \ mod \ 28\),并将 \((x,(r,s)) = (26,(3,26))\) 发送给 A
A 收到消息后进行校验:\(t = \beta^r \cdot r^s \equiv 7^3 \equiv 22 \ mod \ 29\),\(\alpha^x \equiv 2^26 \equiv 22 \ mod \ 29\),\(t \equiv \alpha^x \ mod \ 29\),签名有效
10.3.2 计算方面
签名者可以预先生成临时密钥 \(k_E\) 与 \(r\),当需要签名时可以用来计算 \(s\),计算 \(r\) 需要指数模 \(p\),这可以通过重复平方乘实现。校验一方使用重复平方乘算法进行两次指数运算。
10.3.3 安全性
前面提到的 RSA 签名策略中遇到的问题在 Elgamal 数字签名策略中也会遇到,但解决方式也类似。
计算离散对数
求解离散对数问题是目前已知的攻击 Elgamal 算法的关键,因此需要选择何理的参数,使 DLP 问题难以求解。质数 \(p\) 为 1024 位长以上,我们也必须确保小的子群攻击也是不可行的,因此,实际上,会选择原始元素 \(\alpha\) 来生成具有质数阶的子群,在这样的群中,所有元素都是原始元素,小的子群并不存在。
临时密钥重用
如果签名者重用临时密钥 \(k_E\) 攻击者可以轻松计算出私钥。
攻击者 O 观察两个 \((x,(r,s))\) 签名与数字消息,如果两个消息 \(x_1\) 与 \(x_2\) 具有相同的临时密钥 \(k_E\),O 可以通过观察 \(r\) 值来确定,因为 \(r_1 = r_2 = \alpha^{k_E}\)。两个 \(s\) 值不同,攻击者 O 可以得到下面的两个表达式:
这两个等式包含未知的密钥 \(d\),是 B 的私钥!将等式两侧同时乘 \(k_E\),等式变成简单计算的线性系统。对两个式子做差可以得到:
可以计算得到临时密钥:
如果 \(gcd(s_1 - s_2,p - 1) \neq 1\),那么等式具有多个解,O 则需要校验结果的正确定。利用计算得到的 \(k_E\),O 可以计算私钥:
得到私钥后,攻击者 O 就能够扮演 B 的角色了。为了防止这种形式的攻击,每一个数字签名都需要从随机数生成器中获得新鲜的临时密钥。
例 10.3 假设 O 观察到了如下两条签名消息:
O 已知 B 的公钥,\((p,\alpha,\beta) = (29,2,7)\),拥有了这些消息,O 现在可以计算临时密钥:
这样就可以计算出 B 的私钥:
存在性伪造攻击
与 RSA 数字签名类似,Elgamal 算法也存在攻击者从签名中生成一个随机消息 \(x\) 的可能。如果消息已经经过哈希那么攻击者将无能为力。
10.4 数字签名算法
前面介绍的原生的 Elgamal 签名算法在实际中很少使用。相反,它的变体则更加流行,称作 DSA: Digital Signature Algorithm
。它是美国联邦政府的数字签名标准。相较于 Elgamal 签名策略,DSA 的签名只有 320 位,且能够攻击 Elgamal 的算法却不能够攻击 DSA。
10.4.1 DSA 算法
下面介绍的 DSA 算法具有 1024 为常,但是算法支持更长位长。
密钥生成
- 生成质数 \(2^{1023} < p < 2^{1024}\)
- 寻找一个大的质数 \(2^{159} < q < 2^{160}\),且 \(q\) 能够整除 \(p - 1\)
- 寻找一个元素 \(\alpha\) 满足 \(ord(\alpha) = q\),\(\alpha\) 生成具有 \(q\) 个元素的子群
- 选择随机整数 \(0 < d < q\)
- 计算 \(\beta \equiv \alpha^d \ mod \ p\)
- 形成密钥 \(k_{pub} = {p,q,\alpha,\beta}\),\(k_{pr} = d\)
DSA 的中心思想是这里会涉及到两个循环子群,其中一个是大的 \(Z_p^*\) 这个循环子群的阶位长是 1024,第二个是 160 位长的 \(Z_p^*\),这样的配置可以得到短的签名。
\(p\) 位长 | \(q\) 位长 | 签名 |
---|---|---|
1024 | 160 | 320 |
2048 | 224 | 448 |
3072 | 256 | 512 |
生成签名
就像在 Elgamal 签名策略中的那样,DSA 签名由一对整数 \((r,s)\) 组成。因为两个参数都只有 160 位长,从的签名长度是 320 位长。使用公钥与私钥,消息 \(x\) 的签名计算如下:
- 选择一个整数做为随机临时密钥 \(k_E\),有 \(0 < k_E < q\)
- 计算 \(r \equiv (\alpha^{k_E} \ mod \ p) \ mod \ q\)
- 计算 \(s \equiv (SHA(x) + d \cdot r)k_E^{-1} \ mod \ q\)
在这个标准下,消息 \(x\) 必须先使用哈希函数 SHA-1 进行哈希。哈希函数会在第 11 章介绍,在这里,我们只要知道 SHA-1 将消息 \(x\) 压缩并计算得到一个 160 位长的指纹,这个指纹可以用来代表消息 \(x\)。
签名校验
- 计算辅助值 \(w \equiv s^{-1} \ mod \ q\)
- 计算辅助值 \(u_1 \equiv w \cdot SHA(x) \ mod \ q\)
- 计算辅助值 \(u_2 \equiv w \cdot r \ mod \ q\)
- 计算 \(v \equiv (\alpha^{u_1} \cdot \beta^{u_2} \ mod \ p) \ mod \ q\)
- 若 \(v \equiv r \ mod \ q\) 则校验通过,否则校验不通过
证明 首先从签名 \(s\) 开始
等价于:
等式右侧可以使用辅助值 \(u_1\) 与 \(u_2\) 表示:
从而有:
因为公钥值 \(\beta\) 可以计算为 \(\beta \equiv \alpha^d \ mod \ p\),我们可以写作:
从而有:
因为 \(r \equiv (\alpha^{k_E} \ mod \ p)\),\(v \equiv (\alpha^{u_1}\beta^{u_2} \ mod \ p)\ mod \ q\),等效于 \(r \equiv v \ mod \ q\)。
10.4.2 计算方面
密钥生成
一般步骤是先生成一个 160 位的质数 \(q\),并依据它构造一个大的质数 \(p\)。
/* 生成质数 q 有 2^159 < q < 2^160 */
For i = 1 to 4096
/* 生成随机整数 2^1023 < M < 2^1024 */
M_r = M mod 2q
p - 1 = M - M_r
IF p is prime
RETURN(p, q)
i = i + 1
/* 否则从头执行一遍算法 */
签名
在签名时,我们计算 \(r\) 与 \(s\),计算 \(r\) 可以使用重复平方乘算法。因为 \(k_E\) 只有 160 位长,大约需要 \(1.5 \times 160 = 240\) 次运算。虽然算术是基于 1024 的,但因为最后的结果会模 \(q\),最终生成的是 160 位的结果。计算 \(s\) 只会涉及 160 位的数值,代价最高的步骤是计算 \(k_E\) 的逆。
因为 \(r\) 与消息独立,因此这个值可以预先计算,以加速签名过程。
校验
计算辅助参数 \(w,u_1,u_2\) 都是 160 位长的数据,这样的计算速度会很快。
10.4.3 安全性
标准参数位长与 DSA 的安全等级:
\(p\) | \(q\) | 哈希输出 | 安全等级 |
---|---|---|---|
1024 | 160 | 160 | 80(\(2^{80}\) 计算复杂度) |
2048 | 224 | 224 | 112 |
3072 | 256 | 256 | 128 |
10.5 椭圆曲线数字签名算法(ECDSA)
10.5.1 ECDSA 算法
密钥生成
- 使用具有如下特性的椭圆曲线 \(E\):
- 模数 \(p\)
- 系数 \(a,b\)
- 点 \(A\) 生成质数阶 \(p\) 的循环子群
- 选择随机整数 \(0 < d < q\)
- 计算 \(B = dA\),得到密钥 \(k_{pub} = (p,a,b,q,A,B)\),\(k{pr} = d\)
与 DSA 相似,循环群的阶是 \(q\),位长至少是 160 位。
签名
- 选择一个整数做为随机临时密钥 \(0 < k_E <q\)
- 计算 \(R = k_EA\)
- 令 \(r = x_R\)
- 计算 \(s \equiv (h(x) + d\cdot r)k_E^{-1} \ mod \ q\)
校验
- 计算辅助值 \(w \equiv s^{-1} \ mod \ q\)
- 计算辅助值 \(u_1 \equiv w\cdot h(x) \ mod \ q\)
- 计算辅助值 \(u_2 \equiv w \cdot r \ mod \ q\)
- 计算 \(P = u_1 A + u_2 B\)
- 若 \(x_P \equiv r \ mod \ q\) 则签名有效,否则签名无效
最后一步中的 \(x_P\) 表示点 \(P\) 的 \(x\) 坐标。
证明 首先看一下签名 \(s\):
等效于:
等式右侧可以使用 \(u_1,u_2\) 表示:
因为基于点 A 生成的阶为 \(q\) 的循环群,我们可以构造:
因为群操作满足结合律,因此有:
且有:
10.5.2 计算方面
密钥生成
前面曾提过,寻找合适的椭圆曲线并不是一件轻松的工作,因此实际应用中会使用现成的标准曲线。剩下的就是密钥生成的其他步骤了。
签名
在签名阶段我们会计算点 \(R\),这需要点乘操作,这样也就计算出了 \(r\),而计算参数 \(s\) 我们需要临时密钥的逆,可以使用扩展欧几里得算法实现。剩下的主要操作就是消息的哈希以及模运算了。
校验
计算辅助值 \(w,u_1,u_2\) 只需要进行模算术。主要的计算负载在于计算 \(u_1A + u_2B\)。
10.5.3 安全性
\(q\) 位长 | 哈希输出 | 安全等级 |
---|---|---|
192 | 192 | 96 |
224 | 224 | 112 |
256 | 256 | 128 |
384 | 384 | 192 |
512 | 512 | 256 |