程序猿应该了解的密码技术

目录

1. 概述

2. 加密与解密技术

2.1 对称密码

几种典型的对称密码介绍

对称密码的选择

分组密码的模式

2.2 公钥密码

公钥密码算法RSA

其他公钥密码

2.3 混合密码系统

加密过程

3. 认证

3.1 单向散列函数

几种单向散列函数介绍

单向散列函数无法解决的问题

3.2 消息认证码

消息认证码的使用

消息认证码的重放攻击问题

消息认证码无法解决的问题

3.3 数字签名

数字签名的使用方式

数字签名无法解决的问题

3.4 证书

证书的使用流程

证书的组成

4. 密码学的两个典型应用

4.1 Https

SSL/TLS协议的握手过程

SSL/TLS协议的消息加密过程

4.2 比特币

先介绍几个概念

比特币转账过程

5. 结尾


1. 概述

可能有些人对密码学有个误区,认为密码学就是为了解决机密性的问题,就像很多人会把MD5当成一种加密算法去使用。之所以存在这样的误区,主要问题在于对密码学没有一个整体的认识,对密码学的了解过于碎片化。因此写这篇文章主要目的是为了帮助你梳理密码技术重要的知识点,能帮你对密码学形成一个系统的知识体系。

本文主要分加密与解密、认证两部分对密码学做一个整体的介绍与分析(正如下面的思维导图给出的结构)。同时文章最后会通过剖析Https比特币用到的密码学知识,加深对密码学的理解与运用。

程序猿应该了解的密码技术_第1张图片

(说明:本文篇幅有限,不会对具体加密算法的实现原理做详细介绍。)

 

密码学按照应用目的划分为两块:

  • 加密与解密:为解决机密问题而生(保证私密性),也就是我们经常说的密码。例如使用对称密码或者公钥密码对一串明文进行加密。
  • 认证:为解决信息是否被篡改的完整性以及确认消息是否真的属于某人,它并不是为了加密。例如对一串明文取单向散列函数,我们可以理解为取明文的数字指纹,它只是为了确保消息是否被篡改(在实际使用的时候明文也会发送给接收者,因此认证并不是为了加密)。

2. 加密与解密技术

2.1 对称密码

对称密码是一种使用相同的密钥进行加密和解密的技术,也称为共享密钥密码。

介绍几种对称密码之前,先讲一个概念:

分组密码:每次只能处理特定长度的的一块数据。如果长度超过固定的长度,则进行迭代。而这里的迭代我们称为模式。

几种典型的对称密码介绍

  • DES

将64bit的明文加密成64bit密文的对称密码算法,密钥长度是56bit。DES每次只能加密64bit的数据,这里的64bit称为分组(分组长度为64bit),如果要加密的明文比较长,则加密时需要进行迭代(也就是分组密码的概念),如何迭代则称为模式。

  • 3DES

三重DES,为了增强DES的强度,将DES重复3次所得到的一种密码算法。DES的密钥长度为56bit,因此3DES的密钥长度为168bit。

注意:这里的三重是通过加密->解密->加密过程得到,经过的三次DES密钥都不一样。如下图:

程序猿应该了解的密码技术_第2张图片

  • AES

取代DES而成为新标准的一种对称密码算法。 分组长度固定为128bit,密钥长度可选128、192、256bit。

  • Rijndael

可以理解为AES的另外一种密码算法,相比AES,它的分组长度可以为32、128、256三种。

对称密码的选择

目前DES与3DES的安全性已经被AES所取代,因此对称密码尽量选择AES。

分组密码的模式

由于分组密码只能加密固定长度的数据,因此需要通过使用模式进行迭代处理。例如现在需要用DES加密长度128bit的数据,我们可以将128bit拆分为两组,分组处理之后合并得到密文。这种迭代的方式就叫模式,但实际上这种分组模式具有很大的弱点,这种模式也称文ECB模式,不推荐使用。

  • ECB模式

不推荐使用,电子密码本模式,将明文分组加密之后的结果将直接成为密文分组。 程序猿应该了解的密码技术_第3张图片

  • CBC模式

密文分组链接模式,将明文分组与前一个密文分组进行异或运算,然后再进行加密得到一组密文,然后又作为后一组的明文。当加密第一个明文分组时,由于不存在前一个密文分组,因此需要一个比特序列,这个比特序列称为初始化向量,也叫偏移量。如下图:

