第 3 章 密码学基础¶
信息安全的主要目标是保护信息的机密性、完整性和可用性。机密性主要通过密码技术实现,而信息的完整性也直接或间接地使用了密码的相关技术,因此密码学是信息安全的基础。
长期以来,密码技术只在很小的范围内使用,如军事、外交、情报等部门。随着人类社会向信息社会的演进,基于计算复杂性的计算机密码学得到了前所未有的重视并迅速普及和发展起来。
在国外,密码学已成为计算机网络安全领域的主要研究方向之一。
1. 密码学概述¶
密码学是研究如何隐密地传递信息的学科,其首要目的是隐藏信息的涵义。密码学涉及信息的加密/解密及密码技术在信息传递过程中的应用。
早期的密码技术的安全性基于密码算法的保密,现代的密码技术要求密码算法公开、密钥必须保密,密码算法的强度基于计算的复杂性。
著名的密码学者 Ron Rivest(RSA 密码算法的发明者之一)对密码学的解释是: “密码学是关于如何在敌人存在的环境中通讯”。
密码学的发展¶
密码技术的历史比较悠久,在四千年前,古埃及人就开始使用密码来保密传递消息。
两千多年前,恺撒就开始使用目前称为“恺撒密码”的密码系统。但是密码技术直到 20 世纪 40 年代以后才有重大突破和发展。
特别是 20 世纪 70 年代后期,由于计算机、电子通信的广泛使用,现代密码学得到了空前的发展。
密码学的相关学科¶
密码学相关学科大致可以分为三个方面:
- 密码学(Cryptology)是研究信息系统安全保密的科学;(密码(cipher)是指逐个字符或者逐位地进行变换,它不涉及信息的语言结构)
- 密码编码学(Cryptography)主要研究对信息进行编码,实现对信息的隐藏;(编码(code)则是指用一个词或符号来代替另一个词)
- 密码分析学(Cryptanalytics)主要研究加密消息的破译或消息的伪造。

