赞
踩
《图解密码技术》是一本非常适合新手密码学入门的书籍,对于密码学相关的概念讲解的的非常清楚到位。本文是本人阅读该书做的笔记,供大家参考,不足之处,请批评指正。
公钥密码(public-key cryptography)指在加密和解密时使用不同密钥的方式。因此,公钥密码又称为非对称密码( asymmetric cryptography )。
单向散列函数,就可以检测出数据是否被篡改过。保证完整性,而不是机密性
密码算法是需要重复使用的,但在重复使用同一种算法的过程中,该算法被破译的可能性也在逐渐增大。因此,我们就在密码算法中准备了一些可变部分,并在每次通信时都对这部分内容进行改变,而这一可变部分就是密钥。
将密码算法和密钥分开考虑 ,就解决了希望重复使用,但重复使用会增加风险这个难题
编码:将现实世界中的东西映射为比特序列的操作。ASCII
**XOR:**exclusive or 异或
**暴力破解:**是按顺序将所有的密钥都尝试一遍,并判断所得到的是不是正确的明文的方法。然而,在一次性密码本中,由于我们无法判断得到的是不是正确的明文,因此一次性密码本是无法破译的。
压缩软件的压缩原理,是找出输入数据中出现的冗余的重复序列,并将它们替换成较短的数据。然而一次性密码本所使用的密钥是随机的, 其中不包含任何冗余的重复序列。反过来说,如果一个比特序列能够被压缩,就说明它不是_个随机的比特序列。
DES 是一种将 64 比特的明文加密成 64 比特的密文的对称密码算法,它的密钥长度是 56 比特。尽管从规格上来说,DES 的密钥长度是 64 比特,但由于每隔 7 比特会设置一个用于错误检查的比特,因此实质上其密钥长度是 56 比特。
**轮函数的作用:**是根据 “右侧” 和子密钥生成对 “左侧” 进行加密的比特序列,它是密码系统的核心。将轮函数的输出与 “左侧” 进行 XOR 运算,其结果就是 “加密后的左侧”。也就是说,我们用 XOR 将轮函数的输出与 “左侧” 进行了合并。而输人的 “右侧” 则会直接成为输出的 “右侧”。
总结,一轮的具体计算步骤:
(1) 将输入的数据等分为左右两部分。
(2) 将输人的右侧直接发送到输出的右侧。
(3) 将输入的右侧发送到轮函数。
(4) 轮函数根据右侧数据和子密钥,计算出一串看上去是随机的比特序列。
(5) 将上一步得到的比特序列与左侧数据进行 XOR 运算,并将结果作为加密后的左侧。无论是任何轮数、任何轮函数,Feistel网络都可以用相N的结构实现加密和解密,且加密的结果必定能够正确解密。
**差分分析:**是一种针对分组密码的分析方法,这种方法由 Biham 和 Shamir 提出,其思路是“改变一部分明文并分析密文如何随之改变”。理论上说,即便明文只改变一个比特,密文的比特排列也应该发生彻底的改变。于是通过分析密文改变中所产生的偏差,可以获得破译密码的线索。
**线性分析:**由松井充提出,思路是 “将明文和密文的一些对应比特进行 XOR 并计算其结果为零的概率”,如果密文具备足够的随机性,则任选一些明文和密文的对应比特进行 XOR 结果为零的概率应该为如果能够找到大幅偏离的部分,则可以借此获得一些与密钥有关的信息。
差分分析和线性分析都有一个前提,那就是假设密码破译者可以选择任意明文并得到其加
密的结果,这种攻击方式称为选择明文攻击( Chosen Plaintext Attack, CPA )。
三重 DES ( triple-DES )是为了增加 DES 的强度,将 DES 重复 3 次所得到的一种密码算法,
也称为 TDEA ( Triple Date Encryption Algorithm ),通常缩写为 3DES。
三重DES的加密机制如下图:
三重DES是是加密—>解密—>加密的过程。
AES 是取代其前任标准(DES)而成为新标准的一种对称密码算法。在 2000年从这些候选算法中选出了一种名为 Rijndael 的对称密码算法,并将其确定为了 AES。
Rijndael 是由比利时密码学家 Joan Daemen 和 Vincent Rijmen设计的分组密码算法,于2000 年被选为新一代的标准密码算法—AES。
和 DES —样,Rijndael 算法也是由多个轮构成的,其中每一轮分为 SubBytes、ShiftRows、
MixColumns 和 AddRoundKey 共 4 个步骤。DES 使用 Feistel 网络作为其基本结构,而 Rijndael
没有使用 Feistel 网络,而是使用了 SPN 结构。
流密码是对数据流进行连续处理的一类密码算法。密钥长度与明文长度一致,加密的效率低。
分组密码是每次只能处理特定长度的一块数据的一类密码算法,这里的“一块”就称为分组。分组密码解决了密钥长度与明文一致的问题。分组密码只能加密固定长度的分组,但是我们需要加密的明文长度可能超过分组密码的分组长度,此时就需要对分组密码进行迭代,以便将长明文进行加密。迭代的方法就称为分组密码的模式(mode)。
模式有很多种类,分组密码的主要模式有以下五种:
定义:在ECB模式中,将明文分组加密之后的结果将直接成为密文分组。
特点:使用ECB模式加密时,相同的明文分组会被转换为相同的密文分组(一一对应),因此ECB模式也称为电子密码本模式。
攻击:攻击者只要对任意密文分组进行替换、删除或复制,相应的明文分组也会被替换、删除或复制,即攻击者无需破译密码就能够操纵明文。
定义:在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。
当加密第一个明文分组时,由于不存在“前一个密文分组”,因此需要事先准备一个长度为一个分组的比特序列来代替“前一个密文分组”,这个比特序列称为初始化向量(IV)。一般来说,每次加密时都会随机产生一个不同的比特序列作为初始化向量。
特点:
a. 明文分组在加密之前一定会与“前一个密文分组”进行XOR运算,因此即使明文分组1和2的值是相等的,密文分组的值也不一定是相等的。
b. 在加密过程中,我们无法单独对一个中间的明文分组进行加密。
c. 在解密过程中,假设有一个密文分组损坏了,只要密文分组的长度没有变化,则解密时最多只会有2个分组受到数据损坏的影响。
d. 假设密文分组中有一些比特缺失了,那么会导致密文分组的长度发生变化,此后的分组发生错位,则缺失比特的位置之后的密文分组就全部无法解密了。
攻击:如果攻击者能够对初始化向量中的任意比特进行反转,则明文分组中相应的比特也会被反转,即通过修改密文来操纵解密后的明文。但是,对密文分组也进行同样的攻击是非常困难的。
填充提示攻击是一种利用分组密码中的填充部分来进行攻击的方法,适用于所有需要进行分组填充的模式。(在分组密码中,当明文长度不为分组长度的整数倍时,需要在最后一个分组中填充一些数据使其凑满一个分组长度。)攻击者会反复发送一段密文,每次发送时都对填充的数据进行少许改变。由于接收者在无法正确解密时会返回一个错误消息,攻击者通过这一错误消息就可以获得一部分与明文相关的信息。防御这种攻击的方法是对密文进行认证,以确保密文是由合法的发送者在知道明文内容的前提下生成的。
定义:在CFB模式中,前一个密文分组会被送回到密码算法的输入端(即所谓的“反馈”)。
与CBC模式比较:在CBC模式中,明文分组和密文分组之间有XOR和密码算法两个步骤;而在CFB模式中,明文分组和密文分组之间只有XOR。
CFB模式与CBC模式一样,也需要使用初始化向量(IV)。
与流密码比较:CFB模式的结构与一次性密码本非常相似。一次性密码本是通过将“明文”与“随机比特序列”进行XOR运算来生成“密文”的;而CFB模式则是通过将“明文分组”与“密码算法的输出”进行XOR运算来生成“密文分组”的。即CFB模式中密码算法的输出相当于一次性密码本中的随机比特序列。
CFB模式中由密码算法所生成的比特序列称为密钥流(key stream)。在CFB模式中,密码算法就相当于用来生成密钥流的伪随机数生成器,而初始化向量就相当于伪随机数生成器的“种子”。
在CFB模式中,明文数据可以被逐比特加密,因此我们可以将CFB模式看作是一种使用分组密码来实现流密码的方式。
CFB模式解密时,分组密码算法依然执行加密操作,因为密钥流是通过加密操作生成的。
攻击:对CFB模式可以实施重放攻击(replay attack)。
定义:在OFB模式中,密码算法的输出会反馈到密码算法的输入中。
OFB模式并不是通过密码算法对明文直接进行加密的,而是通过将“明文分组”和“密码算法的输出”进行XOR来产生“密文分组”的。在这一点上OFB模式和CFB模式非常相似。
和CBC模式、CFB模式一样,OFB模式中也需要使用初始化向量(IV)。
与CFB模式比较:
a. OFB模式和CFB模式的区别仅仅在于密码算法的输入。CFB模式中,密码算法的输入是前一个密文分组(密文反馈模式);OFB模式中,密码算法的输入则是密码算法的前一个输出(输出反馈模式)。
b. CFB模式是对密文分组进行反馈的,因此无法跳过明文分组1而先对明文分组2进行加密;OFB模式中,XOR所需要的比特序列(密钥流)可以事先通过密码算法生成,和明文分组无关。只要提前准备好所需的密钥流,则在实际加密的过程中,就完全不需要动用密码算法了,只要将明文与密钥流进行XOR即可。因此,生成密钥流的操作和进行XOR运算的操作是可以并行的。此外,和AES等密码算法相比,XOR运算的速度是非常快的。
定义:CTR模式是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码。
计数器的生成方法:每次加密时都会生成一个不同的值来作为计数器的初始值。其中前8个字节为nonce,这个值在每次加密时必须都是不同的。后8个字节为分组序号,这个部分是会逐次累加的。
与OFB模式比较:CTR模式和OFB模式一样,都属于流密码。OFB模式是将加密的输出反馈到输入;而CTR模式则是将计数器的值用作输入。
特点:
a. CTR模式的加密和解密使用了完全相同的结构,因此在程序实现上比较容易。(和OFB模式一样)
b. CTR模式可以以任意顺序对分组进行加密和解密,因为计数器的值可以由nonce和分组序号直接计算出来。(OFB模式不具备)
c. 能够以任意顺序处理分组,就意味着能够实现并行计算。在支持并行计算的系统中,CTR模式的速度是非常快的。
攻击:在CTR模式中,攻击者可以通过反转密文分组中的某些比特,引起解密后明文中的相应比特也发生反转。(这一弱点和OFB模式相同)
不过CTR模式具备一个比OFB模式要好的性质。在OFB模式中,如果对密钥流的一个分组进行加密后其结果碰巧和加密前是相同的,那么这一分组之后的密钥流就会变成同一值的不断反复。在CTR模式中不存在这一问题。
不发送密钥吧,接收者 Bob 无法解密;发送密钥吧,连窃听者 Eve 也可以解密了(因为密码算法本来就应该是以公开为前提的,隐蔽式安全性是非常危险的)。密钥必须要发送,但又不能发送,这就是对称密码的密钥配送问题。
解决密钥配送问题:
定义:公钥密码也可称为非对称密码(asymmetric cryptography)。公钥密码( public-key cryptography )中,密钥分为加密密钥和解密密钥两种。发送者用加密密钥对消息进行加密,接收者用解密密钥对密文进行解密。其中加密密钥可以公开,解密密钥(也叫做私钥)是不可以公开的。
密钥对中的两个密钥之间具有非常密切的关系——数学上的关系——因此公钥和私钥是不能分别单独生成的。
时钟运算包括加、减、乘、除、乘方、对数
RSA是一种公钥密码算法,它的名字是由它的三位开发者,即 RonRivest 、 Adi Shamir 和Leonard Adleman 的姓氏的首字母组成的( Rivest-Shamir-Adleman );RSA可以被用于公钥密码和数字签名。
RSA 的加密是求明文的E 次方 mod N,因此只要知道E和N这两个数,任何人都可以完成加密的运算。所以说,E和N是 RSA 加密的密钥,也就是说,E和N的组合就是公钥。
E (Encryption)和 N(Number)并不是随便什么数都可以的,它们是经过严密计算得出的。
**注意:**E和N这两个数并不是密钥对(公钥和私钥的密钥对)。E、N这两个数才组成一个公钥,因此一般写成“公钥是(E,N)”或者公钥是{E,N}
对表示密文的数字的 D次方求 mod N就可以得到明文,数 D(Decryption) 和数N组合起来就是 RSA 的解密密钥,因此 D 和N的组合就是私钥。
混合密码系统( hybrid cryptosystem )是将对称密码和公钥密码的优势相结合的方法。一般情况下,将两种不同的方式相结合的做法就称为混合( hybrid )。
将消息通过对称密码来加密,将加密消息时使用的密钥通过公钥密码来加密,这样的两步密码机制就是混合密码系统的本质。
混合密码系统的组成机制:
混合密码系统运用了伪随机数生成器 、 对称密码和公钥密码这三种密码技术。正是通过这三种密码技术的结合,才创造出了一种兼具对称密码和公钥密码优点的密码方式。
会话密钥是对称密码的密钥,同时也是公钥密码的明文。将对称密码和公钥密码两种密码方式相互联系
起来的正是会话密钥。
混合密码系统运用了伪随机数生成器 、 对称密码和公钥密码,因此其中每一种技术要素的强度都必须很高。然而实际上还不仅如此,这些技术要素之间的强度平衡也非常重要。
混合密码系统中,伪随机数生成器被用于产生会话密钥。如果伪随机数生成器的算法很差,生成的会话密钥就有可能被攻击者推测出来。
混合密码系统中,对称密码被用于加密消息。当然 ,我们需要使用高强度的对称密码算法,并确保密钥具有足够的长度。此外,我们还需要选择使用合适的分组密码模式。
混合密码系统中,公钥密码被用于加密会话密钥。我们需要使用高强度的公钥密码算法,并确保密钥具有足够的长度 。
混合密码系统中运用了对称密码和公钥密码两种密码方式,无论其中任何一方的密钥过短,都可能遭到集中攻击,因此对称密码和公钥密码的密钥长度必须具备同等的强度。
然而,考虑到长期运用的情况,公钥密码的强度应该要高于对称密码,因为对称密码的会话密钥被破译只会影响本次通信的内容,而公钥密码一旦被破译,从过去到未来的( 用相同公钥加密的 )所有通信内容就都能够被破译了。
数字签名,是由单向散列函数和公钥密码组合而成的。
证书,是由公钥和数字签名组合而成的。
消息认证码,是由单向散列函数和密钥组合而成的,也可以通过对称密码来生成。
伪随机数生成器,可以使用对称密码、单向散列函数或者公钥密码来构建。
单向散列函数( one-way hash function )有一个输入和一个输出,其中输人称为消息( message ),输出称为散列值( hash value )。单向散列函数可以根据消息的内容计算出散列值,而散列值就可以被用来检查消息的完整性。散列值的长度和消息的长度无关。
根据任意长度的消息计算出固定长度的散列值
能够快速计算出散列值
消息不同散列值也不同:两个不同的消息产生同一个散列值的情况称为碰撞( collision )。
这里所说的抗碰撞性,指的是难以找到另外一条具备特定散列值的消息。当给定某条消息的散列值时,单向散列函数必须确保要找到和该条消息具有相同散列值的另外一条消息是非常困难的。这一性质称为弱抗碰撞性。单向散列函数都必须具备弱抗碰撞性。
和弱抗碰撞性相对的,还有强抗碰撞性。所谓强抗碰撞性,是指要找到散列值相同的两条不同的消息是非常困难的这一性质。在这里,散列值可以是任意值。
密码技术中所使用的单向散列函数,不仅要具备弱抗碰撞性,还必须具备强抗碰撞性。
具备单向性
单向散列函数必须具备单向性( one-way )。单向性指的是无法通过散列值反算出消息的性质。根据消息计算散列值可以很容易,但这条单行路是无法反过来走的
**注意:**尽管单向散列函数所产生的散列值是和原来的消息完全不同的比特序列,但是单向散列函数并不是一种加密,因此无法通过解密将散列值还原为原来的消息。
单向散列函数也称为消息摘要函数( message digest function )、 哈希函数或者杂凑函数 。
输入单向散列函数的消息也称为原像( pre-image )。
单向散列函数输出的散列值也称为消息摘要( message digest )或者指纹( fingerprint )。
完整性也称为一致性。
很多软件,尤其是安全相关的软件都会把通过单向散列函数计算出的散列值公布在自己的官方网站上。用户在下载到软件之后,可以自行计算散列值,然后与官方网站上公布的散列值进行对比。通过散列值,用户可以确认自己所下载到的文件与软件作者所提供的文件是否一致。
为了减轻服务器的压力,很多软件作者都会借助多个网站(镜像站点 )来发布软件,在这种情况下,单向散列函数就会在检测软件是否被篡改方面发挥重要作用。
单向散列函数也被用于基于口令的加密( Password Based Encryption, PBE )。
PBE 的原理是将口令和盐( salt, 通过伪随机数生成器产生的随机值 )混合后计算其散列值,然后将这个散列值用作加密的密钥。通过这样的方法能够防御针对口令的字典攻击。
使用单向散列函数可以构造消息认证码。
消息认证码是将 “发送者和接收者之间的共享密钥” 和 “消息” 进行混合后计算出的散列值。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装。消息认证码在 SSL/TLS 中也得到了运用。
数字签名是现实社会中的签名和盖章这样的行为在数字世界中的实现。数字签名的处理过程非常耗时,因此一般不会对整个消息内容直接施加数字签名,而是先通过单向散列函数计算出消息的散列值,然后再对这个散列值施加数字签名。
密码技术中所使用的随机数需要具备 “事实上不可能根据过去的随机数列预测未来的随机数列” 这样的性质。为了保证不可预测性,可以利用单向散列函数的单向性。
一次性口令经常被用于服务器对客户端的合法性认证。在这种方式中,通过使用单向散列函数可以保证口令只在通信链路上传送一次( one-time ),因此即使窃听者窃取了口令,也无法使用。
Keccak 是一种被选定为 SHA-3 标准的单向散列函数算法。
Keccak 可以生成任意长度的散列值,但为了配合 SHA-2 的散列值长度,SHA-3 标准中共规定了 SHA3-224、SHA-3-256、SHA3-384、 SHA3-512 这 4 种版本。在输人数据的长度上限方面,SHA-1 为2的64次方-1比特,SHA-2 为 2的128次方-1比特,而 SHA-3 则没有长度限制。
此外,FIPS 202 中还规定了两个可输出任意长度散列值的函数(extendable-output function,XOF),分别为 SHAICE128 和 SHAKE256。据说 SHAKE 这个名字取自 Secure Hash Algorithm 与Keccak 这几个单词。
Keccak 采用了与 SHA-1、SHA-2 完全不同的海绵结构( sponge construction )
Keccak 的海绵结构中,输入的数据在进行填充之后,要经过吸收阶段( absorbing phase )和挤出阶段( squeezing phase ),最终生成输出的散列值。
暴力破解:利用文件的冗余性生成具有相同散列值的另一个文件(给定消息和该消息的散列值,找具有相同散列值的一个消息【弱抗碰撞性】)
找出具有指定散列值的消息的攻击分为两种,即 “原像攻击” 和 “第二原像攻击”。原像攻击( Pre-Image Attack ) 是指给定一个散列值,找出具有该散列值的任意消息;第二原像攻击( Second Pre-Image Attack )是指给定一条消息 1, 找出另外一条消息 2, 消息 2 的散列值和消息1 相同。
生日攻击:找两个相同散列值的消息
Mallory 所进行的攻击不是寻找生成特定散列值的消息,而是要找到散列值相同的两条消息,而散列值则可以是任意值。这样的攻击,一般称为生日攻击( birthday attack )或者冲突攻击( collision attack ),这是一种试图破解单向散列函数的 “强抗碰撞性” 的攻击。
单向散列函数能够辨别出 “篡改”,能够确认消息的完整性,但无法辨别出 "伪装”。消息认证码能够向通信对象保证消息没有被
篡改,而数字签名不仅能够向通信对象保证消息没有被篡改,还能够向所有第三方做出这样的保证。
认证需要使用密钥,也就是通过对消息附加 Alice 的密钥( 只有 Alice 才知道的秘密信息 )来确保消息真的属于 Alice。
消息认证码( Message Authentication Code )是一种确认完整性并进行认证的技术,取三个单词的首字母,简称为 MAC。
消息认证码的输人包括任意长度的消息和一个发送者与接收者之间共享的密钥,它可以输出固定长度的数据,这个数据称为 MAC 值。
消息认证码是一种与密钥相关联的单向散列函数。
发送者和接收者需要共享密钥,这一点和我们在第 3 章中介绍的对称密码很相似。实际上,对称密码的密钥配送问题在消息认证码中也同样会发生。要解决密钥配送问题,我们需要像对称密码一样使用一些共享密钥的方法,例如公钥密码、Diffie-Hellman 密钥交换、密钥分配中心,或者使用其他安全的方式发送密钥等。
SWIFT 的全称是 Society for Worldwide Interbank Financial Telecommunication( 环球银行金融电信协会 ),是于 1973 年成立的一个组织,其目的是为国际银行间的交易保驾护航。
银行和银行之间是通过 SWIFT 来传递交易消息的 。而为了确认消息的完整性以及对消息进行验证,SWIFT 中使用了消息认证码。
IPsec 是对互联网基本通信协议——IP 协议( Internet Protocol )增加安全性的一种方式。在IPsec中,对通信内容的认证和完整性校验都是采用消息认证码来完成的。
SSL/TLS 是我们在网上购物等场景中所使用的通信协议。SSL/TLS 中对通信内容的认证和完整性校验也使用了消息认证码。
使用 SHA-2 之类的单向散列函数可以实现消息认证码,其中一种实现方法称为 HMAC。
使用 AES 之类的分组密码可以实现消息认证码。
将分组密码的密钥作为消息认证码的共享密钥来使用,并用 CBC 模式( 第 4 章 )将消息全部加密。此时,初始化向量( IV )是固定的。由于消息认证码中不需要解密,因此将除最后一个分组以外的密文部分全部丢弃,而将最后一个分组用作 MAC 值。由于 CBC 模式的最后一个分组会受到整个消息以及密钥的双重影响,因此可以将它用作消息认证码。
使用流密码和公钥密码等也可以实现消息认证码。
认证加密是一种将对称密码与消息认证码相结合,同时满足机密性、完整性和认证三大功能的机制。
有一种认证加密方式叫作 Encrypt-then-MAC , 这种方式是先用对称密码将明文加密,然后计算密文的 MAC 值。在 Encrypt-then-MAC 方式中,消息认证码的输人消息是密文. 通过 MAC值就可以判断 “这段密文的确是由知道明文和密钥的人生成的”。使用这一机制,我们可以防止攻击者 Mallory 通过发送任意伪造的密文,并让服务器解密来套取信息的攻击。
除了 Encrypt-then-MAC 之外,还有其他一些认证加密方式,如 Encrypt-and-MAC ( 将明文用对称密码加密,并对明文计算 MAC 值 )和 MAC-then-Encrypt ( 先计算明文的 MAC 值,然后将明文和 MAC 值同时用对称密码加密 )。
HMAC 是一种使用单向散列函数来构造消息认证码的方法( RFC2104 ),其中 HMAC 的 H就是 Hash 的意思。
具体步骤如下:
密钥填充
如果密钥比单向散列函数的分组长度要短,就需要在末尾填充 0, 直到其长度达到单向散列函数的分组长度为止。
如果密钥比分组长度要长,则要用单向散列函数求出密钥的散列值,然后将这个散列值用作 HMAC 的密钥。
填充后的密钥与ipad的XOR
将填充后的密钥与被称为ipad的比特序列进行XOR运算。ipad是将00110110这一比特序列(即16进制的36)不断循环反复直到达到分组长度所形成的比特序列,其中ipad的i是inner(内部)的意思。
XOR 运算所得到的值,就是一个和单向散列函数的分组长度相同,且和密钥相关的比特序列。这里我们将这个比特序列称为 ipadkey。
与消息组合
随后,将ipadkey与消息进行组合,也就是将和密钥相关的比特序列(ipadkey)附加在消息的开头。
计算散列值
将3中的结果输入单向散列函数,并计算出散列值
填充后的密钥与opad的XOR
将填充后的密钥与被称为 opad 的比特序列进行 XOR 运算。opad 是将 01011100 这一比特
序列( 即 16 进制的 5 C )不断循环反复直到达到分组长度所形成的比特序列,其中 opad 的 o 是
outer( 外部 )的意思。
X0R 运算所得到的结果也是一个和单向散列函数的分组长度相同,且和密钥相关的比特序
列。这里我们将这个比特序列称为 opadkey。
与散列值组合
将4的散列值拼在opadkey后面
计算散列值
将6的结果输入单向散列函数,并计算出散列值,即最终的MAC值。
通过上述流程我们可以看出,最后得到的 MAC 值,一定是一个和输人的消息以及密钥都相关的长度固定的比特序列。
重放攻击(replay attack):Mallory 并没有破解消息认证码,而只是将 Alice 银行的正确 MAC 值保存下来重复利用而已。
防御重放攻击的方法:
1、序号:约定每次都对发送的消息赋予一个递增的编号( 序号 ),并且在计算 MAC 值时将序号也包含在消息中。这样一来,由于 Mallory 无法计算序号递增之后的 MAC 值,因此就可以防御重放攻击。这种方法虽然有效,但是对每个通信对象都需要记录最后一个消息的序号。
2、时间戳:约定在发送消息时包含当前的时间,如果收到以前的消息,即便 MAC 值正确也将其当做
错误的消息来处理,这样就能够防御重放攻击。这种方法虽然有效,但是发送者和接收者的时钟必须一致,而且考虑到通信的延迟 ,必须在时间的判断上留下缓冲,于是多多少少还是会存在可以进行重放攻击的空间。3、nonce:在通信之前,接收者先向发送者发送一个一次性的随机数,这个随机数一般称nonce。发送者在消息中包含这个 nonce 并计算 MAC 值。由于每次通信时 nonce 的值都会发生变化,因此
无法进行重放攻击。这种方法虽然有效,但通信的数据量会有所增加。
和对单向散列函数的攻击一样,对消息认证码也可以进行暴力破解以及生日攻击。
对于消息认证码来说,应保证不能根据 MAC 值推测出通信双方所使用的密钥。如果主动攻击者 Mallory 能够从MAC 值反算出密钥,就可以进行篡改、伪装等攻击。例如 HMAC 中就是利用单向散列函数的单向性和抗碰撞性来保证无法根据 MAC 值推测出密钥的。
此外,在生成消息认证码所使用的密钥时,必须使用密码学安全的、高强度的伪随机数生成器。如果密钥是人为选定的,则会增加密钥被推测的风险。
消息认证码是对消息进行认证并确认其完整性的技术。通过使用发送者和接收者之间共享的密钥,就可以识别出是否存在伪装和篡改行为。
消息认证码可以使用单向散列函数和对称密码等技术来实现,本章中我们重点介绍了通过单向散列函数来实现的 HMAC。
消息认证码中,由于发送者和接收者共享相同的密钥,因此会产生无法对第三方证明以及无法防止否认等问题。
数字签名是一种将相当于现实世界中的盖章、签字的功能在计算机世界中进行实现的技术。使用数字签名可以识别篡改和伪装,还可以防止否认。
消息认证码之所以无法防止否认,是因为消息认证码需要在发送者 Alice 和接收者 Bob 两者之间共享同一个密钥。正是因为密钥是共享的,所以能够使用消息认证码计算出正确 MAC值的并不只有发送者 Alice, 接收者 Bob 也可以计算出正确的 MAC 值。由于 Alice 和 Bob 双方都能够计算出正确的 MAC 值,因此对于第三方来说,我们无法证明这条消息的确是由 Alice 生成的。
假设发送者 Alice 和接收者 Bob 不需要共享一个密钥,也就是说,Alice 和 Bob 各自使用不同的密钥。
我们假设 Alice 使用的密钥是一个只有 Alice 自己才知道的私钥。当 Alice 发送消息时,她用私钥生成一个 “签名”。相对地,接收者 Bob 则使用一个和 Alice 不同的密钥对签名进行验证。使用 Bob 的密钥无法根据消息生成签名,但是用 Bob 的密钥却可以对 Alice 所计算的签名进行验证,也就是说可以知道这个签名是否是通过 Alice 的密钥计算出来的。如果真有这么一种方法的话,那么不管是别篡改、伪装还是防止否认就都可以实现了吧?
实际上,这种看似很神奇的技术早就已经问世了,这就是数字签名( digital signature )
我们经常会在邮件的末尾附上一段文字来表明自己的名字和所在的公司等信息,这些文字也称为 “签名”。邮件的签名和本章所说的数字签名(签名)是完全不同的两码事。在邮件末尾添加的签名是一串固定的文字,而本章所说的数字签名( 签名 )则是根据消息内容生成的一串“只有自己才能计算出来的数值”,因此数字签名( 签名 )的内容是随消息的改变而改变的。
数字签名技术中,出现了下面两种行为
Alice 使用 “签名密钥” 来生成消息的签名,而 Bob 和 Victor 则使用 “验证密钥” 来验证消息的签名。**数字签名对签名密钥和验证密钥进行了区分,使用验证密钥是无法生成签名的。**这一点非常重要。此外,签名密钥只能由签名的人持有,而验证密钥则是任何需要验证签名的人都可以持有。
数字签名中也同样会使用公钥和私钥组成的密钥对,不过这两个密钥的用法和公钥密码是相反的,即用私钥加密相当于生成签名,而用公钥解密则相当于验证签名 。
直接对消息签名的方法需要对整个消息加密,非常耗时,因为公钥密码算法本来就慢。于是我们不必再对整个消息进行加密( 即对消息签名 ),而是只要先用单向散列函数求出消息的散列值,然后再将散列值进行加密( 对散列值签名 )就可以了。无论消息有多长,散列值永远都是这么短,因此对其进行加密( 签名 )是非常轻松的。
这里所使用的 D 和 N就是签名者的私钥。签名就是对消息的D次方求 mod N的结果,也就是说将消息和自己相乘D次,然后再除以N求余数,最后求得的余数就是签名。
这里所使用的 E 和 N 就是签名者的公钥。接收者计算签名的E次方并求 mod N,得到 “由签名求得的消息”,并将其与发送者直接发送过来的 “消息” 内容进行对比。如果两者一致则签名验证成功,否则签名验证失败。
ElGamal、DSA、ECDSA 和 Rabin 这几种方式。
对数字签名的中间人攻击,具体来说就是主动攻击者 Mallory 介人发送者和接收者的中间,对发送者伪装成接收者,对接收者伪装成发送者,从而能够在无需破解数字签名算法的前提下完成攻击。
实际上,涉及公钥密码的软件都可以显示公钥的散列值,这个散列值称为指纹( fingerprint )。指纹的内容就是像下面这样的一串字节序列。
数字签名中所使用的单向散列函数必须具有抗碰撞性,否则攻击者就可以生成另外一条不同的消息,使其与签名所绑定的消息具有相同的散列值。
在混合密码系统中,消息本身是用对称密码加密的,而只有对称密码的密钥是用公钥密码加密的,即在这里对称密码的密钥就相当于消息。
另一方面,数字签名中也使用了同样的方法,即将消息本身输入单向散列函数求散列值,然后再对散列值进行签名,在这里散列值就相当于消息。
如果将两者的特点总结成简短的标语,我们可以说:对称密码的密钥是机密性的精华,单向散列函数的散列值是完整性的精华。
用数字签名既可以识别出篡改和伪装,还可以防止否认。也就是说,我们同时实现了确认消息的完整性 、进行认证以及否认防止。现代社会中的计算机通信从这一技术中获益匪浅。
然而,要正确使用数字签名,有一个大前提,那就是用于验证签名的公钥必须属于真正的发送者。即便数字签名算法再强大,如果你得到的公钥是伪造的,那么数字签名也会完全失效。
现在我们发现自己陷人了一个死循环——数字签名是用来识别消息篡改 、 伪装以及否认的,但是为此我们又必须从没有被伪装的发送者得到没有被篡改的公钥才行。
为了能够确认自己得到的公钥是否合法,我们需要使用证书。所谓证书,就是将公钥当作一条消息,由一个可信的第三方对其签名后所得到的公钥。
我们需要让公钥以及数字签名技术成为一种社会性的基础设施,即公钥基础设施( Public Key Infrastructure ), 简称 PKI。
通过数字签名我们可以识別篡改和伪装,还可以防止否认。数字签名是一种非常重要的认证技术,但前提是用于验证签名的发送者的公钥没有被伪造。要确认公钥是否合法,可以对公钥施加数字签名,这就是证书。
公钥证书( Public-Key Certificate, PKC )其实和驾照很相似,里面记有姓名 、组织、邮箱地址等个人信息,以及属于此人的公钥,并由认证机构( Certification Authority、Certifying Authority, CA ) 施加数字签名。只要看到公钥证书,我们就可以知道认证机构认定该公钥的确属于此人。公钥证书&简称为证书( certificate )。
Bob生成密钥对
要使用公钥密码进行通信,首先需要生成密钥对。Bob 生成了一对公钥和私钥,并将私钥自行妥善保管。
在这里,密钥对是由 Bob 自己生成的,也可以由认证机构代为生成。
Bob在认证机构Trent(trust,可信的第三方)注册自己的公钥
在第5章中,Bob 直接将自己的公钥发给了 Alice (5.4.3 节),但是在这里 Bob 则将公钥发送给了认证机构 Trent, 这是因为 Bob 需要请认证机构 Trent 对他的公钥加上数字签名(也就是生成证书)。
Trent 收到 Bob 的公钥后,会确认所收到的公钥是否为 Bob 本人所有( 参见专栏 “身份确认和认证业务准则” )。
认证机构 Trent 用自己的私钥对 Bob 的公钥施加数字签名并生成证书
Trent 对 Bob 的公钥加上数字签名。为了生成数字签名,需要 Trent 自身的私钥,因此 Trent需要事先生成好密钥对。
Alice 得到带有认证机构 Trent 的数字签名的 Bob 的公钥( 证书 )
现在 Alice 需要向 Bob 发送密文,因此她从 Trent 处获取证书。证书中包含了 Bob 的公钥,并带有 Trent 对该公钥签署的数字签名。
Alice 使用认证机构 Trent 的公钥验证数字签名,确认 Bob 的公钥的合法性
Alice 使用认证机构 Trent 的公钥对证书中的数字签名进行验证。如果验证成功,就相当于确认了证书中所包含的公钥的确是属于 Bob 的。到这里,Alice 就得到了合法的 Bob 的公钥。
Alice 用 Bob 的公钥加密消息并发送给 Bob
Alice 用 Bob 的公钥加密要发送的消息,并将消息发送给 Bob。尽管这里写的是 “用公钥加密”,但使用第 6 章中介绍的混合密码系统来加密也是可以的。
Bob 用自己的私钥解密密文得到 Alice 的消息
Bob 收到 Alice 发送的密文,然后用自己的私钥解密,这样就能够看到 Alice 的消息了。
公钥基础设施( Public-Key Infrastructure )是为了能够更有效地运用公钥而制定的一系列规范和规格的总称。公钥基础设施一般根据其英语缩写而简称为 PKI。
不过,由于 PKI 中用户和认证机构不仅限于 “人"( 也有可能是计算机 ),因此我们可以给他们起一个特殊的名字,叫作实体( entitiy )。实体就是进行证书和密钥相关处理的行为主体。
用户就是像 Alice、Bob 这样使用 PKI 的人。用户包括两种:一种是希望使用 PKI 注册自己的公钥的人,另一种是希望使用已注册的公钥的人。我们来具体看一下这两种用户所要进行的操作。
【 注册公钥的用户所进行的操作 】
• 生成密钥对( 也可以由认证机构生成 )
• 在认证机构注册公钥
• 向认证机构申请证书
• 根据需要申请作废已注册的公钥
• 解密接收到的密文
• 对消息进行数字签名
【 使用已注册公钥的用户所进行的操作 】
• 将消息加密后发送给接收者
• 验证数字签名
认证机构( Certification Authority, CA )是对证书进行管理的人。在本章开头的例子中,我们给它起了一个名字叫作 Trent。认证机构具体所进行的操作如下。
• 生成密钥对( 也可以由用户生成 )
• 在注册公钥时对本人身份进行认证
• 生成并颁发证书
• 作废证书
认证机构的工作中,公钥注册和本人身份认证这一部分可以由注册机构( Registration Authority, RA )来分担。这样一来,认证机构就可以将精力集中到颁发证书上,从而减轻了认证机构的负担。不过,引入注册机构也有弊端,比如说认证机构需要对注册机构本身进行认证,而且随着组成要素的增加,沟通过程也会变得复杂,容易遭受攻击的点也会增加。
仓库( repository )是一个保存证书的数据库,PKJ 用户在需要的时候可以从中获取证书,它的作用有点像打电话时用的电话本。Alice 获取 Bob的证书时,就可以使用仓库。仓库也叫作证书目录。
在使用对称密码、公钥密码、消息认证码、数字签名等密码技术使用,都需要一个称为密钥( key )的巨大数字。然而,数宇本身的大小并不重要,重要的是密钥空间的大小,也就是可能出现的密钥的总数量,因为密钥空间越大,进行暴力破解就越困难。密钥空间的大小是由密钥长度决定的。
之所以在长度前面加上 “实质” 一词,是因为在 DES 和三重 DES 的密钥中附加了一些用于识别通信错误的校验比特。由于这些校验比特对实际的密钥空间没有影响,因此我们在本节中所说的密胡长度不包含校验比特。
依靠隐藏密码算法本身的设计来确保信息的机密性是非常危险的。如果需要一个高强度的密码算法,不应该自行开发,而是应该使用一个经过全世界密码学家共同验证的密码算法。
信息的机密性不应该依赖于密码算法本身,而是应该依赖于妥善保管的密钥。这是密码世界的常识之一。
在对称密码中,加密和解密使用同一个密钥。由于发送者和接收者之间需要共享密钥,因此对称密码又称为共享密钥密码。对称密码中所使用的密钥必须对发送者和接收者以外的人保密,否则第三方就能够解密密文了。
在公钥密码中,加密和解密使用的是不同的密钥。用于加密的密钥称为公钥,顾名思义它是可以被公开的;用于解密的密钥称为私钥,只有需要进行解密的接收者才持有私钥,私钥也称为秘密密钥。相对应的公钥和私钥之间具有深刻的数学关系,因此也称为密钥对。
在消息认证码中,发送者和接收者使用共享的密钥来进行认证。消息认证码只能由持有合法密钥的人计算出来。将消息认证码附加在通信报文后面,就可以识别通信内容是否被篡改或伪装。由于 “持有合法的密钥” 就是发送者和接收者合法身份的证明,因此消息认证码的密钥必须对发送者和接收者以外的人保密,否则就会产生篡改和伪装的风险。
在数字签名中,签名的生成和验证使用不同的密钥。只有持有私钥的本人才能够生成签名,但由于验证签名使用的是公钥,因此任何人都能够验证签名。
对称密码和公钥密码的密钥都是用于确保机密性的密钥。如果不知道用于解密的合法密钥,就无法得知明文的内容。
相对地,消息认证码和数字签名所使用的密钥,则是用于认证的密钥。如果不知道合法的密钥,就无法篡改数据,也无法伪装本人的身份。
当我们访问以 https:// 开头的网页时,Web 服务器和浏览器之间会进行基于 SSL/TLS 的加密通信。在这样的通信中所使用的密钥是仅限于本次通信的一次性密钥,下次通信时就不能使用了。像这样每次通信只能使用一次的密钥称为会话密钥( session key )。
相对于每次通信都史换的会话密钥,一直被重复使用的密钥称为主密钥( master key )。
一般来说,加密的对象是用户直接使用的信息( 内容 ),这样的情况下所使用的密钥称为CEK ( Contents Encrypting Key, 内容加密密钥 );相对地,用于加密密钥的密钥则称为 KEK( Key Encrypting Key,密钥加密密钥 )。
生成密钥的最好方法就是使用随机数,因为密钥需要具备不易被他人推测的性质。在可能的情况下最好使用能够生成密码学上的随机数的硬件设备,但一般我们都是使用伪随机数生成器这一专门为密码学用途设计的软件。
尽管生成伪随机数的算法有很多种,但密码学用途的伪随机数生成器必须是专门针对密码学用途而设计的。
有时我们也会使用人类可以记住的口令( password 或 passphrase )来生成密钥。passphrase指的是一种由多个单词组成的较长的 password,在本章中我们将两者统称为口令。
严格来说,我们很少直接用口令来作为密钥使用,一般都是将口令输人单向散列函数,然后将得到的散列值作为密钥使用。
在使用口令生成密钥时,为了防止字典攻击,需要在口令上面附加一串称为盐( salt ) 的随机数,然后再将其输人单向散列函数。这种方法称为 “基于口令的密码”( Password Based Encryption, PBE )
在使用对称密码时,如何在发送者和接收者之间共享密钥是一个重要的问题( 即密钥配送问题 )。要解决密钥配送问题,可以采用事先共享密钥 、使用密钥分配中心、使用公钥密码等方法。除上述方法之外,还有一种解决密钥配送问题的方法称为 Diffie-Hellman 密钥交换。
有一种提高通信机密性的技术被称为密钥更新( key updating ), 这种方法就是在使用共享密钥进行通信的过程中,定期( 例如每发送 1000 个字 )改变密钥。当然,发送者和接收者必须同时用同样的方法来改变密钥才行。
在更新密钥时,发送者和接收者使用单向散列函数计算当前密钥的散列值,并将这个散列值用作新的密钥。简单说,就是用当前密钥的散列值作为下一个密钥。
我们假设在通信过程中的某个时间点上,密钥被窃听者获取了,那么窃听者就可以用这个密钥将之后的通信内容全部解密。但是,窃听者却无法解密更新密钥这个时间点之前的通信内容,因为这需要用单向散列函数的输出( 即当前密钥 )反算出单向散列函数的输人( 即上一个密钥 )。由于单向散列函数具有单向性,因此就保证了这样的反算是非常困难的。
这种防止破译过去的通信内容的机制,称为后向安全( backward security )。
由于会话密钥在通信过程中仅限使用一次,因此我们不需要保存这种密钥。然而,当密钥需要重复使用时, 就必须要考虑保存密钥的问题了。
密钥的作废和生成是同等重要的,这是因为密钥和明文是等价的。
解决密钥配送问题的方法—Diffie-Hellman 密钥交换。
Diffie-Hellman 密钥交换( Diffie-Hellman key exchange )是 1976 年由 Whitfield Diffie 和Martin Heilman 共同发明的一种算法。使用这种算法,通信双方仅通过交换一些可以公开的信息就能够生成出共享的秘密数字,而这一秘密数字就可以被用作对称密码的密钥。IPsec 中就使用了经过改良的 Diffie-Hellman 密钥交换。
虽然这种方法的名字叫 “密钥交换”,但实际上双方并没有真正交换密钥,而是通过计算生成出了一个相同的共享秘钥。因此,这种方法也称为 Diffie-Hellman 密钥协商( Diffie-Hellmankey agreement )。
Alice 向Bob发送两个质数P、G
P必须是一个非常大的质数,而 G 则是一个和尸相关的数,称为生成元( generator )。G 可以是一个较小的数字。
P和 G 不需要保密,被窃听者 Eve 获取也没关系。
此外,P 和 G 可以由 Alice 和 Bob 中的任意一方生成。
Alice 生成一个随机数A
A是一个1 ~ P-2 之间的整数。这个数是一个只有 Alice 知道的秘密数字,没有必要告诉Bob, 也不能让 Eve 知道。
Bob 生成一个随机数B
B是一个 1~ P-2 之间的整数。这个数是一个只有 Bob 知道的秘密数字,没有必要告诉Alice, 也不能让 Eve 知道
Alice将G的A次方modP这个数发给Bob
这个数让 Eve 知道也没关系。
Bob将G的B次方modP这个数发给Alice
这个数让 Eve 知道也没关系。
Alice用Bob发过来的数计算A次方并求mod P
Bob 用 Alice 发过来的数计算B次方并求 mod P
基于口令的密码( Password Based Encryption, PBE )就是一种根据口令生成密钥并用该密钥进行加密的方法。其中加密和解密使用同一个密钥。
PBE 加密包括下列 3 个步骤:
(1)生成 KEK; (2)生成会话密钥并加密;(3)加密消息。
(1)生成KEK(Key Encrypting Key)
首先,伪随机数生成器会生成一个被称为盐的随机数。将盐和 Alice 输入的口令一起输入单向散列函数,得到的散列值就是用来加密密钥的密钥( KEK )。
正如在饭菜上撒盐会改变饭菜的味道一样,在口令上加上盐就会改变所生成的 KEK 值。盐是一种用于防御字典攻击的机制。
(2)生成会话密钥并加密
接下来,我们使用伪随机数生成器生成会话密钥。会话密钥是用来加密消息的密钥( CEK )。
会话密钥需要用刚才步骤(1)中生成的 KEK 进行加密,并和盐一起保存在安全的地方。会话密钥加密之后,KEK 就会被丢弃,因为 KEK 没有必要保存下来,只要通过盐和口令就可以重建 KEK。
(3)加密消息
最后,我们用步骤(2)中生成的会话密钥对消息进行加密。
PBE 加密后所产生的输出包括下列 3 种。
PBE 解密包括下列 3 个步骤。
(1)重建 KEK;(2)解密会话密钥;(3)解密消息。
(1)重建KEK
首先我们将之前保存下来的盐,和 Alice 输人的口令一起输人单向散列函数。这个计算过程和生成 KEK 时的计算过程是一样的,因此所得到的散列值就是 KEK。
(2)解密会话密钥
然后,我们获取之前保存下来的 “用KEK 加密的会话密钥”,用步骤(1)中恢复的 KEK 进行解密。这一步我们可以得到会话密钥。
(3)解密消息
最后,我们用步骤(2)中重建的会话密钥对加密的消息进行解密。
将上述解密过程与图 11-8 中 “PBE 加密” 的过程对比一下就会发现,在 PBE 加密过程中使用了两次伪随机数生成器,而在 PBE 解密过程中却一次都没有使用。
盐是由伪随机数生成器生成的随机数,在生成密钥(KEK )时会和口令一起被输人单向散列函数。
盐是用来防御字典攻击的。字典攻击是一种事先进行计算并准备好候选密钥列表的方法。
在这里,事先是很重要的一点。这意味着 Mallory 可以在窃取到加密的会话密钥之前,就准备好了大量的候选 KEK。当 Mallory 窃取加密的会话密钥后,就需要尝试将它解密,这时只要利用事先生成的候选 KEK, 就能够大幅缩短尝试的时间,这就是字典攻击( dictionary attack )。
如果在生成 KEK 时加盐,则盐的长度越大,候选 KEK 的数量也会随之增大,事先生成候选 KEK 就会变得非常闲难。只要 Mallory 还没有得到盐,就无法生成候选 KEK。这是因为加盐之后,候选 KEK 的数量会变得非常巨大。
在 PBE 中,我们通过口令生成密钥( KEK ), 再用这个密钥来加密会话密钥( CEK )。由于通过口令生成的密钥( KEK )强度不如由伪随机数生成器生成的会话密钥( CEK ), 这就好像是将一个牢固的保险柜的钥匙放在了一个不怎么牢固的保险柜中保管,因此在使用基于口令的密码( PBE )时,需要将盐和加密后的 CEK 通过物理方式进行保护。
将单向散列函数进行多次迭代的方法称为拉伸( stretching )。
在生成口令时,使用只有自己才能知道的信息是一个大原则。因为口令不能够被别人推测出来,因此使用只有自己才能知道的信息是理所当然的。然而,在实际生成口令的时候,人们却很容易忘记这一原则。
不要将同一个口令重复用于各种不同的用途,而是应该根据信息的价值区分使用不同的口令。例如,用来在网站上阅读新闻的口令,和用来加密包含 1000 个客户信息的重要文件的口令显然不能用同一个。
应该将笔记与物理的钥匙同等对待。仅将口令的一部分写下来的方法也是非常有效的。
口令是有局限性的。为了说明起来更简单,我们假设口令只能由 8 个字符的英文字母和数字组成。
也就是大约 218 万亿种。这个数字虽然不小,但实际上只不过相当于一个长度为 48 比特的密钥而已。这个长度的密钥是可以通过暴力破解的。如果攻击者的计算机每秒可以生成并尝试1 亿个口令,则只需要 25 天就可以遍历所有的口令了。
实际上,现如今靠人自己来生成和管理口令可以说是非常困难的。为了解决人类难以直接管理口令这个现实问题,就需要一些能够帮助我们生成和管理口令的工具。
这些工具通过随机数来生成难以推测的口令,并能够与浏览器联动在网站上自动输人相应的口令。
不过,我们也必须提防这些工具擅自盗用用户的口令,也就是说,这些工具及其开发者是否 “可信” 是非常重要的。
密钥就是密码技术的钥匙。密钥这样一个很短的比特序列可以确保重要信息的机密性,还可以证明你的身份。
密码技术中所使用的随机数,仅仅具备随机性是不够的,至少还需要具备不可预测性才行。
具备不可预测性的随机数,一定具备随机性。具备不可重现性的随机数,也一定具备随机性和不可预测性。
为了方便起见,我们将上述三个性质按顺序分别命名为 “弱伪随机数” 、“强伪随机数” 和 “真随机数”
如果伪随机数列中不存在统计学偏差,则我们可以认为这个伪随机数列是随机的。判断一个伪随机数列是否随机的方法称为随机数测试,随机数测试的方法有很多种。
由于随机数会被用来生成密钥,因此**密钥不能被攻击者看穿。**但是,杂乱无章并不代表不会被看穿’
因此本书中将只具备随机性的伪随机数称为 “弱伪随机数”。
密码中所使用的随机数仅仅具备随机性是不够的,还需要具备避免被攻击者看穿的不可预测性(unpredictability)。
所谓不可预测性,是指攻击者在知道过去生成的伪随机数列的前提下,依然无法预测出下—个生成出来的伪随机数的性质。其中,“在知道过去生成的伪随机数列的前提下……” 是非常重要的一点。
现在我们假设攻击者已经知道伪随机数生成器的算法。此外,正如攻击者不知道密钥一样,他也不知道伪随机数的种子(伪随机教的种子是一个用于生成伪随机数的初始值)。伪随机数生成器的算法是公开的,但伪随机数的种子是保密的。在上述假设的前提下,即便攻击者知道过去所生成的伪随机数列,他也预测出下一个生成出来的伪随机数—这就是不可预测性。
不可预测性是通过使用其他的密码技术来实现的。例如,可以通过单向散列函数的单向性和密码的机密性来保证伪随机数生成器的不可预测性。
所谓不可重现性,是指无法重现和某一随机数列完全相同的数列的性质。如果除了将随机数列本身保存下来以外,没有其他方法能够重现该数列,则我们就说该随机数列具备不可重现性。
仅靠软件是无法生成出具备不可重现性的随机数列的。软件只能生成伪随机数列,这是因为运行软件的计算机本身仅具备有限的内部状态。而在内部状态相同的条件下,软件必然只能生成相同的数,因此软件所生成的数列在某个时刻一定会出现重复。首次出现重复之前的数列长度称为周期,对于软件所生成的数列,其周期必定是有限的。当然,这个周期可能会很长,但总归还是有限的。凡是具有周期的数列,都不具备不可重现性。
要生成具备不可重现性的随机数列,需要从不可重现的物理现象中获取信息,比如周围的温度和声音的变化、 用户移动的鼠标的位置信息、键盘输人的时间间隔 、 放射线测量仪的输出值等,根据从这些硬件中所获取的信息而生成的数列,一般可以认为是具备不可重现性的随机数列。
随机数可以通过硬件来生成,也可以通过软件来生成。
通过硬件生成的随机数列,是根据传感器收集的热量、声音的变化等事实上无法预测和重现的自然现象信息来生成的。像这样的硬件设备就称为随机数生成器( Random Number Generator, RNG )。
而可以生成随机数的软件则称为伪随机数生成器( Pseudo Random Number Generator,PRNG )。因为仅靠软件无法生成真随机数,因此要加上一个 “伪” 字。
伪随机数生成器具有 “内部状态”,并根据外部输人的 “种子” 来生成伪随机数列
伪随机数生成器的内部状态,是指伪随机数生成器所管理的内存中的数值。当有人对伪随机数生成器发出 “给我一个伪随机数” 的请求时,伪随机数生成器会根据内存中的数值( 内部状态 )进行计算,并将计算的结果作为伪随机数输出。随后,为了响应下一个伪随机数请求,伪随机数生成器会改变自己的内部状态。因此,将根据内部状态计算伪随机数的方法和改变内部状态的方法组合起来,就是伪随机数生成的算法。
由于内部状态决定了下一个生成的伪随机数,因此内部状态不能被攻击者知道。
为了生成伪随机数,伪随机数生成器需要被称为种子( seed )的信息。伪随机数的种子是用来对伪随机数生成器的内部状态进行初始化的。
伪随机数的种子是一串随机的比特序列,根据种子就可以生成出专属于自己的伪随机数列。伪随机数生成器是公开的,但种子是需要自己保密的,这就好像密码算法是公开的,但密钥只能自己保密。由于种子不可以被攻击者知道,因此不可以使用容易被预测的值,例如不可以用当前时间作为种子。
如果只是把算法搞得复杂,那么该算法是无法用于密码技术的。
其中一个原因就是周期太短。使用复杂算法所生成的数列大多数都会具有很短的周期( 即短数列的不断重复 )。由于密码技术中使用的伪随机数必须具备不可预测性,因此周期短是不行的。
另一个原因是,如果程序员不能够理解算法的详细内容,那么就无法判断所生成的随机数是否具备不可预测性。
线性同余法( linear congruential method )是一种使用很广泛的伪随机数生成器算法。然而,它并不能用于密码技术。
简而言之,线性同余法就是将当前的伪随机数值乘以A 再加上 C, 然后将除以M得到的余数作为下一个伪随机数。在线性同余法中,最近一次生成的伪随机数的值就是内部状态,伪随机数的种子被用来对内部状态进行初始化。
在线性同余法中,只要谨慎选择A、C 和M的值,就能够很容易地生成具备随机性的伪随机数列。然而,线性同余法不具备不可预测性,因此不可以将线性同余法用于密码技术。
使用单向散列函数( 如 SHA-1 )可以编写出能够生成具备不可预测性的伪随机数列( 即强伪随机数 )的伪随机数生成器。
攻击者要预测下一个伪随机数,需要知道计数器的当前值。请大家注意,这里输出的伪随机数列实际上相当于单向散列函数的散列值。也就是说,要想知道计数器的值,就需要破解单向散列函数的单向性,这是非常困难的,因此攻击者无法预测下一个伪随机数。总而言之,在这种伪随机数生成器中,单向散列函数的单向性是支撑伪随机数生成器不可预测性的基础。
我们可以使用密码来编写能够生成强伪随机数的伪随机数生成器。既可以使用AES 等对称密码,也可以使用 RSA 等公钥密码。
攻击者要预测下一个伪随机数,需要知道计数器的当前值。然而,由于之前所输出的伪随机数列相当于密文,因此要知道计数器的值,就需要破译密码,这是非常困难的,因此攻击者无法预测出下一个伪随机数。总而言之,在这种伪随机数生成器中,密码的机密性是支撑伪随机数生成器不可预测性的基础。
实现伪随机数生成器的步骤如下:
(1) 初始化内部状态。
(2) 将当前时间加密生成掩码。
(3) 对内部状态与掩码求 XOR。
(4) 将步骤(3)的结果进行加密。
(5) 将步骤(4)的结果作为伪随机数输出。
(6) 对步骤(4)的结果与掩码求 XOR
(7) 将步骤(6)的结果加密。
(8) 将步骤(7)的结果作为新的内部状态。
(9) 重复步骤(2) ~(8)直到得到所需数量的伪随机数。
在步骤(2)中,我们将当前时间进行加密生成了一个掩码。当前时间是可以被攻击者预测出来的,但是由于攻击者不知道加密密钥,因此他无法预测加密后的当前时间( 即掩码 )。在之后的步骤(3)和步骤(6)中,我们将使用掩码对比特序列进行随机翻转。
步骤(3)~(5)的作用是输出伪随机数。这里输出的伪随机数是将内部状态与掩码的 XOR 进行加密之后的结果。那么,攻击者是否能通过将伪随机数进行反算来看穿内部状态与掩码的XOR 呢?不能,因为要看穿这个值, 攻击者必须要破解密码。因此,根据过去输出的伪随机数列,攻击者无法推测出伪随机数生成器的内部状态。
步骤(6) ~(8)的作用是更新内部状态。新的内部状态是将上一个伪随机数与掩码的 XOR 进行加密之后的结果。那么,攻击者是否能够从伪随机数推测出新的内部状态呢?不能,因为要算出新的内部状态,只知道上一个伪随机数是不够的,还必须知道掩码以及加密密钥才行。
一个随机数算法再优秀,如果它不具备不可预测性,那么就不能用于密码学和安全相关用途。
和密码相比,伪随机数生成器实在是很少被人们所注意,因此我们很容易忘记伪随机数生成器也是可以受到攻击的。然而,由于伪随机数生成器承担了生成密钥的重任,因此它经常成为攻击的对象。
伪随机数的种子和密码的密钥同等重要。如果攻击者知道了伪随机数的种子,那么他就能够知道这个伪随机数生成器所生成的全部伪随机数列。因此,伪随机数的种子不可以被攻击者知道。
要避免种子被攻击者知道,我们需要使用具备不可重现性的真随机数作为种子。
当然,我们一般不会到了需要的时候才当场生成真随机数,而是会事先在一个名为随机数池( random pool )的文件中积累随机比特序列。当密码软件需要伪随机数的种子时,可以从这个随机数池中取出所需长度的随机比特序列来使用。
随机数池的内容不可以被攻击者知道,否则伪随机数的种子就有可能被预测出来。
随机数池本身并不储存任何有意义的信息。我们需要保护没有任何意义的比特序列,这一点有点违背常识,但其实却是非常重要的。
(1) 用伪随机数生成器生成会话密钥。
(2) 用公钥密码加密会话密钥,这里使用的密钥是接收者的公钥。
(3) 压缩消息。
(4) 使用对称密码对压缩的消息进行加密,这里使用的密钥是步骤(1) 中生成的会话密钥。
(5) 将加密的会话密钥( 步骤 (2) )与加密的消息( 步骤 (4) )拼合起来。
(6) 将步骤 (5) 的结果转换为文本数据,转换后的结果就是报文数据。
正如下图所示,用公钥密码加密会话密钥,用对称密码加密消息就是混合密码系统的特点。
PGP 的私钥是保存在用户的钥匙串中的。为了防止钥匙串被盗,私钥都是以加密状态保存的,并在保存时使用了基于口令的密码( PBE )。口令是由多个单词组成的短语,没有正确的口令就无法使用相应的私钥。如果攻击者想要使用你的私钥,就必须先窃取保存私钥的钥匙串,然后再破泽加密私钥的密码。
解密私钥的步骤如下:
(1) 接收者输入解密的口令。
(2) 求口令的散列值,生成用于解密私钥的密钥。
(3) 将钥匙串中经过加密的私钥进行解密。
(4) 将报文数据( 文本数据 )转换成二进制数据。
(5) 将二进制数据分解成两部分:加密的会话密钥、经过压缩和加密的消息。
(6) 用公钥密码解密会话密钥,这里使用步骤(3)中生成的接收者的私钥。
(7) 对步骤(5)中得到的经过压缩和加密的消息用对称密码进行解密。这里使用步骤(6)中
生成的会话密钥。
(8) 对步骤(7)中得到的经过压缩的消息进行解压缩。
(9) 得到原始消息。
在钥匙串中,私钥是通过口令进行加密保存的,因此不知道口令的人就无法使用相应的私钥。
(1) 发送者输入签名用的口令。
(2) 求口令的散列值,生成用于解密私钥的密钥。
(3) 将钥匙串中经过加密的私钥进行解密。
(4) 用单向散列函数计算消息的散列值。
(5) 对步骤(4)中得到的散列值进行签名。这一步相当于使用步骤(3)中得到的私钥进行加密。
(6) 将步骤(5)中生成的数字签名与消息进行拼合。
(7) 将步骤(6)的结果进行压缩。
(8) 将步骤(7)的结果转换为文本数据。
(9) 步骤(8)的结果就是报文数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。