基础知识
密码基础
网络数据传输威胁
加密三要素
- 明文:要发送的数据
- 加密算法:以什么样的规则进行加密(常用的算法:
des
、3des
、aes
) - 密钥:双方约定的钥匙,会根据算法的不同,长度不同
解密三要素
- 密文:
%&……
- 解密算法:解密算法可能与加密算法相同,也可能不同
- 密钥:密钥一定与加密密钥相同
凯撒密码
hello world
=> 加密 => khoor zrug
=> 解密 => hello world
加密过程
- 明文:
hello world
- 算法:向右移动
- 密钥:3
解密过程
- 密文:
khoor zrug
- 算法:向左移动(与加密算法不同)
- 密钥:3
加密方式及特点
对称加密
- 密钥数量:1个
- 特点:
- 加密效率高
- 双方使用的密钥相同
- 安全性
- 不安全(相对于非对称加密)
- 使用情况
- 主流的加密方式
非对称加密
- 密钥数量:2个
- 公钥
- 任何人都可以持有
- 一般用于加密作用
- 私钥
- 只有自己持有
- 一般不用于加密,而是用于签名
- 签名的数据,可以证明是私钥持有人发送的数据
- 私钥签名的数据,私钥持有人无法否认自己发送过的数据
- 公钥
- 特点
- 公钥加密的只有自己的私钥能解开
- 加密解密效率很低,一般不做大量数据加解密使用
- 安全性
- 高
- 使用情况
- 一般配合对称加密使用
- 建立连接之初,先使用非对称加密协商好对称加密的算法和密匙
- 然后使用对称加密进行后续加解密
加密领域常识
- 不要使用保密的密码算法
- 使用低强度的密码比不进行任何加密更危险
- 任何密码总有一天会被破译
- 密码只是信息安全的一部分
编解码与加解密
编码解码
由字符转成二进制比特流的过程叫做编码;有比特流转换为可读字符的过程叫做解码
常用编解码方式:
gob
包:go
内置的编解码包html
编码json
编解码binary
包:go
内置的编解码包
加密解密
- 加解密就是对比特流进行编解码
- 加密:对明文的比特序列进行编码,得到密文的比特序列
对称加密
常用算法
DES
Data Encryption Standard
DES
是一种将64比特的明文加密成64比特的密文的对称密码算法,它的密钥长度是56比特。尽管从规格上来说,DES的密钥长度是64比特,但由于每隔7比特会设置一个用于错误检查的比特,因此实质上其密钥长度是56比特DES
是以64比特的明文(化比特序列)为一个单位来进行加密的,这个64比特的单位称为分组。一般来说,以分组为单位进行处理的密码算法称为分组密码(blockcipher
),DES
就是分组密码的一种DES
每次只能加密64比特的数据,如果要加密的明文比较长,就需要对DES
加密进行迭代(反复),而迭代的具体方式就称为模式(mode
)
特点
- 不安全,已经被破解,不建议使用
- 密钥:8字节,64
bit
(其实只使用了56bit
,每七个bit
就会设置一个比特位) - 加密时,会对明文进行分组,分组长度是8
bytes
,得到的密文也是8bytes
为一组 - 加解密效率低(过渡的加解密算法)
3DES
Triple Data Encryption Standard
三重DES
(triple-DES
)是为了增加DES
的强度,将DES
重复3次所得到的一种密码算法,通常缩写为3DES
- 加密过程:加密 - 解密 - 加密
- 中间使用解密的原因是为了兼容以前的
DES
- 解密过程是以解密的方式进行加密,整体还是三次加密
- 密钥:8
bytes
* 3 = 24bytes
= 192bit
- 数据分组长度:与
DES
相同,8bytes
= 64bit
- 中间使用解密的原因是为了兼容以前的
- 解密过程:解密 - 加密 - 解密
3个密钥
- 如果3个密钥都相同,这就相当于
DES
,与前面进行兼容 - 如果密钥1与密钥3相同,相当于有两个密钥,专业名字:
3DES-EDE2
- 如果三个密钥都不相同,专业名字:
3DES-EDE3
AES
Advance Encryption Standard
AES
是取代其前任标准(DES
)而成为新标准的一种对称密码算法。全世界的企业和密码学家提交了多个对称密码算法作为AES
的候选,最终在2000年从这些候选算法中选出了种名为**Rijndael
**的对称密码算法,并将其确定为了AES
Rijndael
:是由比利时密码学家Joan Daemen
和Vincent Rijmen
设汁的分组密码算法,今后会有越来越多的密码软件支持这种算法
Rijndae的分组长度为128比特,密钥长度可以以32比特为单位在128比特到256比特的范围内进行选择(不过在AES的规格中,密钥长度只有128、192和256比特三种)
特点
- 密钥长度:可选的,
16bytes
,24bytes
,32bytes
(des
:8bytes
;3des
:24bytes
) - 分组长度:
16bytes
(des
和3des
:8bytes
) - 加解密效率高,推荐使用
小结
对称加密特点
- 加密与解密使用的密钥相同
- 在一定程度上实现了数据的机密性,且简单、快速
- 但是由于算法一般都是公开的,因此机密性几乎完全依赖于密钥
牢记知识点
des
:key
-> 8字节,分组 -> 8字节3des
:key
-> 24字节,分组 -> 8字节aes
:key
-> 16字节(128),24字节(192),32字节(256),分组 -> 16字节
分组模式
为什么要分组
- 被加密的数据很大,需要对数据进行迭代的加密,所以用对数据进行分组
分组密码(blockcipher
)是每次只能处理特定长度的一块数据的一类密码算法,这里的"一块"就称为分组(block
)。此外,一个分组的比特数就称为分组长度(blocklength
)
DES
和AES
都属于分组密码,它们只能加密固定长度的明文。如果需要加密任意长度的明文,就需要对分组密码进行迭代,而分组密码的迭代方法就称为分组密码的**“模式”**。
分组密码有很多种模式,如果模式的选择不恰当,就无法保证机密性。例如,如果使用ECB
模式,明文中的些规律就可以通过密文被识别出来。
分组密码的主要模式有:ECB
、CBC
、CFB
、OFB
、CTR
明文分组与密文分组
明文分组:是指分组密码算法中作为加密对象的明文。明文分组的长度与分组密码算法的分组长度是相等的
密文分组:是指使用分组密码算法将明文分组加密之后所生成的密文。
- 密文分组的长度与明文分组一致
五种分组模式
ECB
:Electronic Coding Book
,电子密码本模式(不使用,淘汰)CBC
:Cypher Block Chaining
,密文分组链接模式(常用)CFB
:Cypher FeedBack
,密文反馈模式(也有使用,建议使用CTR)OFB
:Output FeedBack
,输出反馈模式(也有使用,建议使用CTR)CTR
:Count
,计数器模式(建议使用)
ECB
ECB
(Electronic Code Book
,电子密码本)模式是最简单的加密模式,明文消息被分成固定大小的块(分组),并且每个块被单独加密。每个块的加密和解密都是独立的,且使用相同的方法进行加密,所以可以进行并行计算,但是这种方法一旦有一个块被破解,使用相同的方法可以解密所有的明文数据
特点
- 安全性比较差,不推荐使用
- 适用于数据较少的情形
- 加密前需要把明文数据填充到块大小的整倍数
go
语言没有支持这种分组模式- 分组长度由加密算法决定(
des
:8bytes
,aes
:16bytes
)
CBC
CBC
(Cipher Block Chaining
,密文分组链接)模式中每一个分组要先和前一个分组加密后的数据进行XOR异或操作,然后再进行加密。这样每个密文块依赖该块之前的所有明文块,为了保持每条消息都具有唯一性,第一个数据块进行加密之前需要用初始化向量(Initialize Vector)进行异或操作。CBC
模式是一种最常用的加密模式,它主要缺点是加密是连续的,不能并行处理,并且与ECB
一样消息块必须填充到块大小的整倍数。
- 数据分组长度根据算法而定
- 需要提供初始化向量(
Initialize Vector
),要求长度必须与分组长度相同 - 每一个密文都是下一次加密操作的输入
- 不能够并行加密,可以并行解密
- 加密强度高
- 如果数据切割后长度不满足需求,需要对数据进行填充
ECB与CBC模式的比较
如果将一个分组的加密过程分离出来,我们就可以很容易地比较出ECB
模式和CBC
模式的区别。ECB
模式只进行了加密,而CBC
模式则在加密之前进行了一次XOR
初始化向量
当加密第一个明文分组时,由于不存在“前一个密文分组“,因此需要事先准备一个长度为一个分组的比特序列来代替"前一个密文分组",这个比特序列称为初始化向量(Initialization vector
)
通常缩写为IV
一般来说,每次加密时都会随机产生一个不同的比特序列来作为初始化向量。
明文分组在加密之前一定会与"前一个密文分组"进行XOR
运算,因此即便明文分组1和2的值是相等的,密文分组1和2的值也不一定是相等的。这样一来,ECB
模式的缺陷在CBC
模式中就不存在了。
CFB
CFB
模式的全称是Cipher FeedBack
模式(密文反馈模式)。在CFB
模式中,前一个分组的密文加密后和当前分组的明文XOR异或操作生成当前分组的密文
所谓反馈,这里指的就是返回输入端的意思,即前一个密文分组会被送回到密码算法的输入端。
CFB
模式的解密和CBC
模式的加密在流程上其实是非常相似的
- 分组长度取决于加密算法。
- 需要初始化向量,长度必须与明文分组相同。
- 先对密文进行加密,然后再与明文分组进行异或。(
CBC
是先异或,再加密) - 由于没有直接对明文分组进行加密,所以不需要填充
- 注意:解密的时候,是对初始化向量进行加密操作,这样才能得到同样的数据
CBC模式与CFB模式对比
在CBC
模式中,明文分组和密文分组之间有XOR
和密码算法两个步骤,而在CFB
模式中,明文分组和密文分组之间则只有XOR
在CFB
模式中,明文分组并没有通过密码算法来直接进行加密(加密的是上一次的密文,而不是本次的明文)
OFB
OFB
式的全称是Output-Feedback
模式(输出反馈模式)。在OFB
模式中,密码算法的输出会反馈到密码算法的输入中,即上一个分组密码算法的输出是当前分组密码算法的输入。
OFB
模式并不是通过密码算法对明文直接进行加密的,而是通过将“明文分组”和“密码算法的输出”进行XOR
来产生“密文分组”的,在这一点上OFB
模式和CFB
模式非常相似。
- 分组长度取决于加密算法
- 不断对初始向量的输出进行加密,从而得到数据来源
- 不需要进行数据填充
OFB与CFB的对比
OFB
模式和CFB
模式的区别仅仅在于密码算法的输入。
CFB
式中,加密算法的输入是前一个密文分组,也就是将密文分组反馈到加密算法中,因此就有了“密文反馈模式”这个名字。
相对地,OFB
模式中,加密算法的输入则是加密算法的前一个输出,也就是将输出反馈给加密算法,因此就有了“输出反馈模式"这个名字。
CTR
CTR
模式的全称是CounTeR
模式(计数器模式)。CTR模式是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码
CTR
模式中,每个分组对应一个逐次累加的计数器,并通过对计数器进行加密来生成密钥流。也就是说,最终的密文分组是通过将计数器加密得到的比特序列,与明文分组进行XOR
而得到的。
- 分组长度取决于加密算法
- 不需要填充
- 可以并行加密和并行解密,效率高,推荐使用
计数器的生成方法
每次加密时都会生成一个不同的值(nonce
)来作为计数器的初始值。当分组长度为128比特(16字节)时,计数器的初始值可能是像下面这样的形式
其中前8个字节为nonce
(随机数),这个值在每次加密时必须都是不同的,后8个字节为分组序号,这个部分是会逐次累加的。
按照上述生成方法,可以保证计数器的值每次都不同。由于计数器的值每次都不同,因此每个分组中将计数器进行加密所得到的密钥流也是不同的。也是说,这种方法就是用分组密码来模拟生成随机的比特序列。
CTR模式与OFB模式对比
CTR
模式和OFB
模式一样,都属于流密码。OFB
模式是将加密的输出反馈到输入,而CTR
模式则是将计数器的值用作输入。
CTR模式的特点
CTR
模式的加密和解密使用了完全相同的结构,因此在程序实现上比较容易。这一特点和同为流密码的OFB
模式是一样的。- 此外,
CTR
模式中可以以任意顺序对分组进行加密和解密,因此在加密和解密时需要用到的“计数器"的值可以由nonce
和分组序号直接计算出来。这一性质是OFB
模式所不具备的。 - 能够以任意顺序处理分组,就意味着能够实现并行计算。在支持并行计算的系统中,
CTR
模式的速度是非常快的。
小结
加密算法与分组模式的关系
分组特点总结
Go实现
DES-CBC
加密
- 创建并返回一个使用
DES
算法的cipher.Block
接口- 参数为密钥,密钥长度为64
bit
,即64/8=8字节(byte
)
- 参数为密钥,密钥长度为64
- 对最后一个明文分组进行数据填充
DES
是以64比特的明文(比特序列)为一个单位来进行加密的- 最后一组不够64
bit
,则需要进行数据填充
- 创建一个密码分组为链接(
CBC
)模式的,底层使用DES
算法加密的BlockMode
接口 - 加密连续的数据块
解密
- 创建并返回一个使用
DES
算法的cipher.Block
接口 - 创建一个密码分组为链接模式的,底层使用
DES
解密的BlockMode
接口 - 数据块解密
- 去掉最后一组的填充数据
具体代码
GitHub(私有仓库)
AES-CTR
加密
- 创建一个
cipher.Block
接口 - 选择分组模式:
CTR
- 加密操作
解密
- 创建一个
cipher.Block
接口 - 选择分组模式:
CTR
- 解密操作
具体代码
GitHub(私有仓库)
非对称加密
对称加密中存在的问题
- 当通信对象很多时会面临众多秘钥的有效管理问题。
- 对于一个新的数据通信对象,密钥怎样进行传输的问题。
非对称加密介绍
非对称加密也叫公钥密码:使用公钥加密,使用私钥解密
在对称加密中,由于加密和解密的密钥是相同的,因此必须向接收者配送密钥。用于解密的密钥必须被配送给接收者,这一问题称为密钥配送问题。如果使用非对称加密也可以称为公钥加密,则无需向接收者配送用于解密的密钥,这样就解决了密钥配送问题。可以说非对称加密是密码学历史上最伟大的发明。
非对称加密中,密钥分为加密密钥和解密密钥两种。发送者用加密密钥对消息进行加密,接收者用解密密钥对密文进行解密。
RSA
-
私钥:使用随机数按照一定规则生成的
-
公钥:由私钥推导而来
随机数据 => 算法 => 私钥 =>公钥
-
私钥:只有自己持有,不可以向任何人传播
-
公钥:任何人都可以持有,公钥加密的数据只能被配套的私钥解开
|
|
使用场景
- 通信加密:公钥加密,私钥解密
https
:验证服务器,数字证书,使用ca
认证公钥- 签名(防止篡改):哈希+非对称加密
- 网银U盾:验证
client
,U盾相当于私钥,公钥在服务端 github ssh
(secure shell
)登录ssh
是一种网络协议,主要用于两个计算机之间的加密登录与数据传输ssh
登录的时候没有ca认证,需要用于自己确认登录主机的指纹,点击yes
后远程主机存放到本地的know_hosts
中,后续登录会跳过警告ssh-keygen -t rsa
,演示
RSA的加密过程
在RSA
中,明文、密钥和密文都是数字,RSA
的加密过程可以用下列公式来表达:
$$ 密文=明文 ^ E mod N(RSA加密) $$
也就是说,RSA
的密文是对代表明文的数字的E
次方求modN
的结果。换句话说,就是将明文自己做E
次乘法,然后将其结果除以N
求余数,这个余数就是密文。
E
和N
是RSA
加密的密钥,也就是说,E和N的组合就是公钥
E
和N
并不是随便什么数都可以的,它们是经过严密计算得出的。顺便说一句,E
是加密(Encryption
)的首字母,N
是数字(Number
)的首字母。
有一个很容易引起误解的地方需要大家注意一一E
和N
这两个数并不是密钥对(公钥和私钥的密钥对)。E
和N
两个数才组成了一个公钥,因此我们一般会写成“公钥是(E,N)
”或者“公钥是{E,N}
”这样的形式,将E
和N
用括号括起来。
总而言之,RSA的加密就是"求E次方的modN"
RSA的解密过程
$$ 明文=密文 ^ D mod N(RSA解密) $$
对表示密文的数字的D
次方求modN
就可以得到明文。换句话说,将密文自己做D
次乘法,再对其结果除以N
求余数,就可以得到明文。
这里所使用的数字N
和加密时使用的数字N
是相同的。数D和数N组合起来就是RSA的解密密钥,因此D
和N
的组合就是私钥。只有知道D
和N
两个数的人才能够完成解密的运算。
规则描述
|
|
PKCS
全称是Public-Key Cryptography Standards
,是由RSA
实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准
PKCS#1
:RSA
加密标准。PKCS#1
定义了RSA
公钥函数的基本格式标准,特别是数字签名。它定义了数字签名如何计算,包括待签名数据和签名本身的格式;它也定义了RSA
公/私钥的语法。
GitHub(私有仓库)
ECC
椭圆曲线密码学(英语:Elliptic curve cryptograjthy,缩写为ECC),一种建立公开密钥加密的算法,基于椭圆曲线数学。椭圆曲线在密码学中的使用是在1985年由Neal Koblitz和Victor Miller分别独立提出的。
ECC的主要优势是在某些情况下它比其他的方法使用更小的密钥一一比如RSA加密算法一一提供相当的或更高等级的安全。
椭圆曲线密码学的许多形式有稍微的不同,所有的都依赖于被广泛承认的解决椭圆曲线离散对数问题的困难性上。与传统的基于大质数因子分解困难性的加密方法不同,ECC通过椭圆曲线方程式的性质产生密钥。
ECC164位的密钥产生的一个安全级相当于RSA1024位密钥提供的保密强度,而且计算量较小,处理速度更快,存储空间和传输带宽占用较少。目前我国居民二代身份证正在使用256位的椭圆曲线密码,虚拟货币比特币也选择ECC作为加密算法
数学原理:不管是RSA
还是ECC
或者其它,公钥加密算法都是依赖于某个正向计算很简单(多项式时间复杂度),而逆向计算很难(指数级时间复杂度)的数学问题。
椭圆曲线依赖的数学难题是:k
为正整数,P
是椭圆曲线上的点(称为基点),k * P = Q
,已知Q
和P
,很难计算出k
GitHub(私有仓库)
Base64
介绍
Base64
编码,是我们程序开发中经常使用到的编码方法。因为base64
编码的字符串,更适合不同平台、不同语言的传输)。它是一种基于用64个可打印字符来表示二进制数据的表示方法。
作用
- 由于某些系统中只能使用
ASCII
字符。Base64
就是用来将非ASCII
字符的数据转换成ASCII
字符的一种方法 - 对二进制文件进行文本化后的传输
- 前后台交互时,经常使用
base64
,这样可以避免特殊字符传输错误
原理
Base64
编码要求把3个8位字节(3*8=24
)转化为4个6位的字节(4*6=24
),之后在6位的前面补两个0,形成8位一个字节的形式。如果剩下的字符不足3个字节,则用0填充,输出字符使用=
,因此编码后输出的文本末尾可能会出现1或2个=
为了保证所输出的编码位可读字符,Base64制定了一个编码表,以便进行统一转换。编码表的大小为2^6=64
,这也是Base64
名称的由来。
base64
就是一种基于64个可打印字符来表示二进制数据的方法。- 编码后便于传输,尤其是不可见字符或特殊字符,对端接收后解码即可复原。
base64
只是编码,并不具有加密作用。
GitHub(私有仓库)
哈希函数
哈希函数 — 单向散列函数
概述
在刑事侦查中,侦查员会用到指纹。通过将某个特定人物的指纹与犯罪现场遗留的指纹进行对比,就能够知道该人物与案件是否存在关联。针对计算机所处理的消息,有时候我们也需要用到“指纹”。当需要比较两条消息是否一致时,我们不必直接对比消息本身的内容,只要对比它们的“指纹“就可以了。
使用单向散列函数就可以获取消息的“指纹”,通过对比“指纹”,就能够知道两条消息是否一致。现在使用非常广泛的是SHA
单向散列函数。
Hash
:可以对输入的数据内容生成一个唯一的数值
对于同一个算法,有如下特性:
- 输入内容不变,输出内容不变
- 输入内容改变,那怕是一点点改变,输出的内容千差万别
- 无论输入的内容大小如何,(1M,1K,1G),生成的哈希长度相同
- 哈希运算是对输入内容做摘要(指纹),无法根据哈希值反推回原文
什么是单向散列函数
单向散列函数(one-wayftnction
)有一个输入和一个输出,其中输入称为消息(message
),输出称为散列值(hashvalue
)。单向散列函数可以根据消息的内容计算出散列值,而散列值就可以被用来检查消息的完整性。
这里的消息不一定是人类能够读懂的文字,也可以是图像文件或者声音文件。单向散列函数不需要知道消息实际代表的含义。无论任何消息,单向散列函数都会将它作为单纯的比特序列来处理,即根据比特序列计算出散列值。
散列值的长度和消息的长度无关。无论消息是1比特,还是100MB,甚至是100GB,单向散列函数都会计算出固定长度的散列值。以SHA-1
单向散列函数为例,它所计算出的散列值的长度永远是160比特(20字节)
关于术语
单向散列函数的相关术语有很多变体,不同参考资料中所使用的术语也不同,下面我们就介绍其中的几个。单向散列函数也称为消息摘要函教(message digest function
)、哈希函数或者杂凑函数
输入单向散列函数的消息也称为原像(pre-image
)
单向散列函数输出的散列值也称为消息摘要(message digest
)或者指纹(fingerprint
)
完整性也称为一致性
顺便说一句,单向散列函数中的“散列"的英文hash
一词,原意是古法语中的“斧子”,后来被引申为“剁碎的肉沫”,也许是用斧子一通乱剁再搅在一起的那种感觉吧。单向散列函数的作用,实际上就是将很长的消息剁碎,然后再混合成固定长度的散列值。
单向散列函数的性质
- 根据任意长度的消息计算出固定长度的散列值
- 能够快速计算出散列值
- 消息不同散列值也不同
正如同将玻璃砸得粉碎很容易,但却无法将碎片还原成完整的玻璃一样,根据消息计算出散列值很容易,但根据散列值却无法反算出消息。
在这里需要注意的一点是,尽管单向散列函数所产生的散列值是和原来的消息完全不同的比特序列,但是单向散列函数并不是一种加密,因此无法通过解密将散列值还原为原来的消息。
应用
- 检测软件是否被篡改
- 消息认证码
- 消息认证码是将"发送者和接收者之间的共享密钥"和"消息",进行混合后计算出的散列值。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装。
- 消息认证码在
SSL/TLS
中也得到了运用
- 数字签名
- 私钥对文件签名时,并不会对文件本身做签名,而是对这个文件的哈希值进行签名
- 数字签名是现实社会中的签名(
sign
)和盖章这样的行为在数字世界中的实现。数字签名的处理过程非常耗时,因此一般不会对整个消息内容直接施加数字签名,而是先通过单向散列函数计算出消息的散列值,然后再对这个散列值施加数字签名。
- 伪随机数生成器
- 密码技术中所使用的随机数需要具备“事实上不可能根据过去的随机数列预则未来的随机数列”这样的性质。为了保证不可预测性,可以利用单向散列函数的单向性
- 一次性口令
- 一次性口令经常被用于服务器对客户端的合法性认证。在这种方式中,通过便用单向散列函数可以保证口令只在通信链路上传送一次(
one-time
),因此即使窃听者窃取了口令,也无法使用
- 一次性口令经常被用于服务器对客户端的合法性认证。在这种方式中,通过便用单向散列函数可以保证口令只在通信链路上传送一次(
- 密码存储
- 网站数据库中,对密码的存储并不是密码的明文,而是密码的哈希值
- 每次登录时,会对密码进行哈希处理,然后与数据库对比
- 即使数据库被盗,黑客也无法拿到用户的密码,保证用户账户安全
常用的单向散列函数
MD4、MD5
MD4
是由Rivest
于1990年设计的单向散列函数,能句够产生128比特的散列值(RFC1186
,修订版RFC1320
)。不过,随着Dobbertin
提出寻找MD4
散列碰撞的方法,因此现在它已经不安全了
MD5
是由Rwest
于1991年设计的单项散列函数,能够产生128比特的散列值(RFC1321
)。
MD5
的强抗碰撞性已经被攻破,也就是说,现在已经能够产生具备相同散列值的两条不同的消息,因此它也已经不安全了。
MD4
和MD5
中的MD
是消息摘要(Message Digest
)的缩写
SHA
SHA-1
SHA-2
:SHA-224
、SHA-256
、SHA-384
、SHA-521
SHA-1
是由NIST
(NationallnstituteOfStandardsandTechnology
,美国国家标准技术研究所)设计的一种能够产生160比特的散列值的单向散列函数。1993年被作为美国联邦信息处理标准规格(FIPS PUB 180)发布
的是SHA
,1995年发布的修订版FIPS PUB180-1
称为SHA-1
SHA-1
的消息长度存在上限,但这个值接近于2^64^比特,是个非常巨大的数值,因此在实际应用中没有问题
SHA-256
、SHA-384
和SHA-512
都是由NIST
设计的单向散列函数,它们的散列值长度分别为256比特、384比特和512比特。这些单向散列函数合起来统称SHA-2
,它们的消息长度也存在上限(SHA-256的上限接近于2^64^比特,SHA384和SHA-512的上限接近于2^128^比特)。这些单向散列函数是于2002年和SHA-1
一起作为FIPS PUB 180-2
发布的,SHA-1
的强抗碰撞性已于2005年被攻破,也就是说,现在已经能够产生具备相同散列值的两条不同的消息。不过,SHA-2
还尚未被攻破。
比特数 | 字节数 | |
---|---|---|
MD4 | 128bit | 16byte |
MD5 | 128bit | 16byte |
SHA-1 | 160bit | 20byte |
SHA-224 | 224bit | 28byte |
SHA-256 | 256bit | 32byte |
SHA-384 | 384bit | 48byte |
SHA-521 | 512bit | 64byte |
GitHub(私有仓库)
消息认证码
消息认证码 — 消息被正确传输了吗
从一个Alice和Bob的故事开始讲起,Alice和Bob分别是两家银行,Alice银行通过网络向Bob银行发送了一条汇款请求,Bob银行收到的请求内容是:从账户A向账户B汇款1000万元
当然,Bob银行所收到的汇款请求内容必须与Alice银行所发送的内容是完全一致的。如果主动攻击者Mallory在中途将Alice银行发送的汇款请求进行了篡改,那么Bob银行就必须要能够识别出这种篡改,否则如果Mallory将收款账户改成了自己的账户,那么1000万元就会被盗走。
话说回来,这条汇款请求到底是不是Alice银行发送的呢?有可能Alice银行根本就没有发送过汇款请求,而是由主动攻击者Mallory伪装成Alice银行发送的。如果汇款请求不是来自Alice银行,那么就绝对不能执行汇款。
现在我们需要关注的问题是汇款请求(消息)的“完整性"和“认证”这两个性质。
消息的完整性(integrity),指的是“消息没有被篡改"这一性质,完整性也叫一致性
- 如果能够确认汇款请求的内容与Alice银行所发出的内容完全一致,就相当于是确认了消息的完整性,也就意味着消息没有被篡改。
消息的认证(authentication)指的是“消息来自正确的发送者"这一性质
- 如果能够确认汇款请求确实来自Alice银行,就相当于对消息进行了认证,也就意味着消息不是其他人伪装成发送者所发出的
消息认证码(message authentication code
)是一种确认完整性并进行认证的技术,取三个单词的首字母,简称为MAC
消息认证码的输入包括任意长度的消息和一个发送者与接收者之间共享的密钥,它可以输出固定长度的数据,这个数据称为MAC值
根据任意长度的消息输出固定长度的数据,这一点和单向散列函数很类似。但是单向散列函数中计算散列值时不需要密钥,而消息认证码中则需要使用发送者与接收者之间共享的密钥
要计算MAC必须持有共享密钥,没有共享密钥的人就无法计算MAC值,消息认证码正是利用这一性质来完成认证的。此外,和单向散列函数的散列值一样,哪怕消息中发生1比特的变化,MAC值也会产生变化,消息认证码正是利用这一性质来确认完整性的
消息认证码有很多种实现方法,大家可以暂且这样理解:消息认证码是一种与密钥相关联的单向散列函数
单向散列函数与消息认证码的比较
认证步骤
我们还是以Alice银行和Bob银行的故事为例,来讲解一下消息认证码的使用步骤:
- 发送者Alice与接收者Bob事先共享密钥
- 发送者Alice根据汇款请求消息计算MAC值(使用共享密钥)
- 发送者Alice将汇款请求消息和MAC值两者发送给接收者Bob
- 接收者Bob根据接收到的汇款请求消息计算MAC值(使用共享密钥)
- 接收者Bob将自己计算的MAC值与从Alice处收到的MAC值进行对比。
- 如果两个MAC值一致,则接收者Bob就可以断定汇款请求的确来自Alice(认证成功);如果不一致,则可以断定消息不是来自Alice(认证失收)
使用场景
SWIFT
- 环球银行金融电信协会,是一个组织,为银行间的交易报价护航。银行间交互使用了
SWIFT
进行交互,这里面对消息的完成性和身份验证,就是使用了消息认证码- 最初使用消息认证码,由人工配送秘钥
- 后来使用非对称加密
- 环球银行金融电信协会,是一个组织,为银行间的交易报价护航。银行间交互使用了
https
ssl/tls
协议,里面的握手协议使用了消息认证码https = http + ssl/tls
(security socket layer)
IPSec
IP
协议的增强版,使用了消息认证码
HMAC
介绍
HMAC
是一种使用单向散列函数来构造消息认证码的方法(RFC2104
),其中HMAC的H就是Hash的意思
HMAC
中所使用的单向散列函数并不仅限于一种,任何高强度的单向散列函数都可以被用于HMAC
,如果将来设计出新的单向散列函数,也同样可以使用。
使用SHA-1
、MD5
、RIPEMD-160
所构造的HMAC
,分别称为HMAC-SHA-1
、HMAC-MD5
和HMAC-RIPEMD
HMAC生成消息认证码的内部实现
里面有哈希操作(4)(7)
通过上述流程我们可以看出,最后得到的MAC值,一定是一个和输入的消息以及密钥都相关的长度固定的比特序列
消息认证码的密匙配送问题
在消息认证码中,需要发送者和接收者之间共享密钥,而这个密钥不能被主动攻击者Mallory获取。如果这个密钥落入Mallory手中,则Mallory也可以计算出MAC值,从而就能够自由地进行篡改和伪装攻击,这样一来消息认证码就无法发挥作用了。
发送者和接收者需要共享密钥,这一点和我们介绍的对称加密很相似。实际上,对称加密的密钥配送问题在消息认证码中也同样会发生。秘钥的配送问题可以使用非对称加密的方式进行解决。
消息认证码无法解决的问题
假设发送者Alice要向接收者Bob发送消息,如果使用了消息认证码,接收者Bob就能够断定自己收到的消息与发送者Alice所发出的消息是否是一致的,这是因为消息中的MAC值只有用Alice和Bob之间共享的密钥才能够计算出来,即便主动攻击者Mallory篡改消息,或者伪装成Alice发送消息,Bob也能够识别出消息的篡改和伪装。
但是,消息认证码也不能解决所有的问题,例如“对第三方证明"和“防止否认”,这两个问题就无法通过消息认证码来解决。
对第三方证明
假设Bob在接收了来自Alice的消息之后,想要向第三方验证者Victor证明这条消息的确是Alice发送的,但是用消息认证码无法进行这样的证明,这是为什么呢?
首先,Victor要校验MAC值,就需要知道Alice和Bob之间共享的密钥。
假设Bob相信Victor,同意将密钥告诉Victor,即便如此,Victor也无法判断这条消息是由Alice发送的,因为Victor可以认为:“即使MAC值是正确的,发送这条消息的人也不一定是Alice,还有可能是Bob。"
能够计算出正确MAC值的人只有Aice和Bob,在他们两个人之间进行通信时,可以断定是对方计算了MAC值,这是因为共享这个密钥的双方之中,有一方就是自己。然而,对于第三方Victor、Alice或Bob却无法证明是对方计算了MAC值,而不是自己。而数字签名就可以实现对第三方的证明
防止否认
假设Bob收到了包含MAC值的消息,这个MAC值是用Alice和Bob共享的密钥计算出来的,因此Bob能够判断这条消息的确来自Alice。
但是,上面我们讲过,Bob无法向验证者Victo证明这一点,也就是说,发送者Alice可以向Victor声称:“我没有向Bob发送过这条消息。“这样的行为就称为否认(repudiation)。
Ailce可以说“这条消息是Bob自己编的吧“,“说不定Bob的密钥被主动攻击者Mallory给盗取了,我的密钥可是妥善保管着呢"等。说白了,就是Alice和Bob吵起来了。
即便Bob拿MAC值来举证,Victor也无法判断Aice和Bob谁的主张才是正确的,也就是说,用消息认证码无法防止否认(nonrepudiatlon)
GitHub(私有仓库)
数字签名
数字签名 — 消息到底是谁写的
数字签名是一种将相当于现实世界中的盖章、签字的功能在计算机世界中进行实现的技术。使用数字签名可以识别篡改和伪装,还可以防止否认。
我们假设Alice使用的密钥是一个只有Alice自己才知道的私钥。当Alice发送消息时,她用私钥生成一个“签名”。相对地,接收者Bob则使用一个和Alice不同的密钥对签名进行验证。使用Bob的密钥无法根据消息生成签名,但是用Bob的密钥却可以对Alice所计算的签名进行验证,也就是说可以知道这个签名是否是通过Alice的密钥计算出来的。如果真有这么一种方法的话,那么不管是识别篡改、伪装还是防止否认就都可以实现了,实际上,这种看似很神奇的技术早就已经问世了,这就是数字签名(digital signature)
实际上,数字签名和非对称加密有着非常紧密的联系。简而言之,数字签名就是通过将非对称加密“反过来用”而实现的,下面我们来将密钥的使用方式总结成一张表:
私钥 | 公钥 | |
---|---|---|
非对称加密 | 接收者解密时使用 | 发送者加密时使用 |
数字签名 | 签名者生成签名时使用 | 验证者验证签名时使用 |
注意:
- 签名的数据不是数据本身,而是数据的哈希值
消息认证问题的解决
- 无法有效的配送秘钥 => 数字签名中,不需要协商秘钥,没有配送需求
- 无法进行第三方证明 => 任何人都持有公钥,都可以帮助认证
- 无法防止发送方否认 => 私钥只有发送方持有,无法进行抵赖
数字签名存在问题
使用非对称加密进行数字签名存在的问题:公钥分发困难
- 直接传递公钥,容易被截取
- 放到固定的位置,容易被替换
总之,从技术层面,没有办法解决这个问题,但可以从社会层面解决:引入第三方认证机构,CA
(Certificate Authority
),CA机构是一系列具有社会公信力的机构的总称,它们负责为厂商提供数字证书,从而解决公钥分发困难问题
GitHub(私有仓库)
证书
证书 — 为公钥加上数字签名
windows
查看证书方式:certmgr.msc
要开车得先考驾照.驾照上面记有本人的照片、姓名、出生日期等个人信息,以及有效期、准驾车辆的类型等信息,并由公安局在上面盖章。我们只要看到驾照,就可以知道公安局认定此人具有驾驶车辆的资格。
公钥证书(Public-Key Certificate,PKC)其实和驾照很相似,里面记有姓名、组织、邮箱地址等个人信息,以及属于此人的公钥,并由认证机构(Certification Authority、Certifying Authority,CA)施加数字签名。
只要看到公钥证书,我们就可以知道认证机构认定该公钥的确属于此人。公钥证书也简称为证书(certificate)
可能很多人都没听说过认证机构,认证机构就是能够认定"“公钥确实属于此人”,并能够生成数字签名的个人或者组织。认证机构中有国际性组织和政府所设立的组织,也有通过提供认证服务来盈利的一般企业,此外 个人也可以成立认证机构。
证书的应用场景
下面我们来通过证书的代表性应用场景来理解证书的作用。
下图展示了Alice向Bob发送密文的场景,在生成密文时所使用的Bob的公钥是通过认证机构获取的。
认证机构必须是可信的,对于“可信的第三方”,下图中会使用Trent这个名字,这个词是从trust(信任)一词演变而来的。
上面就是利用认证机构Trent进行公钥密码通信的流程。其中1、2、3这几个步骤仅在注册新公钥时才会进行,并不是每次通信都需要。此外,步骤4仅在Alice第一次用公钥密码向Bob发送消息时才需要进行,只要Alice将Bob的公钥保存在电脑中,在以后的通信中就可以直接使用了。
- SSL:
- 非对称加密(证书)协商对称密钥
- 对称加密
证书标准规范X.509
证书是由认证机构颁发的,使用者需要对证书进行验证,因此如果证书的格式千奇百怪那就不方便了。于是,人们制足了证书的标准规范,其中使用最广泛的是由ITU(International Telecommumcation Union,国际电信联盟)和ISO(Intemational Organization for Standardization,国际标准化组织)制定的X.509
规范很多应用程序都支持X.509
并将其作为证书生成和交换的标准规范。
X.509
是一种非常通用的证书格式。所有的证书都符合ITU-T X.509国际标准,因此(理论上)为一种应用创建的证书可以用于任何其他符合X.509标准的应用。X.509证书的结构是用ASN1(Abstract Syntax Notation One)
进行描述数据结构,并使用ASN.1语法进行编码。
在一份证书中,必须证明公钥及其所有者的姓名是一致的。对X.509证书来说,认证者总是CA或由CA指定的人,一份X.509证书是一些标准字段的集合,这些字段包含有关用户或设备及其相应公钥的信息。X.509标准 定义了证书中应该包含哪些信息,并描述了这些信息是如何编码的(即数据格式)
一般来说,一个数字证书内容可能包括基本数据(版本、序列号)、所签名对象信息(签名算法类型、签发者信息、有效期、被签发人、签发的公开密钥)、CA的数字签名,等等。
SSL/TLS
SSL/TLS — 为了更安全的通信
- 早期的版本叫
SSL
(3.0之后叫TLS
) - 1.0
TLS
= 3.0SSL
- 1.1
TLS
= 3.1SSL
SSL/TLS
是世界上应用最广泛的密码通信方法。比如说,当在网上商城中输人信用卡号时,我们的Web刘览器就会使用SSL/TLS
进行密码通信。使用SSL/TLS
可以对通信对象进行认证,还可以确保通信内容的机密性。
SSL/TLS
中综合运用了之前所学习的对称密码、消息认证码、公钥密码、数字签名、伪随机数生成器等密码技术,严格来说,SSL
(Secure Socket Layer
)与TLS
(Transport Layer Security
)是不同的,TLS
相当于是SSL
的后续版本。
https通信流程
HTTPS = HTTP + SSL
- 服务器自己生成公钥私钥,也可以不自己生成,全部由CA帮助生成
- 服务器将公钥发送给CA机构
- CA机构也有自己的私钥公钥。CA使用自己的私钥对服务器的公钥进行签名
- 还有一些其他辅助信息(发行机构,主题,指纹)
- 公钥
- 签名
- CA向服务器颁发一个数字证书
- 当用户访问服务器的时候,服务器会将CA证书发送给用户
- 在用户的浏览器中,已经随着操作系统预装了知名CA机构的根证书,这里面包含了CA机构的公钥,浏览器就会对服务器的证书进行验证
- 如果验证成功,说明服务器可靠,可以正常通信
- 如果验证失败,显示(Not Secure),提示Warning
- 证书有效时,用户的浏览器会将自己支持的对称加密算法(des,3des,aes)发送给服务器,生成随机秘钥(对称),使用服务器的公钥,对上述信息加密(非对称)。发送给服务器
- 服务器选择一个加密算法,使用对称秘钥加密消息,发送给客户端
- 双方达成一致,接下来通信转换为对称加密
总结
- 所有的通信不再传输公钥进行认证,而是传输数字证书来认证
- 证书里面包含了公钥,可以由CA机构认证
常见证书格式
X.509
规范中一般推荐使用PEM
(Privacy Enhanced Mail)格式来存储证书相关的文件。
证书文件的文件名后缀一般为.crt
或.cer
。对应私钥文件的文件名后缀一般为.key
。证书请求文件的文件名后缀为.csr
。有时候也统一用pem
作为文件名后缀。
PEM
格式采用文本方式进行存储。一般包括首尾标记和内容块,内容块采用Bse64
进行编码。
编码格式总结:
X.509 DER
(Distinguished Encoding Rules
)编码,后缀为:.der
、.cer
、.crt
X.509 BASE64
编码(PEM
格式),后缀为:,pem
、.cer
、.crt
PEM
- Privacy Enhanced Mail
- 查看内容,以
-----BEGIN...
开头,以-----END...
结尾 - 我们使用openssl生成的都是pem格式的
- 查看PEM格式证书的信息:
|
|
DER
- Distinguished Encoding Rules
- 打开看是二进制格式,不可读
- Java和Windows服务器偏向于使用这种编码格式
- 查看DER格式证书的信息
|
|
证书信任链
证书之间是可以有信任关系的,通过一个证书可以证明另一个证书也是真实可信的。实际上,证书之间的信任关系,是可以嵌套的。比如:C信任A1,A1信任A2,A2信任A3,这个叫做证书的信任链。只要你信任链上的头一个证书,那后续的证书,都是可以信任的。
假设C证书信任A和B;然后A信任A1和A2;B信任B1和B2,则它们之间,构成如下的一个树形关系(一个倒立的树):
处于最顶上的树根位置的那个证书,就是“根证书”。除了根证书,其它证书都要依靠上一级的证书,来证明自己
生成自签名证书
自签名证书,自己颁发给自己,自己验证自己
下列两种方式生成的证书都是pem
格式的,可以导入到计算机。
方式一
- 创建一个目录如
Mytest
,进入该目录,在该目录下打开命令行窗口 - 启动
openssl
|
|
- 使用
openssl
工具生成一个RSA
私钥,注意:生成私钥,需要提供一个至少4位的密码,使用3des
对私钥进行加密
|
|
- 生成
CSR
(证书签名请求)
|
|
- 删除私钥中的密码,第一步给私钥文件设置密码是必须要做的,如果哦不想要可以删掉
|
|
- 生成自签名证书
|
|
方式二
不需要生成csr
,直接生成证书,没有指定Subject
相关的数据,所以还会引导输入
|
|
-nodes
:不设置密码
解析证书:
|
|
公钥基础设施
仅制定证书的规范还不足以支持公钥的实际运用,我们还需要很多其他的规范,例如证书应该由谁来颁发,如何颁发,私钥泄露时应该如何作废证书,计算机之间的数据交换应采用怎样的格式等。公钥基础设施能够使公钥的运用更加有效。
公钥基础设施(Public-Key infrastructure
)是为了能够更有效地运用公钥而制定的一系列规范和规格的总称。公钥基础设施一般根据其英语缩写而简称为PKI
PKI
只是一个总称,而并非指某一个单独的规范或规格。例如,RSA
公司所制定的PKCS
(Public-Key Cryptography Standards
,公钥密码标准)系列规范也是PKI
的一种,而互联网规格RFC
(Request for Comments
)中也有很多与PKI
相关的文档。此外,X.509
这样的规范也是PKI
的一种。在开发PKI
程序时所使用的由各个公司编写的API
(Application Programming Interface
,应用程序编程接口)和规格设计书也可以算是PKI
的相关规格。
因此,根据具体所采用的规格,PKI
也会有很多变种,这也是很多人难以整体理解PKI
的原因之一。
为了帮助大家整体理解PKI,我们来简单总结一下PKI的基本组成要素(用户、认证机构、仓库)以及认证机构所负责的工作。
用户:使用PKI
的人
用户就是像Alice、Bob这样使用PKI
的人。用户包括两种:一种是希望使用PKI
注册自己的公钥的人,另一种是希望使用已注册的公钥的人。我们来具体看一下这两种用户所要进行的操作。
-
注册公钥的用户所进行的操作
- 生成密钥对(也可以由认证机构生成)
- 在认证机构注册公钥
- 向认证机构申请证书
- 根据需要申请作废已注册的公钥
- 解密接收到的密文
- 对消息进行数字签名
-
使用已注册公钥的用户所进行的操作
- 将消息加密后发送给接收者
- 验证数字签名
认证机构:颁发证书的人
认证机构(Certification Authority,CA
)是对证书进行管理的人。上面的图中我们给它起了一个名字叫作Trent
。认证机构具体所进行的操作如下:
- 生成密钥对(也可以由用户生成)
- 生成密钥对有两种方式:一种是由PKI用户自行生成,一种是由认证机构来生成。在认证机构生成用户密钥对的情况下,认证机构需要将私钥发送给用户,这时就需要使用PKCS#12(Personal Information Exchange Syntax Standard)等规范。
- 在注册公钥时对本人身份进行认证,生成并颁发证书
- 作废证书(
Certificate Revocation List
),简称为CRL
仓库:保存证书的数据库
仓库(repository)是一个保存证书的数据库,PKI
用户在需要的时候可以从中获取证书,它的作用有点像打电话时用的电话本。在本章开头的例子中,尽管没特别提到,但Alice获取Bob的证书时,就可以使用仓库。仓库也叫作证书目录。