目前的大部分量子保密通信技术:主要是通过量子技术实现安全的密钥分发
消息和加密¶
遵循国际命名标准,加密和解密可以翻译成:“Encipher(译成密码)”和“Decipher(解译密码)”。也可以这样命名:“Encrypt(加密)”和“Decrypt(解密)”。
- 消息被称为明文
- 用某种方法伪装消息以隐藏它的内容的过程称为加密
- 加了密的消息称为密文
- 把密文转变为原始明文的过程称为解密
明文、密文¶
- 明文用 M(Message,消息)或 P(Plaintext,明文)表示,它可能是比特流、文本文件、位图、数字化的语音流或者数字化的视频图像等。
- 密文用 C(Ciphertext)表示,也是二进制数据,有时和 M 一样大,有时稍大。通过压缩和加密的结合,C 有可能比 P 小些。
- 加密函数 E 作用于 M 得到密文 C,用数学公式表示为:$E(M)=C$
- 解密函数 D 作用于 C 产生 M,用数学公式表示为:$D(C)=M$
- 先加密、再解密,原始的明文将恢复出来,下式必须成立:$D(E(M))=M$
鉴别、完整性和抗抵赖性¶
除了提供机密性外,密码学需要提供三方面的功能:鉴别、完整性和抗抵赖性。
- 鉴别
-
消息的接收者应该能够确认消息的来源;入侵者不可能伪装成他人。
- 完整性
-
消息的接收者应该能够验证在传送过程中的消息没有被修改;入侵者不可能用假消息代替合法消息。
- 抗抵赖性
-
发送消息者事后不可能虚假地否认他发送的消息。
算法和密钥¶
现代密码学要求密码算法公开,密钥保密。
密钥用 K 表示。K 可以是很多数值里的任意值,密钥K的可能值的范围叫做密钥空间。对称密码算法加密和解密运算都使用这个密钥,即运算都依赖于密钥,并用 K 作为下标表示,加/解密函数表达为:
$$ E_K(M)=C \\ D_K(C)=M \\ D_K(E_K(M))=M $$
不同的加密密钥和解密密钥¶
有些算法使用不同的加密密钥和解密密钥,也就是说加密密钥 K1 与相应的解密密钥 K2 不同,在这种情况下,加密和解密的函数表达式为:
$$ E_{K1}(M)=C \\ D_{K2}(C)=M $$
函数必须具有的特性是:$D_{K2}(E_{K1}(M))=M$
对称算法的基本原理¶
基于密钥的算法通常有两类:对称算法和公开密钥算法(非对称算法)。对称密码算法有时又叫传统密码算法,加密密钥能够从解密密钥中推算出来,反过来也成立。
在大多数对称算法中,加/解密的密钥是相同的。对称算法要求发送者和接收者在安全通信之前,协商一个密钥。对称算法的安全性依赖于密钥的保密,泄漏密钥就意味着任何人都能对消息进行加/解密。对称算法的加密和解密表示为:
$$ E_K(M)=C \\ D_K(C)=M $$
对称密码算法的两个分支¶
- 分组密码算法:对明文的一组位进行加密和解密运算,这些位组称为分组,相应的算法称为分组算法。常见的分组算法有 DES、DES3(3DES)、IDEA、AES 等。DES 分组长度为 64 位、密钥长度为 64 位;AES 加密数据块分组长度必须为 128 比特,密钥长度可以是 128 比特、192 比特、256 比特中的任意一个(如果数据块及密钥长度不足时,会补齐)
- 序列密码(流密码)算法:一次只对明文的单个位(有时对字节)运算的算法称为序列密码算法或流密码。常见的流密码有 RC4、A5、SEAL、PIKE 等。
公开密钥算法的基本原理¶
公开密钥算法(非对称算法)的加密密钥和解密密钥不同,而且解密密钥不能根据加密密钥计算出来,或者至少在可以计算的时间内不能计算出来。
之所以叫做公开密钥算法,是因为加密密钥能够公开,即陌生者能用加密密钥加密信息,但只有用相应的解密密钥才能解密信息。加密密钥叫做公开密钥(简称公钥),解密密钥叫做私人密钥(简称私钥)。
公开密钥 K1 加密表示为:$E_{K1}(M)=C$。公开密钥和私人密钥是不同的,用相应的私人密钥 K2 解密可表示为:$D_{K2}(C)=M$。
安全协议¶
密码协议也称作安全协议,是使用密码学的协议,是以密码学为基础的消息交换协议,其目的是在网络环境中提供各种安全服务。
密码学是网络安全的基础,但网络安全不能单纯依靠安全的密码算法。安全协议是网络安全的一个重要组成部分,我们需要通过安全协议进行实体之间的认证、在实体之间安全地分配密钥或其它各种秘密、确认发送和接收的消息的不可否认性等。
常见的安全协议有:认证协议、不可否认协议、公平性协议、身份识别协议、密钥管理协议。
密码编码原则:密码算法应建立在算法的公开不影响明文和密钥的安全。
2. 对称密码技术¶
对称密码体制的加密密钥和解密密钥是相同的,其中最负盛名的是曾经广泛使用的 DES 和正在推广使用的 AES。与公开密钥密码技术相比,其最大的优势就是速度快,一般用于对大量数据的加/解密。
数据加密标准(DES,Data Encryption Standard)是一种使用密钥加密的块密码,1976 年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来。它基于使用 56 位密钥的对称算法。
DES 算法的安全性¶
DES 的 56 位密钥过短,现在已经不是一种安全的加密方法。早在 1999 年 1 月,distributed.net 与电子前哨基金会合作,就在 22 小时 15 分钟内破解了一个 DES 密钥。
随着计算机的升级换代,运算速度大幅度提高,破解 DES 密钥所需的时间也将越来越短。为了保证实用应用所需的安全性,可以使用 DES 的派生算法 3DES 来进行加密。3DES 被认为是十分安全的,虽然它的速度较慢。另一个计算代价较小的替代算法是 DES-X,它通过将数据在 DES 加密前后分别与额外的密钥信息进行异或来增加密钥长度。GDES 则是一种速度较快的 DES 变体,但它对微分密码分析较敏感。
高级加密标准(AES)¶
2000 年 10 月,在历时接近 5 年的征集和选拔之后,NIST 选择了一种新的密码,即高级加密标准(AES),用于替代 DES。2001 年 2 月 28 日,联邦公报发表了 AES 标准,从此开始了其标准化进程,并于 2001 年 11 月 26 日成为 FIPS PUB 197 标准。
AES 算法在提交的时候称为 Rijndael。选拔中其它进入决赛的算法包括 RC6、Serpent、MARS 和 Twofish。
DES 算法的原理¶
DES 是一种典型的块密码,一种将固定长度的明文通过一系列复杂的操作变成同样长度的密文的算法。
对 DES 而言,块长度为 64 位。同时,DES 使用密钥来自定义变换过程,因此只有持有加密密钥的用户才能解密密文。
DES 的密钥表面上是 64 位,然而只有其中的 56 位被实际用于算法,其余 8 位可以被用于奇偶校验,并在算法中被丢弃。因此,DES 的有效密钥长度为 56 位。
DES 算法的整体结构如图所示:

