本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/27
总结下《图解密码技术》的核心点,自己忘的时候瞅一眼。
密码
对称密码(symmetric cryptography)
加密和解密使用同一密匙。
公钥密码(public-key cryptography)
加密和解密使用不同密钥的方式。
单向散列函数(one-way hash function)
计算散列值,保证的不是机密性,而是完整性。
消息认证码(message authentication code)
确认消息是否被篡改,而且能够确认消息是否来自所期待的通信对象。
数字签名(digital signature)
为了防止通信方的否认
伪随机数生成器(pseudo random number generator,PRNG)
模拟产生随机数的算法
对称密码
DES(Data Encryption Standard)
1977年美国联邦信息处理标准所采用的一种对称密码,现在已经能被暴力破解。
DES是一种将64比特的明文加密成64比特密文的对称密码算法,密匙长度是56比特。但由于每隔7比特会设置用于错误检查的比特,因此密匙长度实际是56比特。
DES基本结构也被称为Feistel网络,加密的各个步骤称为轮,整个加密的过程就是进行若干次轮的循环。DES是一种16轮循环的Feistel网络。
但是这样一来右侧根本没有加密,因此我们需要用不同的子密匙对一轮的处理重复若干次,并在两轮处理之间将左侧和右侧数据对调。
DES破解消息:http://wenku.baidu.com/link?url=0UrkWjEU0SsXN4uA059-vm_e32e3YxflhD4X6J3dU_aupbTSog70EabsJ4u6YMldfygILs8BX1q7DI0GuAhdw2Qud6IywUVXt04ybUrB44K
AES(Advanced Encryption Standard)
取代DES成为新标准的一种对称密码算法。在2000年从候选算法中选出了一种名为Rijndael的对称密码算法。
分组长度是128比特。在AES规格中,密匙长度只有128、1192、256比特三种。
- 逐个的对16字节的输入数据进行SubBytes处理。即以每个字节的值(0~255)为索引进行替换,即将一个字节的值替换成另一个字节的值。
- 将输出以字节为单位进行打乱处理(有规律的)
- 以一个4字节的值进行比特运算,将其变成另外一个4字节的值。
- 将输出与轮密匙进行XOR。
Rijndael需要重复进行10~14轮计算。
目前为止还没有出现针对Rijndael的有效攻击。
分组密码的模式
模式
分组密码只能加密固定长度的分组,但是我们要加密的长度可能会超过分组密码的分组长度,这是就需要对分组密码算法进行迭代,迭代的方法就是分组密码的模式。
ECB模式
相同的明文会被转换为相同的密文。只要观察一下密文,就知道明文存在怎样的重复组合,并以此为线索破解密码。并且在ECB模式中,每个明文分组都各自独立的加密和解密,攻击者可以改变密文分组的顺序。
不推荐使用。
CBC模式
每次加密时都会随机产生一个不同的比特序列来作为初始化向量。
CFB模式
OFB模式
CTR模式
计数器
前8字节为nonce,这个值在每次加密时必须不同(防止具有相同的分组序号的相同明文加密后产生相同的密文)。后8字节为分组序号,这个部分会逐次累加。
能够以任意顺序处理分组,就意味着能够实现并行计算。
公钥密码
解决密匙配送问题:
- 共享密匙
- 密匙分配中心
- Diffie-Hellman
- 公钥密码
公钥密码处理速度只有对称密码的几百分之一。
RSA
一种公钥算法。
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
一旦发现了对大整数进行质因数分解的高效算法,RSA就能被破译。
中间人攻击
仅靠公钥密码本身,是无法防御中间人攻击的。要防御中间人攻击,还需要一种手段来确认所收到的公钥是否真的属于Bob,这种手段称为认证。
混合密码系统
公钥密码的问题:
- 处理速度远低于对称密码。
- 难以抵御中间人攻击。
混合密码系统的组成机制:
- 用对称密码加密消息
- 通过伪随机数生成对称密码加密中使用的会话密钥
- 用公钥密码加密会话密钥
- 从混合密码系统外部赋予公钥密码加密时使用的密钥
用对称密码提升明文的加密速度,用公钥密码提升对称密码的密匙的安全性(因为对称密码的密匙长度相对于明文长度很短,所以公钥密码速度慢的问题可以被忽略)。
单向散列函数
也叫消息摘要函数,哈希函数或者杂凑函数。
用来确认明文的完整性,或者是否被篡改。
单向散列函数有一个输入和输出,输入称为消息,输出称为散列值。
弱抗碰撞性
找到和该消息具有相同散列值的另一条消息是非常困难的。
强抗碰撞性
找到相同散列值的两条不同的消息是非常困难的。
具体例子
MD4、MD5
MD4产生128比特的散列值,现在已经不安全。
MD5产生28比特的散列值,目前MD5的强碰撞性已经被攻破。
SHA-1、SHA-256、SHA-384、SHA-512
SHA-1产生160比特的散列值,强碰撞性于2005年被攻破。
SHA-256、SHA-384、SHA-512统称SHA-2,散列长度分别为256,384,512比特。
SHA-2尚未被攻破。
查看php支持的哈希函数
<?php
print_r(hash_algos());
php使用哈希函数范例:
<?php
echo hash(‘sha256‘, ‘abc‘);
echo hash(‘sha512‘, ‘abc‘);
// md5, sha1.. 等等也都可以用此寫法
echo hash(‘md5‘, ‘abc‘);
echo hash(‘sha1‘, ‘abc‘);
单向散列函数并不是加密算法
针对单向散列函数的攻击
暴力破解
为了破解弱抗碰撞性,可以使用暴力破解。以SHA-1为例,散列值长度值为160比特,因此最多尝试2的160次方就能找到目标消息了。
生日攻击
为了破解强抗碰撞性。
设想在N个人中,如果要保证至少两个人的生日一样的概率大于二分之一,N至少多少?
先算N个人生日全都不一样的概率(过程略了),N=23。
当Y非常大时,N约等于Y的平方根。
以160比特的散列值为例,N=2^80时,生日攻击有二分之一的概率成功。
单向散列函数可以辨别篡改,但是无法辨别伪装。此时我们需要认证技术。
参考:php处理密码的几种方式
https://jellybool.com/post/php-password-hash-in-the-right-way
消息认证码
message authentication code(MAC),输入包括消息和共享密钥,可以输出固定长度的数据。
但是还是那个恒久的问题:共享密钥配送问题。如果共享密钥落入第三方手中,消息认证码就无法发挥作用了(解决方法还是上面提过的四种解决方法)。
实现方法
- 单向散列函数实现
使用SHA-1、MD5之类的单向散列函数可以实现,其中一种实现方法叫做HMAC。 分组密码实现
HMAC
H是HASH的意思。
对消息认证码的攻击
重放攻击
防御方法:
- 序号
约定每次发送都发送一个递增的编号,并在计算MAC值时将序号也包含在消息中,这样攻击者无法计算序号递增之后的MAC值。 - 时间戳
约定发送消息时包含当前时间,如果收到以前的消息即使MAC正确也丢弃。但是需要考虑通信延迟,必须在时间的判断上留下缓冲。 nonce
在通信之前,接受者先向发送者发送一个一次性的随机数nonce。发送者在消息中包含这个nonce并计算MAC值。由于每次nonce都会变化,因此无法重放攻击。无法解决的问题
- 对第三方的证明
防止否认
数字签名
消息认证码之所以无法防止否认,是因为发送者和接受者共享密钥。对于第三方来说,我们无法证明这条消息是由发送发生成的,因为接收方也可以伪造此消息。
所以,我们可以模仿公钥密码,发送方使用只有自己知道的密钥。这样只有发送方可以正确解密的消息,这样就可以防止发送方的否认。
但是如果数字签名的生成者说:“我的私钥被别人窃取了”,怎么办?报警吧,哈哈哈
但是数字签名还是无法防止中间人攻击,要防止,必须确认自己得到的公钥是否真的属于自己的通信对象。可以打电话核对公钥的散列值,也可以使用公钥证书。
证书
公钥证书记有姓名、组织、邮箱地址等个人信息,以及属于此人的公钥,并由认证机构(CA)施加数字签名。只要看到公钥证书,我们就知道认证机构认定该公钥的确属于此人。
其中(1)(2)(3)仅在注册新公钥才会进行,并不是每次通信需要,(4)仅在发送方第一次用公钥密码向接受者发送消息才会进行,公钥可以保存在电脑中。
公钥基础设施(PKI)
公钥基础设施,为了更有效的运用公钥而制定的一系列规范和规格的总称。
规范包括证书由谁颁发,如何颁发,私钥泄露时应该如何作废证书,计算机之间的数据交换应该采用怎样的格式。
基本要素
- 用户
- 认证机构
- 仓库
认证机构(CA)
认证机构的工作:
- 生成密钥对(也可由用户生成)
- 注册公钥时对本人身份进行认证
- 生成并颁发证书
作废证书
作废证书与CRL
做作废证书,认证机构需要制作一张证书作废清单CRL,PKI用户需要从认证机构获取最新的CRL,并查询自己要用于验证签名的公钥是否作废。
证书层级结构
对证书的攻击
- 在公钥注册之前攻击
- 注册相似人名进行攻击
- 窃取私钥
- 伪装成认证机构
钻CRL空子
随机数
性质
- 随机性
- 不可预测
不可重现
伪随机数生成器的算法是公开的,但是种子是保密的。
仅靠软件无法生成真随机数。
具体的伪随机数生成器
线性同余法
不具备不可预测性,不可用于密码技术。C语言的库函数rand,Java的java.util.Random类采用此方法。
单向散列函数
具备不可预测性。
密码法
要破解不可预测性,必须破译密码。
对种子攻击
如果被攻击者知道了种子,那么就能知道这个伪随机数生成器生成的全部伪随机数列。
要避免种子被攻击者知道,我们需要使用不可重现性的真随机数作为种子。例如Linux系统中的/dev/random文件就是一个根据硬件设备驱动收集的背景噪声存储真随机数的随机数池。
密钥
会话密钥:仅限于本次通信,下次通信不能使用。
主密钥:一直被重复使用。
基于口令的密码(PBE)
我们需要保证消息的重要性,需要用密钥(CEK)对消息进行加密,但是需要保证CEK的机密性,用另一个密钥(KEK)对CEK进行加密,但是还是需要保证KEK的机密性,陷入了死循环?
- 生成KEK
- 生成会话密钥并加密
加密消息
盐的作用
防御字典攻击。字典攻击是一种实现进行计算并准备好候选密钥列表的方法。
字典攻击:http://baike.baidu.com/link?url=Zma4cc9nFzhmdM0cmLiHLvGm65dUQRXzfLiK_o5SRPge05lCfh1MQnLGgx937mZuRMycHWJ_njK9zfX3KA-ISK