程序猿应该了解的密码技术_第4张图片

  • 其他模式与模式的选择

其他模式还有:CFB、OFB、CTR模式。关于三个模式的介绍,这里不做说明。在模式的选择上,除了不应该选用ECB模式之外,其余的模式都可以选择,另外各自有各自的长处与短处,这里不做说明。

2.2 公钥密码

  • 用公钥加密,用私钥解密,也称为非对称密码。

  • 在对称密码中,由于加密与解密的密钥相同,必须解决密钥配送的问题,如果使用公钥密码,则无需向接受者配送用于解密的密钥。

  • 公钥通信流程:

程序猿应该了解的密码技术_第5张图片

  • 公钥密码无法解决的问题

这里我们仔细分析上面的通信过程,会发现如果接收者在将公钥发送给发送者的时候,被黑客窃取,然后伪造成自己是发送者,将自己的公钥发送给发送者,发送者用黑客的公钥加密消息,黑客再将加密的消息拦截到,用自己的私钥解密,成功解密了发送者的消息。

这个问题叫做公钥认证问题。也叫中间人攻击,后面认证部分讲到。

公钥密码算法RSA

RSA加密与解密

C为密文,M为明文,公钥为E和N,私钥为D和N

C = M^EmodN

M = C^DmodN

密文=明文的E次方除以N的余数

明文=密文的D次方除以N的余数

生成RSA密钥对过程:

  1. 求N:用伪随机数生成两个质数p、q。N=p*q。
  2. 求L:L是p-1和q-1的最小公倍数,L只是参与产生密钥对的中间数。
  3. 求E:利用伪随机数生成1到L之间的候选数,然后从候选数中选择一个与L的最大公约数为1,这个候选数即为E。这时候已经求得了公钥E和N。
  4. 求D:D由E计算而来,必须满足:1
  • 注意:这里可能有人会有疑问,既然能通过E求出D,而E又是公钥的一部分,那岂不可以直接算出D,从而得到私钥

  • 仔细看求D满足的条件,这里有一个非常重要的参数L,L是由p和q得到,而p*q=N,N是知道的,那是否可以由N得到p和q,从而得到L呢?实际上这属于质因数分解的问题。也就是说只要能将N分解出p和q,就能破解出密钥。然而实际上对一个大整数进行质因数分解是一件非常困难的事情。

总结:通过公钥计算出私钥的难度在于对一个大整数进行质因数分解,这其实是一个数学难题。这正是RSA生成的密钥对能保证私钥安全的关键所在

其他公钥密码

  • EIGamal:利用了mod N下求离散对数的困难度。存在一个缺点:加密的密文长度会变成明文的两倍。

  • Rabin:利用了mod N下求平方根的困难度。

  • 椭圆曲线密码:所需要的密钥长度比RSA短。比特币所使用的公钥密码算法正是椭圆曲线密码。

2.3 混合密码系统

用对称密码对明文进行加密,用公钥密码来加密对密码中的密钥。

在讲对称密码的时候提到过对称密码中密钥配送的问题,因此可以用公钥密码解决密钥配送的问题。而实际上公钥密码存在一个加密解密速度慢的问题:处理速度远远低于对称密码。

因此将对称密码与公钥密码优势结合利用,这就是我们这里说的混合密码系统。

加密过程

程序猿应该了解的密码技术_第6张图片

加密过程图说明

  1. 左半部分为公钥密码加密对称密码的密钥过程,右半部分为对称密码加密明文过程。

  2. 其中会话密钥即为用对称密码加密明文消息的密钥。这个会话密钥用接收者的公钥进行加密,同密文一起发送给接收者。

  3. 接受者用自己的私钥解密出会话密钥,利用会话密钥解密密文得到明文消息。

 


3. 认证

3.1 单向散列函数

通过单向散列函数可以计算出消息的散列值,可以理解成为消息的指纹,通过对比指纹,可以知道两条消息是否一致。单向散列函数也叫哈希函数、消息摘要函数。

**单向散列函数用于确保消息的完整性以及防止篡改。**例如可以通过单向函数提取出一个文件的散列值,如果散列值发生了变化,代表文件被修改了。(实际我们在某个网站下载某个文件的时候,网站会给这个文件的散列值方便你确定文件是否正确)

单向散列函数具有以下特点:

  • 根据任意长度的消息计算出固定长度的散列值。
  • 消息不同散列值也不同(具备抗碰撞性)。
  • 单向性。