DES 算法实现加密需要三个步骤:
-
变换(置换)明文
对给定的 64 位比特的明文 $x$,首先通过一个置换 IP 表来重新排列 x,从而构造出 64 位比特的 $x_0$,$x_0=IP(x)=L_0R_0$,其中 $L_0$ 表示 $x_0$ 的前 32 比特,$R_0$ 表示 $x_0$ 的后 32 位。
-
按照规则进行 16 轮迭代,规则为
- $L_i=R_{i-1}$
- $R_i=L_i\circ F(R_{i-1},K_i)$(i=1,2,3...16)
- 经过第一步变换已经得到 $L_0$ 和 $R_0$ 的值,其中符号 ⊕ 表示的数学运算是异或,f 表示一种置换,由 S 盒置换构成,$K_i$ 是一些由密钥编排函数产生的比特块。f 和 $K_i$ 将在后面介绍。
-
对 $L_{16}R_{16}$ 利用 $IP^{-1}$ 作逆置换,就得到了密文 y。
DES 算法的详细内容请参考密码学方面的专著,其具体实现的源代码请参考 OpenSSL 源代码:
DES 的各种变种¶
由于 DES 的密钥长度仅为 56 比特,破解密文需要 256 次穷举搜索,在目前已难于保证密文的安全。
为了解决 DES 密钥长度过短的问题,可以采用组合密码技术,也就是将密码算法组合起来使用。三重 DES(简写为 DES3 或 3DES)是最常用的组合密码技术,破解密文需要 2112 次穷举搜索,其算法如图所示。

