单向散列函数

单向散列函数有一个输入和一个输出,输入称为消息(message),输出称为散列值(hash value)。

散列值又称哈希值、密码校验和(cryptographic checksum)、指纹(fingerprint)、消息摘要(message digest)。

散列值的长度和消息的长度无关。例如 SHA-256 单向散列函数计算出的散列值的长度都是 256 比特。

根据消息的内容计算出的散列值可以被用来检查消息的完整性。

单向散列函数的性质需要具备的性质:

  1. 从任意长度的消息计算出固定长度的散列值。
  2. 能够快速计算出散列值。
  3. 消息不同散列值也不同。
  4. 具备单向性(无法通过散列值反算出消息)。

两个不同的消息产生同一个散列值的情况称为碰撞(collision)。

抗碰撞性(collision resistance)指难以发现碰撞。密码技术中使用的单向散列函数必须具备抗碰撞性。

当给定某条消息的散列值时,单向散列函数必须确保找到和该条消息具有相同散列值的另外一条消息是困难的,这一性质称为弱抗碰撞性

单向散列函数必须具备弱抗碰撞性。

强抗碰撞性指要找到散列值相同的两条不同的消息是非常困难的这一性质。

密码技术中使用的单向散列函数不仅要具备弱抗碰撞性,还必须具备强抗碰撞性。

你能找我和我手里的叶子一模一样的叶子算我输;你随便找,找到两片一模一样的叶子算我输。

应用和实例

实际应用:

  1. 检测软件是否被篡改
  2. 基于口令的加密
    • 基于口令的加密(Password Based Encryption,PBE)原理是将口令和盐(salt,通过伪随机数生成器产生的随机值)混合后计算其散列值,然后将改散列值用作加密的密钥,这样可以防御针对口令的字典攻击。
  3. 消息认证码
    • 使用单向散列函数可以构造消息认证码。消息认证码是将发送者和接收者之间的共享密钥和消息进行混合后计算出的散列值。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装(消息认证码在 SSL/TLS 中也得到了运用)。
  4. 数字签名
    • 数字签名是现实中的签名和盖章这样的行为在数字世界中的实现。数字签名处理过程非常耗时,因此一般不会对整个消息内容直接施加数字签名,而是先通过单向散列函数计算出消息的散列值,然后再对这个散列值进行数字签名。
  5. 伪随机数生成器
    • 使用单向散列函数可以构造伪随机数生成器。密码技术中所使用的随机数需要具备“事实上不可能根据过去的随机数列预测未来的随机数列”这样的性质。为了保证不可预测性,可以利用单向散列函数的单向性。
  6. 一次性口令
    • 使用单向散列函数可以构造一次性口令(one-time password)。一次性口令常用于服务器对客户端的合法性认证,通过使用单向散列函数可以保证口令只在通信链路上传送一次,因此即使窃听者窃取了口令,也无法使用。

MD4,MD5 是能够生成 128 比特散列值的单向散列函数(MD 指 Message Digest)。
针对 MD4 的碰撞的方法已被提出,MD5 的强抗碰撞性已被攻破,它们都不再安全。

SHA-1 是由 NIST 设计的能够产生 160 比特散列值的单向散列函数。
SHA-1 的强抗碰撞性已于 2005 年被攻破,不再安全。

SHA-256、SHA-384 和 SHA512 都是由 NIST 设计的单向散列函数,它们的散列值长度分别为 256 比特、384 比特和 512 比特,这些单向散列函数合起来统称 SHA-2。

6 种版本的 SHA-2:

名称 输出长度 内部状态长度 备注
SHA-224 224 32 × 8 = 256 将 SHA-256 的结果截掉 32 比特
SHA-256 256 32 × 8 = 256
SHA-512/224 224 64 × 8 = 512 将 SHA-512 的结果截掉 288 比特
SHA-512/256 256 64 × 8 = 512 将 SHA-512 的结果截掉 256 比特
SHA-384 384 64 × 8 = 512 将 SHA-512 的结果截掉 128 比特
SHA-512 512 64 × 8 = 512

SHA-224 和 SHA-256 在实现上采用了 32×8 比特的内部状态,因此更适合 32 位的 CPU。

SHA-2 消息长度存在上限:SHA-256 上限接近 $$2^{64}$$ 比特,SHA-384 和 SHA-512 上限接近 $$2^{128}$$ 比特。