几种单向散列函数介绍

  • MD4、MD5

产生128bit的散列值。我们常用的MD5值为32位,是按照16进制转换而来,目前MD4、MD5已经相对不安全,可以在很短时间内通过碰撞产生具备相同散列值但是不同的消息。

  • SHA-1、SHA-2

SHA-1:产生160bit的散列值。其抗碰撞性已被攻破。

SHA-2:SHA-256、SHA-384、SHA-512统称为SHA-2,这些后面的数字代表的是各自能产生多长的散列值,例如SHA-256产生256bit的散列值。

  • RIPEMD-160 产生160bit的散列值,比特币中有使用到。

  • SHA-3

单向散列函数无法解决的问题

前面我们讲到单向函数可以解决消息完整性的问题(是否被篡改),但是这里存在一个问题,这里举一个示例:

A发送消息给B,同时发送了消息的散列值,但是如果中间出现C将A的消息拦截到,然后伪装成A发送一段伪装的消息以及散列值给B,实际上B拿到这个消息发现是完整的,但实际上这个消息并不是A发出的,也就是说这里存在伪装的问题,也就是无法解决消息是否真正属于发送者的问题。这种问题可以归纳为消息的认证,而解决这类问题可以通过:消息认证码和数字签名解决,也就是后面部分讲的。

3.2 消息认证码

在讲单向散列函数的时候,我们讲到单向函数可以解决消息完整性的问题(是否被篡改),但是无法解决伪装的问题,而消息认证码可以解决。那么消息认证码是如何解决的呢?我们往下看。

  • 消息认证码,可以理解成是单向散列函数的一种应用,通过发送者和接受者直接共享一个密钥,将消息与密钥通过单向散列函数获取到散列值的一种认证技术。消息认证码是加了共享密钥的一种单向散列函数(带密钥的散列函数)。这个散列值也叫消息的MAC值。

  • 消息认证码除了可以通过单向散列函数(SHA-2、MD5)实现之外,还可以通过对称密码算法实现,例如通过AES加密得到密文,将明文与密文一起发出。

消息认证码的使用

这里以A向B发起转账100块钱请求为例。

程序猿应该了解的密码技术_第7张图片

程序猿应该了解的密码技术_第8张图片

说明:消息认证码关键在于加了一个只有发送者和接收者才有的一个共享密钥。

消息认证码的重放攻击问题

在上面的转账100的示例中,黑客可以不用通过窃取共享密钥来伪装转账请求,而是通过拦截到转账请求,然后不断发起这个转账请求,达到重复转账的目的。

解决办法:

  1. 增加序号求MAC值,接收方记录最后一个消息的序号值。
  2. 增加时间戳求MAC值;
  3. 增加随机数nounce求MAC值;接收者先向发送者发送一个一次性随机数,发送者在消息中包含随机数计算MAC值。

消息认证码无法解决的问题

我们仔细思考下上面A向B发起转账的例子,如果这里A反悔说我没有发起这笔转账,或者在向第三方证明这个是不是由A发出的,其实是无法证明的。因为这个共享密钥B也知道,A可以声称是B自己给自己发的,或者抵赖说是B把密钥泄露给了其他人。

这里也就是说的消息认证码存在无法向第三方证明和防止否认的问题。 而接下来要讲的数字签名可以解决这两个问题。

3.3 数字签名

数字签名相当于现实世界中的盖章和签字,不但可以解决篡改和伪装,还可以防止否认。

数字签名相当于是公钥密码反过来使用的一种应用,这里回顾一下公钥密码加密的用法是:

公钥密码:发送者用接收者的公钥加密明文得到密文,将密文发送给接收者,接收者用自己的私钥解密出明文消息。

数字签名则是利用私钥通过公钥密码算法对消息加密得到签名值,发送者将明文和签名值一同发送给接收者,接收者用公钥对签名解密得到明文,和发送者的明文比较是否一致,来确保消息是否已经被篡改并且确保消息是否属于发送者。

注意:这里的密钥对是由发送者生成的,而公钥密码中密钥对是由接收者生成的

  • 公钥密码与数字签名的比较:

程序猿应该了解的密码技术_第9张图片

数字签名的使用方式

数字签名的时候,是直接利用私钥对消息进行签名,如果消息过长,由于公钥密码加密过程很慢,因此不建议直接对消息进行签名。