基于 DES 的其它算法还有 DESX、CRYPT(3)、GDES、RDES、更换 S 盒的 DES、使用相关密钥 S 盒的 DES 等。
3. RSA 公钥加密技术¶
公开密钥加密(public-key cryptography)也称为非对称(密钥)加密,该思想最早由 Ralph C. Merkle 在 1974 年提出。之后在 1976 年,Whitfield Diffie(迪菲)与 Martin Hellman(赫尔曼)两位学者在现代密码学的奠基论文《New Direction in Cryptography》中首次公开提出了公钥密码体制的概念。
公钥密码体制中的密钥分为加密密钥与解密密钥,这两个密钥是数学相关的,用加密密钥加密后所得的信息,只能用该用户的解密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个的秘密性质。公开的密钥称为公钥(PK),不公开的密钥称为私钥(SK)。
常见的公钥加密算法有 RSA、ElGamal、背包算法、Rabin(RSA 的特例)、Diffie-Hellman 密钥交换协议中的公钥加密算法、椭圆曲线加密算法(Elliptic Curve Cryptography,ECC)。使用最广泛的是 RSA 算法(由发明者 Rivest、Shamir 和 Adleman 姓氏首字母缩写而来),ElGamal 是另一种常用的非对称加密算法。
RSA 和 ElGamal 是同时很好地用于加密和数字签名的公开密钥算法。此类算法要求加密、解密的顺序可以交换,即满足以下公式:
$$ E_{PK}(D_{SK}(M)) = D_{SK}(E_{PK}(M)) = M $$
RSA 算法¶
RSA 算法于 1977 年由 Rivest、Shamir 和 Adleman(当时他们三人都在麻省理工学院工作)发明,是第一个既能用于数据加密也能用于数字签名的算法。RSA 算法易于理解和操作,虽然其安全性一直未能得到理论上的证明,但是它经历了各种攻击,至今未被完全攻破,所以,实际上是安全的。
1973 年,在英国政府通讯总部工作的数学家克利福德 · 柯克斯(Clifford Cocks)在一个内部文件中提出了一个与 RSA 相似的算法,但他的发现被列入机密,一直到 1997 年才被发表。
对极大整数做因数分解的难度决定了 RSA 算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA 算法就愈可靠。如果有人找到了一种快速因数分解的算法,那么用 RSA 加密的信息的可靠性就肯定会极度下降,但找到这样的算法的可能性是非常小的,目前只有短的 RSA 密钥才可能被强力方式解破。到 2013 年为止,世界上还没有任何可靠的攻击 RSA 算法的方式。只要其密钥的长度足够长,用 RSA 加密的信息实际上是不能被解破的。
1983 年麻省理工学院在美国为 RSA 算法申请了专利。这个专利 2000 年 9 月 21 日失效。由于该算法在申请专利前就已经被发表了,在世界上大多数其它地区这个专利权不被承认。
RSA 算法描述¶
- 密钥计算方法
-
- 选择两个大素数 $p$ 和 $q$(典型值为 1024 位)
- 计算 $n=p\times q$ 和 $z=(p-1)\times(q-1)$
- 选择一个与 $z$ 互质的数,令其为 $d$
- 找到一个 $e$ 使满足 $e\times d\equiv 1 \pmod z$
- 公开密钥为 $(e, n)$,私有密钥为 $(d, n)$
- 加密方法
-
- 将明文看成比特串,将明文划分成 k 位的块 P 即可,这里 k 是满足 $2^k<n$ 的最大整数。
- 对每个数据块 P,计算 $C=P^e\pmod n$,C 即为 P 的密文。
- 解密方法
-
- 对每个密文块 C,计算 $P=C^d\pmod n$,P 即为明文。
RSA 算法举例(略)¶
RSA 算法的安全性¶
假设偷听者乙获得了甲的公钥 (e, n) 以及丙的加密消息 C,但她无法直接获得甲的私人密钥 d。
要获得 d,最简单的方法是将 n 分解为 p 和 q,这样她可以得到同余方程 $d\times e\equiv 1\pmod{(p-1)(q-1)}$ 并解出 d,然后代入解密公式:
$$ P=C^d\pmod n $$
这样就破解了密文 C,导出了明文 P。
但至今为止还没有人找到一个多项式时间的算法来分解一个大的整数的因子,同时也还没有人能够证明这种算法不存在。至今为止也没有人能够证明对 n 进行因数分解是唯一的从 C 导出 P 的方法,但今天还没有找到比它更简单的方法(至少没有公开的方法)。因此今天一般认为只要 n 足够大,那么攻击者就没有办法了。
目前,假如 n 的长度小于或等于 256 位,那么用一台个人电脑在几个小时内就可以分解它的因子。1999 年,数百台电脑合作分解了一个 512 位长的 n。2009 年 12 月 12 日,编号为 RSA-768(768 bits,232 digits)数也被成功分解。这一事件威胁现流行的 1024 bit 密钥的安全性,普遍认为用户应尽快升级到 2048 bit 或以上。
RSA 算法的速度¶
比起 DES 和其它对称算法来说,RSA 要慢得多。速度慢一直是 RSA 的缺陷,一般来说只用于少量数据加密。事实上 RSA 一般用于数字签名和对工作密钥的加密,对数据的加密一般采用速度更快的对称密码算法。
RSA 是被研究得最广泛的公钥算法,从提出到现在已经过了几十年,经历了各种攻击的考验,逐渐被人们接受,普遍认为是目前最优秀的公钥方案之一。
RSA 算法的程序实现¶
根据 RSA 算法的原理,可以利用 C 语言实现其加密和解密算法。RSA 算法比 DES 算法复杂,加/解密所需要的时间也比较长。具体实现见 OpenSSL 的源代码(https://www.openssl.org)
(此处没有演示)
4. 消息摘要和数字签名:实现完整性和抗抵赖的方法¶
使用高强度的密码技术可以保证数据的机密性。然而,密码算法的运行速度较慢,如果数据的价值(比如卫星拍摄的视频或图像,声音等大数据)不值得用密码技术对其进行保护,而只需保证其完整性时,人们迫切需要一种技术能实现高速的完整性鉴别。同时,为了防止发送信息的一方否认曾经发送信息,也需要一种技术来鉴别信息确实发送自某个密钥持有者。消息摘要和数字签名可以满足这两方面的需求。
报文摘要(消息摘要)¶
消息摘要的目的是将消息鉴别与数据保密分开,其基本设想是:发送者用明文发送消息,并在消息后面附上一个标签,允许接收者利用这个标签来鉴别消息的真伪。
用于鉴别消息的标签必须满足以下两个条件:
- 第一,能够验证消息的完整性,即能辨别消息是否被修改;
- 第二,标签不可能被伪造。
为了辨别消息是否被修改,可以将一个散列函数作用到一个任意长的消息 m 上,生成一个固定长度的散列值 H(m),这个散列值称为该消息的数字指纹,也称消息摘要 (message digest,MD)。消息的发送者对发送的消息计算一个消息摘要M1,和消息一起发给接收者;接收者对收到的消息也计算一个消息摘要 M2,如果 M2 等于 M1,则验证了消息的完整性,否则就证明了消息被篡改了。
为了保证标签不可能被伪造,发送方可以用密码技术对消息摘要 M1 进行加密保护,得到加密后的消息摘要 C,接收方对 C 进行解密恢复 M1,再与消息摘要 M2 比较,从而判断消息的完整性。加密后的消息摘要也称为消息鉴别标签。
用于消息鉴别的散列函数 H 必须满足以下特性:
- H 能够作用于任意长度的数据块,并生成固定长度的输出。
- 对于任意给定的数据块 x,H(x) 很容易计算。
- 对于任意给定的值 h,要找到一个 x 满足 H(x)=h,在计算上是不可能的(单向性)。(这一点对使用加密散列函数的消息鉴别很重要)
- 对于任意给定的数据块 x,要找到一个 y≠x 并满足 H(y)=H(x),在计算上是不可能的。(这一点对使用加密算法计算消息鉴别标签的方法很重要)
- 要找到一对(x, y)满足 H(y)=H(x),在计算上是不可能的。(抵抗生日攻击)
MD5 和 SHA¶
目前使用最多的两种散列函数是 MD5 和 SHA 序列函数。MD5 的散列码长度为 128 比特。SHA 序列函数是美国联邦政府的标准,如 SHA-1 散列码长度为 160 比特,SHA-2 散列码长度为 256、384 和 512 位。
MD5 的碰撞问题¶
MD5 的散列码长度为 128 比特,已经被证明是不安全的。2004 年,山东大学的王小云(现为科学院院士,清华大学和山东大学的教授)第一次发现 MD5 算法存在碰撞的可能,并给出了实例。
d131dd02c5e6eec4 693d9a0698aff95c 2fcab58712467eab 4004583eb8fb7f89
55ad340609f4b302 83e488832571415a 085125e8f7cdc99f d91dbdf280373c5b
d8823e3156348f5b ae6dacd436c919c6 dd53e2b487da03fd 02396306d248cda0
e99f33420f577ee8 ce54b67080a80d1e c69821bcb6a88393 96f9652b6ff72a70
d131dd02c5e6eec4 693d9a0698aff95c 2fcab50712467eab 4004583eb8fb7f89
55ad340609f4b302 83e4888325f1415a 085125e8f7cdc99f d91dbd7280373c5b
d8823e3156348f5b ae6dacd436c919c6 dd53e23487da03fd 02396306d248cda0
e99f33420f577ee8 ce54b67080280d1e c69821bcb6a88393 96f965ab6ff72a70
Both produce the MD5 hash
79054025255fb1a26e4bc422aef54eb4.
SHA-1 的碰撞问题¶
On February 23, 2017, CWI Amsterdam and Google announced they had performed a collision attack against SHA-1. They had given 2 different PDF files with the same SHA-1 outputs.
目前(2021 年 9 月)
- 对于安全要求较高的应用,不能使用 MD5
- 找到 SHA-1 的碰撞是较困难的;SHA-2 是安全的
数字签名¶
数字签名(Digital Signature)是指用户用自己的私钥对原始数据的消息摘要进行加密所得的数据,即加密的摘要。
信息接收者使用信息发送者的公钥对附在原始信息后的数字签名进行解密后获得消息摘要 M1,并与原始数据产生的消息摘要 M2 对照,便可确信原始信息是否被篡改。这样就保证了消息来源的真实性和数据传输的完整性。
图:用公钥算法实现数字签名及完整性验证

消息的保密传输¶
为了对消息进行保密传输,通常将公钥密码技术和对称密码技术结合起来使用。
- 在发送方 A 随机生成一个对称密码算法的密钥 K。
- 然后用 K 对消息加密得到密文 C 并生成密文的数字摘要 M,接着用 A 的私钥对 M 签名,将密文 C、用 B 的公钥加密的签名及 K 发送给接收方 B。
- 接收方 B 进行相反的操作,就可以实现消息的保密传输及完整性验证。
5. PGP 及其应用¶
为了保护电子邮件及文件的保密性,Phil Zimmermann 提出了 Pretty Good Privacy 加密标准,得到了广泛的应用。
PGP(Pretty Good Privacy)是一个基于 RSA 公钥加密体系的邮件加密软件。
PGP 加密技术的创始人是美国的 Phil Zimmermann。他创造性地把 RSA 公钥体系和传统加密体系的结合起来,并且在数字签名和密钥认证管理机制上有巧妙的设计,因此 PGP 成为目前几乎最流行的公钥加密软件包。
PGP 简介¶
由于 RSA 算法计算量极大,在速度上不适合加密大量数据,所以 PGP 实际上用来加密的不是 RSA 本身,而是采用传统加密算法 IDEA,IDEA 加解密的速度比 RSA 快得多。
PGP 随机生成一个密钥,用 IDEA 算法对明文加密,然后用 RSA 算法对密钥加密。收件人同样是用 RSA 解出随机密钥,再用 IDEA 解出原文。这样的链式加密既有 RSA 算法的保密性(Privacy)和认证性(Authentication),又保持了 IDEA 算法速度快的优势。
PGP 提供五种服务:鉴别,机密性,压缩,兼容电子邮件,分段。
PGP 最初在 Windows 实现,直到 PGP Desktop 9.0 一直为免费共享软件,后来 PGP 被 Symantec 收购,成为了收费软件。
OpenPGP(http://www.openpgp.org)是源自 PGP 标准的免费开源实现,目前是世界上应用最广泛的电子邮件加密标准。OpenPGP 由 IETF 的 OpenPGP 工作组提出,其标准定义在 RFC 4880。在 Windows 和 Linux/Unix 下均有免费开源的版本。
GunPG(The GNU Privacy Guard)是 OpenPGP 的最典型实现,目前支持 Windows、Linux、MacOS 等流行操作系统。相关软件可以从 https://www.gnupg.org 下载。
关于 Gpg4win 的使用展示此处省略
6. 使用 OpenSSL 中的密码函数¶
OpenSSL(https://www.openssl.org)是使用非常广泛的 SSL 的开源实现,是用 C 语言实现的。由于其中实现了为 SSL 所用的各种加密算法,因此 OpenSSL 也是被广泛使用的加密函数库。
有两种方式使用 OpenSSL 的加/解密功能:
- 其一是在命令行下运行 OpenSSL,以适当的参数运行
openssl命令,就可以实现加密和解密功能; - 另一种是在自己的应用程序中使用加密函数,这需要利用 OpenSSL 提供的 C 语言接口,以函数调用的方式式使用加密函数库。
在命令行下使用 OpenSSL¶
Windows 和 Linux 环境下的 OpenSSL 有相同的命令行程序名 openssl。在命令行窗口下运行 openssl help,可以列出 OpenSSL 支持的命令。
OpenSSL 的命令分成三类:标准命令、数字摘要命令和加密命令。
| 命令类别 | 子命令 |
|---|---|
| 标准命令 Standard commands |
asn1parse, ca, ciphers, cms, crl, crl2pkcs7, dgst, dh, dhparam, dsa, dsaparam, ec, ecparam, enc, engine, errstr, gendh, gendsa, genpkey, genrsa, nseq, ocsp, passwd, pkcs12, pkcs7, pkcs8, pkey, pkeyparam, pkeyutl, prime, rand, req, rsa, rsautl, s_client, s_server, s_time, sess_id, smime, speed, spkac, srp, ts, verify, version, x509 |
| 数字摘要命令 Message Digest commands |
md4, md5, mdc2, rmd160, sha, sha1 |
| 加密命令 Cipher commands |
aes-128-cbc, aes-128-ecb, aes-192-cbc, aes-192-ecb, aes-256-cbc, aes-256-ecb, base64, bf, bf-cbc, bf-cfb, bf-ecb, bf-ofb, camellia-128-cbc, camellia-128-ecb, camellia-192-cbc, camellia-192-ecb,camellia-256-cbc, camellia-256-ecb, cast, castcbc,cast5-cbc, cast5-cfb, cast5-ecb, cast5-ofb,des, des-cbc, des-cfb, des-ecb, desede, des-ede-cbc, des-ede-cfb, des-ede-ofb, des-ede3, des-ede3-cbc, des-ede3-cfb, des-ede3-ofb, des-ofb, des3, desx, idea, idea-cbc, idea-cfb, idea-ecb, idea-ofb, rc2, rc2-40-cbc, rc2-64-cbc, rc2-cbc, rc2-cfb, rc2-ecb, rc2-ofb, rc4, rc4-40, seed, seed-cbc, seed-cfb, seed-ecb, seed-ofb |
OpenSSL 支持的命令是相当丰富的。如果对某个命令的用法不是很清楚,可以用 openssl 命令名称 -? 查看该命令的说明。
例如,如果不了解 openssl passwd 的用法,可以在命令行下输入 openssl passwd -?,运行结果(Ubuntu Linux)如下:
fanping@vbu32:~/work$ openssl passwd -?
Usage: passwd [options] [passwords]
……
常用的 OpenSSL 的命令¶
| 功能 | 命令及说明 |
|---|---|
| 版本和编译参数 | 显示版本和编译参数:openssl version -a |
| 支持的子命令、密码算法 | 查看支持的子命令:openssl helpSSL 密码组合列表: openssl ciphers |
| 测试密码算法速度 | 测试所有算法速度:openssl speed测试 RSA 速度: openssl speed rsa测试 DES 速度: openssl speed des |
| RSA 密钥操作 | 产生 RSA 密钥对:openssl genrsa -out 1.key 1024取出 RSA 公钥: openssl rsa -in 1.key -pubout -out 1.pubkey |
| 加密文件 | 加密文件:openssl enc -e -rc4 -in 1.key -out 1.key.enc解密文件: openssl enc -d -rc4 -in 1.key.enc -out 1.key.dec |
| 计算 Hash 值 | 计算文件的 MD5 值:openssl md5 < 1.key 或 openssl md5 1.key计算文件的 SHA1 值: openssl sha1 < 1.key |
实例¶
密钥在文件 key.txt 中,用 des3 算法对文件 test.data 加密和解密,并验证其正确性。
- 加密为
test.3des:openssl enc -e -des3 -in test.data -out test.3des -kfilekey.txt - 解密
test.3des为test.dddd:openssl enc -d -des3 -in test.3des -out test.dddd -kfile key.txt - 验证
test.dddd和原始文件test.data相同:openssl md5 test.dddd test.data
在 Windows 的 C 程序中使用 OpenSSL¶
OpenSSL 提供了 C 语言接口所需的头文件、库文件和动态链接库。为了使用该接口,必须安装面向软件开发人员的软件包(安装文件较大),并将 OpenSSL 的 lib 和 include 目录添加到 lib 和环境变量中。对于 Visual Studio C++ 开发平台,最简单的方法是将 OpenSSL 的 lib 和 include 目录拷贝到 VC 目录(默认安装在 C:\Program Files\Microsoft Visual Studio 9.0\VC)中,这样就不需要额外设置环境变量。
为了使用 OpenSSL 库函数,在 C 程序中必须包含相应的头文件,链接的时候必须加入相关的库。
在 Linux 的 C 程序中使用 OpenSSL¶
Linux 系统的发行版一般预装了命令行 OpenSSL 程序,没有安装 openssl 库。为了在 C 程序中使用 OpenSSL,需要安装 openssl 库。
在 Ubuntu Linux 系统中运行以下命令安装 openssl 库:
sudo apt-get install libssl-dev
在 Fedora Linux 系统中切换到 root,再运行以下命令安装 openssl 库:
yum install openssl-devel.x86_64
或
yum install openssl-devel.i686
7. Windows 系统提供的密码算法¶
Windows 通过 CryptoAPI 提供密码算法服务,支持数据的加密/解密和基于数字证书的身份认证等功能,同时也允许第三方开发符合 Windows 规范的密码算法。程序员只须调用相应的 API 函数就可以完成加密操作,而不必了解算法的实现细节。
CryptoAPI 系统架构如图所示。

密码服务提供者 CSP¶
CSP 是真正执行加密工作的独立的模块。物理上一个 CSP 由两部分组成:一个动态链接库和一个签名文件。每个 CSP 都有一个名字和一个类型。每个 CSP 的名字是惟一的,这样便于 CryptoAPI 找到对应的 CSP。
函数 CryptEnumProviderTypes 可以枚举系统中的 CSP 类型和该类型的名字,函数 CryptEnumProviders 可以枚举系统中 CSP 的名字和类型。例程 enumerateProvidersAndTypes.cpp 枚举了系统中的 CSP 类型、类型名和 CSP 名字。
为了使用 CSP 提供的密码算法,首先必须调用 CryptAcquireContext 获得指向特定 CSP 的句柄(handle),该句柄代表 CSP 提供者及对应的密钥容器。
对于 Windows 系统中的每个用户,每个 CSP 都有多个密钥容器,每个密钥容器由惟一的名字标识。密钥容器存储了用户的密钥,包括签名密钥和密钥交换密钥。
以密钥容器名字作为函数 CryptAcquireContext() 的参数,函数将返回指向这个密钥容器的句柄。如果该名字的容器不存在,可以用 CryptAcquireContext() 函数产生一个新的密钥容器。
使用完 CSP 的密钥容器后,用函数 CryptReleaseContext() 释放其对应的句柄。
使用 CSP 提供的密码技术实现保密通信¶
使用 CSP 实现保密通信的主要过程如下:
- 用
CryptGenKey生成一个随机会话密钥; - 用该会话密钥加密数据;
- 指定目标用户的公钥,用
CryptExportKey将会话密钥导出为一个 BLOB 密钥,该导出的密钥是被目标用户的公钥加密了; - 发送加密的信息和加密的 BLOB 密钥给目标用户;
- 目标用户用
CryptImportKey导入 BLOB 密钥到其 CSP。只要在步骤 3 指定了目标用户的公钥,则导入密钥时会自动解密会话密钥。 - 目标用户用会话密钥解密所收到的加密信息
MSDN 中的例子程序
- Microsoft 的 MSDN 中给出了四个例子程序,演示了在应用程序中使用 CSP 及密码函数的方法。
- 由于例子程序较大,在此不做进一步的分析。感兴趣的读者请参考 MSDN。
8. Python 语言的加密模块¶
在 PyPI 有 2 个可用的 Python 密码模块:
旧的 pycrypto:
- Latest version Released: Oct 18, 2013
- https://pypi.org/project/pycrypto/
新的 pycryptodome:
- Latest version Released: Feb 9, 2021
- https://pypi.org/project/pycryptodome/
Pycryptodome 的安装和使用
安装:pip install pycryptodome
使用 AES:from Crypto.Cipher import AES
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
key = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_EAX)
ciphertext, tag = cipher.encrypt_and_digest(data)
file_out = open("encrypted.bin", "wb")
[ file_out.write(x) for x in (cipher.nonce, tag, ciphertext) ]
file_out.close()
from Crypto.Cipher import AES
file_in = open("encrypted.bin", "rb")
nonce, tag, ciphertext = [ file_in.read(x) for x in (16, 16, -1) ]
# let's assume that the key is somehow available again
cipher = AES.new(key, AES.MODE_EAX, nonce)
data = cipher.decrypt_and_verify(ciphertext, tag)
作业与实践¶
- 在腾讯会议系统中,对称密码技术和公钥密码技术适合应用在哪几个阶段?说明理由。
- 简述散列函数 MD5 的碰撞问题。既然 MD5 存在碰撞问题,为何 http://mirrors.ustc.edu.cn/ubuntu-releases/18.04/ 仍然给出 MD5 值作为完整性验证的依据。
- 简述用 RSA 公钥算法实现数字签名的过程。
- 用 PGP 加密某个文件,如果接收该加密文件的用户为 1 个,加密文件的大小为 24kB;如果接收该加密文件的用户为 10 个,请问加密文件的大小是原来的 10 倍(240kB)吗?为什么?