SHA-2 尚未被攻破,是安全的,可以使用。

RIPEMD-160 是一种能够产生 160 比特散列值的单向散列函数,是欧盟 RIPE 项目所设计的 RIPEMD 单向散列函数的修订版。这一系列函数还包括 RIPEMD-128、RIPEMD-256、RIPEMD-320 等其他一些版本。
RIPEMD 的强抗碰撞性已于 2004 年被攻破。

比特币中使用的就是 RIPEMD-160。

SHA-3(Secure Hash Algorithm-3)是一种作为新标准发布的单向散列函数算法,用来替代在理论上已被找出攻击方法的 SHA-1 算法。全世界的企业和密码学家提交了很多 SHA-3 的候选方案,经过 5 年的选拔,于 2012 年正式确定将 Keccak 算法作为 SHA-3 标准。

基于 NIST 所设定的条件,用户可以免费、自由地使用 SHA-3 算法(这点与 AES 一样)。

SHA-3 是安全的,可以使用。

对单向散列函数的攻击

  1. 暴力破解
    • 任何文件的内容都有一定的冗余性,利用文件的冗余性生成具有相同散列值的另一个文件,就是一种针对单向散列函数的攻击。具体做法是每次都改变一点消息的值,然后对消息求散列值。这种攻击目的是寻找一条具备特定散列值的消息,是一种试图破解单向散列函数的“弱抗碰撞性”的攻击。
    • 暴力破解需要尝试的次数可以根据散列值的长度计算出来。
      • 以 SHA3-512 为例,它的散列值长度为 512 比特,最多只要尝试 $$2^{512}$$ 次就能够找到目标消息,但如此多的尝试次数在现实中是不可能完成的。
      • 由于尝试次数完全由散列值长度决定的,因此散列值长度越长的单向散列函数,其抵御暴力破解的能力也就越强。
  2. 生日攻击
    • 生日攻击(birthday attack)是一种试图破解单向散列函数的“强抗碰撞性”的攻击。它的目标不是找到可以生成特定散列值的消息,而是要找到散列值相同的两条消息,散列值可以是任意值。
    • 生日问题描述:N 个人组成一个集合,N 至少多大时 N 人的集合中至少有两个人生日是同一天的概率大于二分之一。
      • 先算 N 个人生日全都不一样的概率,再用 1 去减,结果是 $$1-(365×364×…×(365-N+1))/365^N$$。N 取 23 时,结果约为 0.507297,大于二分之一。
      • 任意生日的相同概率比想象的要大,这一现象称为生日悖论(birthday paradox)。
    • 生日问题一般化:一年的天数为 Y 天,N 至少多大时 N 人的集合中至少有两个人生日世同一天的概率大于二分之一。
      • 当 Y 非常大时,N 和 Y 的近似关系为 $$N = \sqrt{Y}$$。
    • 散列值相当于生日,所有可能出现的散列值的数量相当于一年的天数。若单向散列函数的散列值长度为 M 比特,则 M 比特能包含的散列值的个数为 $$2^M$$(相当于一年的天数 Y)。当 N(生成散列值的个数)等于 $$\sqrt {2^M}$$ 时,生日攻击就会有二分之一的概率能够成功,与暴力破解相比所需的尝试次数少得多。

找出具有指定散列值的消息的攻击分为“原像攻击”和“第二原像攻击”。

原像攻击(Pre-Image Attack)指给定一个散列值,找出具有该散列值的任意消息。

第二原像攻击(Second Pre-Image Attack)是指给定一条消息 a,找出另外一条消息 b,使消息 b 的散列值和消息 a 相同。

单向散列函数不能做什么

主动攻击者 M 伪装成 A 向 B 发送消息和散列值,B 可以通过单向散列函数验证消息的完整性,但无法确认消息是否真的来自 A。

当不仅需要确认消息的完整性,还需要确认消息来源时,就需要进行认证。

用于认证的技术包括消息验证码和数字签名。消息认证码能够向通信对象保证消息没有被篡改;数字签名不仅能够向通信对象保证消息没有被篡改,还能够向所有第三方做出这样的保证。

消息认证码

使用消息认证码(Message Authentication Code,MAC)可以判断消息是否被篡改,以及是否有人伪装成发送者发送了该消息。

消息的完整性消息指没有被篡改,消息的认证指消息来自正确的发送者