正确使用方式:先通过单向散列函数计算消息的散列值,然后用私钥对散列值进行数字签名。

程序猿应该了解的密码技术_第10张图片

数字签名无法解决的问题

在上图数字签名的过程图中,如果攻击者在第2步拦截到,用自己的公钥发送给接收者,然后继续拦截第5步,使用自己的私钥签名消息,则接收者拿到的消息就是伪造的,因此归根到一个问题:公钥被篡改

这里我们似乎陷入了死循环的问题,数字签名是为了解决防止篡改伪造的认证问题,但是数字签名使用的公钥又可能被篡改伪造。为了解决这个问题,必须引入一个可信的第三方:证书。我们继续往下看:

3.4 证书

证书是为了解决公钥属于谁的问题,我们可以简单理解为证书就是为公钥加上数字签名。 证书由认证机构(CA)颁发给公钥所属人。证书按应用可以分为:服务器证书、电子邮件证书、代码签名证书等。我们接触比较多的就是所谓的服务器证书所代表的HTTPS。

证书的使用流程

这里用一个通信的流程图说明证书是如何发挥作用的:

  • 证书的签发过程: 用户首先产生自己的密钥对,并将公共密钥及部分个人身份信息(CSR)传送给认证中心(CA)。认证中心在核实身份后,将执行一些必要的步骤,以确信请求确实由用户发送而来,然后,认证中心将发给用户一个数字证书,该证书内包含用户的个人信息和他的公钥信息,同时还附有认证中心的签名信息。用户就可以使用自己的数字证书进行相关的各种活动。

证书的组成

证书=公钥+申请者与颁发者信息+签名;

程序猿应该了解的密码技术_第11张图片

  • 证书的层级结构

在上面的证书流程图中,发送者会利用认证机构的公钥去验证接收者B的公钥是否合法,这里我们默认了认证机构的公钥是合法的,但实际上这里并不是默认了这里的认证机构是合法的,而是因为有其他认证机构对这个认证机构的公钥施加了数字签名,即生成了一张认证机构的公钥证书。一个认证机构认证另外一个认证机构。。。似乎这里有点乱,我们画个图梳理下。

Root CA:这里为了证明公钥的合法性,似乎出现了无求的问题,也就是说一定要有一个根,我们默认这个根是合法的,这里的根就是处于最上层的认证机构,我们称之为根CA。

4. 密码学的两个典型应用

4.1 Https

我们知道Http协议都是明文传输的,意味着其他人可以随意获取到传输的明文信息,因此有了Https,使用Https传输信息和接受信息都是以密文传输的。

如上图所示,在 HTTPS 加密中真正起作用的其实是 SSL/TLS 协议。SSL/TLS 协议作用在 HTTP 协议之下。 即Https=Http+SSL/TLS。

  • 在了解Https通信过程之前,我们先思考三个问题:
  1. 如何保证A发送消息给B的消息不会被窃取到?
  2. 如何保证A发送消息给B不会被篡改?
  3. 如何保证B是真正的通信方?
  • 思考解答:
  1. 属于机密性,因此可以使用对称密码。
  2. 属于完整性问题,可以考虑使用消息认证码。
  3. 属于通信对象认证问题,用数字签名生成的证书解决。

关于上面两个问题关键都有一个共享密钥的问题需要解决,而这个共享密钥并不能简单地直接发送给对方,因此这里又一个SSL/TLS很重要的一个过程:SSL/TLS的握手过程

SSL/TLS协议的握手过程

程序猿应该了解的密码技术_第12张图片

过程说明

  1. 客户端生成一个随机数(Client random)发送给服务端,同时还会把自己支持的密钥套件清单等发送给服务端。
  2. 服务端也生成一个随机数(Server Ramdom),然后把这个随机数、自己的数字证书、使用的密码套件等发送给客户端。
  3. 客户端验证证书合法性,然后生成一个新的随机数,这个随机数叫做预备主密码(Premaster secret),然后用证书的公钥加密这个预备主密码给服务端。
  4. 服务端用自己的私钥解密出这个预备主密码。
  5. 客户端和服务端用前面交换的三个随机数:Client random、Server Ramdom、Premaster secret生成出主密码(Session Key)。

Session key(主密码):从上面的握手过程我们发现握手过程就是为了协商出这个主密码。这个数字非常重要,这个数字只是一次会话有效,之后的数据真正的通信过程全部依靠这个数值。