消息认证码的输入包括任意长度的消息,和一个发送者与接收者之间共享的密钥;它的输出是固定长度的数据,称为 MAC 值。
输入任意长度的消息可以输出固定长度的数据,这一点和单向散列函数类似。

要计算 MAC 必须持有共享密钥,没有共享密钥的人就无法计算 MAC 值,消息认证码利用这一点来完成认证。

使用步骤与应用实例

使用步骤:

  1. 发送者 A 和接收者 B 事先共享密钥。
  2. A 使用共享密钥计算消息的 MAC 值。
  3. A 将消息和它的 MAC 值发送给 B。
  4. B 用共享密钥计算接收到的消息的 MAC 值,并与 A 发送过来的 MAC 比对,一致则认证成功(消息确实由 A 发送),否则认证失败。

消息认证码同样需要解决密钥配送问题。

应用实例:

  1. SWIFT
    • SWIFT 全称是 Society for Worldwide Interbank Financial Telecommunication(环球银行金融电信协会)。银行和银行之间通过 SWIFT 来传递交易消息的。为了确认消息的完整性以及对消息进行验证,SWIFT 使用了消息认证码。
  2. IPsec
    • IPsec 是对互联网基本通信协议 IP 协议(Internet Protocol)增加安全性的一种方式。在 IPsec 中,对通信内容的认证和完整性校验都是采用消息认证码来完成的。
  3. SSL/TLS
    • SSL/TLS 中对通信内容的认证和完整性校验也使用了消息认证码。

消息认证码的 HMAC 实现

消息认证码的实现方法:

  • 使用单向散列函数实现:使用 SHA-2 之类的单向散列函数,其中一种实现方法称为 HMAC(H 指 Hash)。
  • 使用分组密码实现:使用 AES 之类的分组密码。
  • 其他实现方法:使用流密码和公钥密码等。

HMAC 步骤(hash(opadkey || hash(ipadkey || message))):

  1. 参数含义:
    • ipad 是将 00110110 这一比特序列(16 进制的 36)不断循环扩展,直到达到单向散列函数的分组长度后得到的比特序列,i 是 inner(内部)的意思。
    • opad 是将 01011100 这一比特序列(16 进制的 5C)不断循环扩展,直到达到单向散列函数的分组长度后得到的比特序列,o 是 outer(外部)的意思。
  2. 填充共享密钥 secret 得到 padded_secret
    • secret 比单向散列函数的分组长度要短,就需要在末尾填充 0,直到长度达到单向散列函数的分组长度;若 secret 比分组长度要长,则要用单向散列函数计算密钥的散列值,然后将这个散列值用作 HMAC 的密钥。
  1. padded_secretipad 进行 XOR 运算得到 ipadkey
    • ipadkey 就是一个和单向散列函数的分组长度相同,且和密钥相关的比特序列。
  2. ipadkey 拼接到 message 的开头;
  3. 将拼接后的 message 输入单向散列函计算散列值;
  4. padded_secretopad 进行 XOR 运算得到 opadkey
    • opadkey 也是一个和单向散列函数的分组长度相同,且和密钥相关的比特序列。
  5. opadkey 拼接到第 3 步中计算得到的散列值的开头;
  6. 将第 6 步的结果输入单向散列函计算散列值,得到最终的 MAC。
    • MAC 是一个和输入的消息以及密钥都相关的长度固定的比特序列。

消息认证码的攻防

  1. 重放攻击(replay attack)
    • 攻击过程:
      • M 可以窃听到 A、B 两家银行的通信。
      • M 到 A 银行向自己在 B 银行的账户汇款 100 元。
      • A 银行将该汇款请求和对应的 MAC 值发送给 B 银行。
      • M 窃听到该条汇款请求消息和 MAC 并将它们保存下来。
      • B 银行用收到的消息自行计算 MAC 值,并与收到的 MAC 值进行比对,结果相同,于是判断该消息是来自 A 银行的合法请求,向 M 的账户汇款 100 元。
      • M 将保存下来的汇款请求和 MAC 值再次发给银行 B。
      • B 银行仍然判断请求合法,再次向 M 的账户汇款。
      • M 继续重放攻击,B 继续给 M 的账户转账。
    • 防御:
      1. 序号:约定每次都对发送的消息赋予一个递增的编号(序号),在计算 MAC 值时将序号也包含在消息中。这样一来攻击者无法计算序号递增之后的 MAC 值,也就无法进行重放攻击。缺点是每个通信对象都需要记录最后一个消息的序号。
      2. 时间戳:约定在发送消息时包含当前的时间,如果收到的是以前的消息,即使 MAC 值正确也将其当做错误的消息来处理,这样就能防御重放攻击。此方法要求发送者和接收者的时钟必须一致,考虑到通信的延迟,必须在时间判断上留下缓冲,于是还是存在少量重放攻击的空间。
      3. nonce:在通信前接收者先向发送者发送一个一次性的随机数 nonce,发送者在消息中包含这个 nonce 并计算 MAC 值,由于每次通信时 nonce 的值都会变化,因此无法进行重放攻击。缺点是通信的数据量有所增加。
  2. 密钥推测攻击
    • 和对单向散列函数的攻击一样,对消息认证码也可以进行暴力破解以及生日攻击。
    • 对于消息认证码来说,要保证不能根据 MAC 值推测出通信双方所使用的共享密钥。必须使用密码学安全的、高强度的伪随机数生成器来生成消息认证码所需要的密钥,若密钥由人为选定,会增加密钥被推测的风险。
      • HMAC 利用单向散列函数的单向性和抗碰撞性来保证无法根据 MAC 值推测出密钥。

消息认证码不能做什么

消息认证码无法解决的问题:向第三方证明和防否认。

B 收到 A 的消息之后,想向第三方 C 证明消息确实是 A 发送的。C 要校验 MAC 值,就需要知道 A 和 B 之间共享的密钥。即便 B 相信 C、同意将共享密钥告诉 C,C 也无法判断这条消息是 A 发送的还是 B 自己发送的。

同理,A 也可以否认向 B 发过该条消息,因为 B 无法证明消息不是他自己发的。

消息认证码防止了没有共享密钥的人的伪装,但无法防止持有共享密钥的人伪装成其它的共享密钥持有者。

数字签名可以实现对第三方的证明和防止否认的功能。

数字签名

在数字签名中,生成签名和验证签名使用的是不同的密钥(和消息认证码不同),实际上是把公钥密码“反过来”使用。

消息认证码和数字签名的区别可以类比对称密码和公钥密码。

RSA 算法中的公钥密码和数字签名是完全相反的关系,其它算法中不一定。

  私钥 公钥
公钥密码 接收者解密时使用 发送者加密时使用
数字签名 签名者签名时使用 验证者验证签名时使用
密钥持有者 私人持有 任何人都可持有

公钥密码中,私钥用于解密并接收他人发送给自己的加密消息;数字签名中,私钥用于将自己要发送的消息进行签名后发送出去。

用公钥加密所得到的密文只能用与之配对的私钥才能解密;同样地,用私钥加密所得到的密文也只能用与之配对的公钥才能解密。

用私钥进行加密这一行为只能由持有私钥的人完成,正是基于这一事实我们才可以将用私钥加密过的密文当作签名看待,使得签名者无法否认。

具体签名方法

  1. 直接对消息签名
    1. A 用私钥加密消息 a,得到 A 对消息 a 的签名 sa,并将消息 a 和签名 sa 一起发送给 B。
    2. B 尝试用 A 的公钥解密签名 sa(即验证签名)。若收到的签名不是用 A 的私钥签名的结果,则解密后得到的数据看上去是随机的;若解密得到的内容和消息 a 一致,则验签成功。

公钥密码算法很慢,对整个消息进行加密非常耗时。

  1. 对消息的散列值签名
    1. A 用单向散列函数计算出消息 a 的散列值 ha,用私钥对 ha 加密得到的密文 sha 就是 A 对消息散列值的签名。A 和消息和签名一起发送给 B。
    2. 此处 B 比对的是散列值 sa 的内容是否一致。

数字签名的加密不是为了保证机密性,而是在利用没有私钥的人无法生成正确签名这一性质。

生成的密文是私有持有者认证符号(authenticator),因为只有他能生成。

数字签名所要实现的不是防止修改,而是识别修改。

和现实世界不一样的是,消息的内容不同,数字签名也不同。

老的数字签名无法作废。要想作废,可以在消息内容中声明该消息的有限期(如公钥证书),或者对新的消息内容做一份数字签名并以新的为准。