Session key(主密码)的具体作用:

1、生成通信过程中加密用的对称密码的公共密钥和初始化向量(分组密码模式需要的值)。

2、消息认证码的公共密钥。

SSL/TLS协议的消息加密过程

通过握手协议通信双方得到了会话的主密码,既可以得到了消息认证码的共享密钥A,以及对称密码需要的共享密钥B。那么对消息进行加密需要的条件都已经具备了,消息加密过程如下图:

说明:这里给的消息加密过程非常简单,但实际上这里面加密的过程还涉及到分段压缩等过程,这里没有具体说明。

4.2 比特币

在这之前,你可能听说比特币是基于密码学的一种技术,那我们这里就来看看比特币用到了密码技术的哪些东西。

有一点需要说明:比特币不是为了保证交易的的私密性,而是为了保证比特币拥有者的控制权与真实性(资金归谁的问题),因此比特币用到的密码学知识是我们前面讲的第二部分认证的内容(例如数字签名,单向散列函数等),而非加密的内容。

先介绍几个概念

  • 比特币P2P网络

比特币是去中心的核心就在于依赖这个P2P网络,全世界所有的比特币用户组成的网络构成了比特币的P2P网络。我们在理解比特币的时候不应该只是理解成比特币是一种虚拟货币,而应该理解成比特币是一个基于P2P点对点的网络支付系统。

  • 比特币钱包和地址

现实中转账我们需要支付宝钱包账号或者银行卡,那么同样的道理转账比特币也需要钱包。钱包决定了你对比特币的控制权。而钱包里面关键的就是用于接收比特币的公钥和用于发送比特币的私钥。而这对密钥就是比特币使用数字签名保证比特币属于真正拥有者的关键之处。

比特币的公钥:通过椭圆曲线乘法可以从私钥计算得到公钥,这是不可逆转的过程:K = k * G 。其中k是私钥,G是被称为生成点的常数点,而K是所得公钥。其反向运算,被称为“寻找离散对数”——已知公钥K来求出私钥k——是非常困难的,就像去试验所有可能的k值,即暴力搜索。

比特币地址:比特币交易都是在比特币地址之间完成的。比特币的地址是由钱包公钥通过两次单向散列函数求出散列值,再通过Base58Check编码得到。

关于私钥到比特币的地址生成过程看下面两个图:

  • 区块链

区块链就是保存了比特币全部交易的公共账本,由一个个区块通过链表的形式组成。而每一个区块上面包含了数条转账记录以及一个区块头。

区块头(HEADER):主要保存了上一个区块头的散列值(Previous Block Hash)以及本身的区块头散列值(Merkle Root)。区块头的散列值是根据区块包含所有交易数据计算出的散列值。

比特币转账过程

这里假设A向B转账1个BTC。

  1. A创建一笔转账1个BTC的交易,这笔交易指定了发送到B的钱包地址,同时用自己的私钥对转账施加数字签名。
  2. A将这笔交易以及签名发送到P2P网络(全世界网络广播这笔交易)
  3. 比特币网络中的所有人都可以通过转账人地址计算出A的公钥,验证签名是否正确,即确认支付者在该时刻对所交易的比特币是否拥有所有权。
  4. 矿工打包A的交易与此时其他人交易到一个区块,并添加到区块链。
  5. 由于这笔交易是转账到B的钱包地址,因此只有拥有这个地址对应的私钥的人才能归属到这一笔转账,因此比特币网络的账本中就记录了B拥有这一个比特币。

5. 结尾

当我们遇到需要利用到密码学知识去解决实际问题的时候,我们脑海里面应该有一个这样的清单:

  • 对称密码
  • 公钥密码
  • 单向散列函数
  • 消息认证码
  • 数字签名
  • 伪随机数生成器

同时要明白你要解决的问题是机密性问题还是认证性问题,就像前面聊的Https和比特币两大应用,比特币因为没有需要解决机密性问题,因此不存在用对称密码或者公钥密码去加密转账的说法,而只有认证需要用到的数字签名和单向散列函数。而像Https既包含了机密性问题,又包含了认证的问题,因此Https包含了大量的密码学知识,既有加密解密,也有认证。


本文内容出处:图解密码技术

你可能感兴趣的:(密码学,密码学,网络,区块链,比特币,java)