进行数字签名时,用户不会亲自去计算而是交由软件处理。不安全的环节在软件端。

ECDSA(Elliptic Curve Digital Signature Algorithm)是一种利用椭圆曲线密码来实现的数字签名算法(NIST FIPS 186-3)。

对数字签名的攻击

  1. 中间人攻击
    • 攻击者在发送者和接收者中间,对发送者伪装成接收者,对接收者伪装成发送者。
    • 此处关键在于确认自己拿到的公钥是否真的属于自己的通信对象
  2. 对单向散列函数的攻击
    • 在数字签名中使用的单向散列函数必须具备抗碰撞性,否则攻击者可以生成另外一条不同的消息,使其与绑定的消息具有相同的散列值。
  3. 利用数字签名攻击公钥密码
    • \(签名(密文) = 消息(明文)^E mod N\),对消息签名即是解密消息。
    • 攻击方法:
      1. 攻击者窃听到 A 发送给 B 的密文(用 B 的公钥加密)s 并保存下来。
      2. 攻击者让 B 签名一段消息并将结果发送给他,消息的内容是 s,s 在 B 看来只是一段无法理解的随机数据。
      3. 攻击者拿到的是 A 发送给 B 的消息的明文。
    • 绝对不要对不理解的消息签名
    • 应对策略:
      • 不直接对消息签名,而是对消息的散列值签名。
      • 公钥密码和数字签名使用不同的密钥对。
  4. 潜在伪造
    • 如果一个没有私钥的攻击者能够对有意义的消息生成合法的数字签名,这个数字签名算法无疑是不安全的,因为这样的签名可以被伪造。即使签名的对象是无意义的消息(例如随机比特序列),如果攻击者能够生成合法的数字签名(即攻击者生成的签名能够正常通过校验),也应该将其当成是对这种签名算法的潜在威胁,这种情况称为潜在伪造。
    • 在用 RSA 来解密消息的数字签名算法中,潜在伪造是可能的:只要将随机比特序列 S 用 RSA 的公钥加密生成密文 M,S 就是 M 的合法数字签名。
      • RSA-PSS(Probabilistic Signature Scheme)签名算法并不对消息本身进行签名,而是对其散列值进行签名,在计算散列值的时候还要对消息加盐(salt)。
  5. 其他攻击
    • 针对公钥密码的攻击方法大部分都能够被用于攻击数字签名。

数字签名无法解决的问题

要能够正确使用数字签名的一个大前提是用于验证签名的公钥必须属于真正的发送者。

用于识别消息篡改、伪装以及否认的数字签名本身需要使用公钥,但它却无法识别它要使用的公钥是否是伪造的、是否被篡改,陷入死循环。

为了确认公钥的合法性,需要使用证书。

证书是将公钥作为一条消息,由一个可信的第三方对其签名后所得到的公钥。

这样的方法只是把问题转移了。为了对证书上施加的数字签名进行验证,必定需要另一个公钥。

如何构筑一个可信的数字签名链条、由谁来颁发可信的证书,这已经是社会学的问题。

我们需要让公钥以及数字签名技术成为一种社会性的基础设施,即公钥基础设施(Public Key Infrastructure, PKI)。

证书

公钥证书(Public-Key Certificate, PKC)记有申请者的姓名、组织、邮箱地址等信息和申请者的公钥,并由可信的第三方认证机构(Certification Authority, CA)进行数字签名。

一张公钥证书是认证机构对证书上的公钥和个人信息间的关联关系认可。
驾驶证是公安部对个人和他所具备的驾驶技能之间的关系的认可。

公钥证书简称证书(certificate)。

A 向 B 发送密文实例:

  1. B 生成一对公钥和私钥(也可以由 CA 代为生成)。
  2. B 将公钥发给 CA,CA 收到 B 的公钥后,确认所收到的公钥是否为 B 本人所有。
    • 若认证机构提供的是测试用的服务,可能不会进行任何身份确认。
    • 赛门铁克的证书服务中身份确认的三个等级:
      1. 通过发送邮件来确认本人身份。
      2. 通过第三方数据库来确认本人身份。
      3. 通过当面认证和身份证明来确认本人身份。
  3. CA 用自己的私钥对 B 的公钥进行数字签名并生成 B 的证书。
  4. A 从 CA 处拿到 B 的证书,使用 CA 的公钥验证 B 证书中的数字签名,验证成功则表示证书中的公钥确实属于 B。到此 A 获得 B 的合法公钥。
  5. A 用 B 的公钥加密要发送的消息并发送给 B。
  6. B 用自己的私钥解密信息。

证书的标准规范中,使用最广泛的是 X.509 规范(RFC3280)。

浏览器上点击网站 https 图标可以查看证书。

仅制定证书的规范还不足以支撑公钥的实际运用,还需要很多其他规范,例如证书由谁颁发,如何颁发, 私钥泄露时如何作废证书,计算机之间的数据交换采用什么格式等。

公钥基础设施 PKI

公钥基础设施(Public-Key Infrastructure)是为了能够更有效地运用公钥而制定的一系列规范和规格的总称。

PKI 是一个总称,并非指某一个单独的规范或规格。例如 RSA 公司制定的 PKCS(Public-Key Cryptography Standards,公钥密码标准)系列规范就是 PKI 的一种;互联网规格 RFC(Request for Comments)中也有很多与 PKI 相关的文档。

PKI 组成要素:

  1. 用户:使用 PKI 的实体
  2. 认证机构(CA):颁发证书的实体
  3. 仓库(证书目录):保存证书的数据库

实体(entity)就是对证书和密钥进行操作的行为主体。

在认证机构生成用户密钥对的场景下,认证机构需要将私钥发送给用户,具体的方法在 RFC7292(PKCS #12: Personal Information Exchange Syntax V 1.1)中进行了规定。

在用户自行生成密钥对的场景下,用户会请求认证机构来生成证书。申请证书时所使用的规范由 RFC2986(PKCS #10: Certification Request Syntax Specification Version 1.7)等定义。

认证机构根据认证业务准则(Certification Practice Statement,CPS)对用户的身份进行认 证,并生成证书。生成证书时需使用认证机构的私钥进行数字签名。生成的证书格式由 X.509 定义。

要作废证书,认证机构需要有一张证书作废清单(Certificate Revocation List,CRL)。

CRL 是一张己作废的证书序列号的清单,并由认证机构加上数字签名。

认证机构的公钥,可以由其他的认证机构进行数字签名,在这样的认证链条的终点的 CA 称为根 CA。

根 CA 对自己的公钥进行数字签名(称为自签名,self-signature),即自己为自己颁发证书。

信任链条的终点是对根 CA 的信任。

如果能够取得可信的公钥,则不需要认证机构。

在能够处理证书的电子邮件软件和 Web 浏览器中,已经包含了一些知名认证机构的证书。

不管证书的链条是否具有层级结构,我们之所以信任某个认证机构,是因为那是我们基于多个可信的情报源所做出的判断。

无论是数字签名、证书,还是认证机构的层级结构,都不可能在完全不可信的状态下创造出信任关系。只有以某种已经存在的信任关系为种子,才能够引申出其他的信任关系。

对证书的攻击

  1. 在公钥注册之前发动攻击
    • B 生成密钥对在认证机构注册公钥之前,公钥被攻击者替换。
    • 应对方法:认证机构在确认 B 身份时可以将公钥的指纹一并发送给 B 让他确认。
  2. 申请实体名相似的证书进行攻击
  3. 窃取认证机构的私钥进行攻击
  4. 伪装成认证机构进行攻击
  5. 利用 CRL 发布的时间差来发动攻击。
    • 窃取他人私钥后在 CRL 更新之前使窃取的私钥作恶。
    • A 用自己的私钥签名让 B 给自己转账的消息,收到 B 的账款后 A 马上向 CA 注销自己的原先的私钥。A 以私钥丢失而 CRL 未及时更新、转账消息是窃取私钥者用窃取的私钥进行签名的这样的说辞为由,进行抵赖。
  6. Superfish
    • Superfish 是一款广告软件,它能够通过监听和收集用户通信中的个人信息来有针对性地展示广告。为了实现这一功能,Superfish 会在系统中安装根证书,并劫持浏览器与服务器之间的通信, 将网站的证书替换成自己的证书。为了能够针对任意网站动态生成证书,Superfish 内置了用于生成数字签名的私钥,用户的计算机变成了一个不可信的 CA,而且生成签名所需的口令只是一个简单的单词。这样一来,恶意软件就可以利用 Superfish 随意生成伪造的网站证书,使得钓鱼网站在用户的浏览器上看起来就像真正的网站一样。

References