vlambda博客
学习文章列表

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

解决HTTP安全的方法就是采用HTTPS,而HTTPS本质上就是对密码学算法的组合,因此我们必须掌握最基本的密码学基础知识。

还记得上篇文章说密码学的几个目标吗?(请记住这三个目标,将是贯串后续几篇文章的主题)

  • (1)机密性

  • (2)完整性

  • (3)身份验证和不可抵赖性

第一个目标是机密性,这是基本目标,本篇文章首先学习经典的密码学算法:对称加密算法和公开密钥算法。

一、对称加密算法-简介

什么是数据加密?

将一段数据处理成无规则的数据,除非有关键的密钥,否则谁也无法得知无规则数据的真实含义。

对称加密算法用一个密钥来加密信息,同样地,也是用这一个密钥来解密。注意,全程只有一个密钥。

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

因此,不难理解,对称加密中“对称”的意思就是指加密和解密使用的是同一个密钥。

加密和解密操作是一个互逆的过程,算法的背后是复杂的数学知识,不在本系列研究和谈论范围。

对称加密算法有很多种实现,其中AES算法比较出名,该算法是对称加密算法的标准算法。

在对称加密算法中,密钥长度是非常关键的一个概念,密钥长度决定了算法的安全性,比如AES的密钥长度有128、192、256比特长度可选。

这个密钥长度是有讲究的,代码中一般是会指定使用的key是多少字节的,若约定使用16字节(128比特长度)长度的密钥,一旦密钥使用17个字节的密钥则会报错:

java.security.InvalidKeyException: Invalid AES key length: 17 bytes

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

二、对称加密算法-块密码算法原理

对称加密算法分为两大类型,分别是块密码算法和流密码算法,AES则是典型的块密码算法

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

这里简单介绍下块密码算法的工作原理。

块密码算法在运算(加密或者解密)的时候,不是一次性完成的,每次对固定长度的数据块( block )进行处理,也就是说完成一次加密或者解密可能要经过多次运算。

数据块的长度就称为分组长度( block size ),由于大部分明文的长度远远大于分组长度,所以要经过多次迭代运算才能得到最终的密文或明文,块密码算法有多种迭代模式( Block cipher modes of operation ) , 迭代模式也被称为分组模式。

迭代模式有ECB模式、CBC模式和CTR模式等,这里简单介绍下ECB模式,抛砖引玉,其他模式读者朋友们可自行了解,本文不详细介绍了。

ECB模式的加密过程:

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

  • 将明文拆分成多个数据块,每个数据块的长度等于分组长度,如果最后一个数据块长度小于分组长度,需要进行填充保证最后一个数据块长度等于分组长度。

  • 依次对每个数据块进行迭代得到每个数据块的密文分组,将所有密文分组组合在一起就得到最终的密文,密文长度等同于明文长度。

ECB模式的加密过程:

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

  • 将密文拆分成多个数据块,每个数据块长度等于分组长度。

  • 依次对每个数据块进行迭代得到每个数据块的明文分组,最后一个明文分组要去除填充值,最终将明文分组组合在一起就得到最终的明文。

对应到代码,比如JAVA AES工具类中,往往会看到这么一句定义:

private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法

ECB就是迭代模式,但是PKCS5Padding又是什么呢?

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

三、对称加密算法-填充机制

基于明文需要切分为多个数据块(分组长度)处理,因此明文需要是分组长度的整数倍,如果不是倍数,必须有一种填充的机制,填充某些数据保证明文长度是分组长度的倍数。

举个例子,假设分组长度为64比特,明文最后的一个分组长度为24比特,此时需要填充,可选择比如补充40比特的0字符。

解密后,可去掉末尾的0字符即可,可这样的方式有个问题:不知道有多少个0字符是作为填充的,万一原始分组的最后也存在0字符呢

我们可以使用PKCS#7填充标准,它也很简单:假设分组长度为8字节,最后一个明文分组长度为1字节,则需要填充7字节的0x07:

待加密数据原长度为1字节:
0x41
填充后:
0x410x070x070x070x070x070x070x07

假设最后一个明文分组长度为3字节,则需要填充5字节的0x05:

待加密数据原长度为3字节:
0x410x410x41
填充后:
0x410x410x410x050x050x050x050x05

解密时,读取最后一个字节的值,比如读到的是5,则说明明文最后一个分组加密前填充了5个字节,则去掉最后5个字节的数据得到原文。

所以,这样就很容易得到:填充值的最后一个字节代表的就是实际填充的长度

这里我们主要关注PKCS#5和PKCS#7两种,他们处理填充机制的方式其实是一样的,只是PKCS#5处理的分组长度只能是8字节,而PKCS#7的分组长度可以是1到255任意字节,从这个角度看,可以认为PKCS#5是PKCS#7标准的子集。

那么问题来了,如果明文的最后一个分组长度恰好就是分组长度呢

按照这个机制,如果明文的最后一个分组长度恰好就是分组长度(这里假设分组长度还是8字节),则需要再填8个字节,其内容都为0x08,这样无论是不是分组的整数倍,都统一填充,解密时可以统一处理,不会产生歧义

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

四、公开密钥算法

继续介绍加密学中另一个重要的算法:公开密钥算法,也称为非对称加密算法。

公开密钥算法不是一个算法,而是一组算法,如果公开密钥算法用于加密解密运算,习惯上称为非对称加密算法。

非对称是相对于对称而言的,他两一对比就知道啦:

  • 对称加密方法中,我们只用一个密钥来进行加密和解密。这也是“对称”一词的由来。

  • 非对称加密方法中,我们用一个密钥来进行加密,用另一个密钥来解密。因为两个密钥不一样,所以是“非对称”。

六十六、进阶篇-浅谈对称加密算法和公开密钥算法

在非对称加密中有两个密钥:

  • 一个是"公钥"(Public Key),用于加密。

  • 一个是"私钥"(Private Key),用于解密。

顾名思义,公钥可以在网络上以明文传输,可以被任何人获取,但是私钥则需要妥善保存,一旦泄露则需要立即更换,否则将产生严重的安全问题。

非对称加密最出名的就是 RSA 算法。

1977 年,三位数学家 Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字的首字母命名,叫做 RSA 算法。

五、既生瑜何生亮

既然有了对称加密算法,为什么还出现了公开密钥算法呢?

我们必须了解其不同。

首先从功能定位上,对称加密算法虽然有很多的算法和加密机制,但主要用于加密和解密,而非对称加密算法的功能比较多,可以进行加密解密、密钥协商、数字签名等,这个我们后面会着重学习。

所以,公开密钥算法的诞生,并不是纯粹为了做数据加密,它有更多的神圣职责,因此针对加密这个职责,公开密钥算法并不专业。为什么这么说呢?

相对于对称加密算法,公开密钥算法尤其是RSA算法运算非常缓慢,一般情况下,需要加密的明文数据都非常大,如果使用公开密钥算法进行加密,运算性能会惨不忍睹。

非对称加密比对称加密要慢大概 100 ~ 1000 倍。

公开密钥算法在密码学中一般进行密钥协商或数字签名,因为这两者运算的数据都相对比较小。

公开密钥算法得到最广泛使用的是RSA算法,RSA算法是一个多用途的算法,可以进行加密解密、密钥协商、数字签名等,都是十分重要的概念,本篇文章核心关注其加密解密运算。

六、RSA加密算法的应用场景

在大部分场景下,比如HTTP应用中,传递的内容都很大,RSA加密算法很慢,但是它确实具有加密和解密特性,我们来介绍下RSA算法加密特性的应用场景。

(1)单步加密

公钥是公开的,很多客户端知道,而私钥必须由服务器端保密,所以一般客户端用公钥加密的方式传递一些关键数据,比如客户端可以对自己的信用卡号加密,然后传递给服务器端,服务器端解密后无须回应,这就是单步加密的模式。

下面举个例子,客户端要向服务器端发送自己的身份证号,可能的步骤如下:

  • 客户端向服务器端发送连接请求,服务器返回RSA密钥对的公钥给客户端,自己保留密钥对的私钥。

  • 客户端接收到服务器的公钥,使用公钥对身份证号进行RSA加密,并发送给服务器。

  • 服务器端用 RSA 私钥解密接收到的数据,获取的值就是用户的身份证号。

由于只有服务器端才有私钥,所以攻击者即使获取到加密数据也不能进行反解。

(2)双向加密

在单步加密过程中,服务器端无法发送密文,如果服务器端用私钥加密数据,然后发送给客户端,由于公钥是公开的,任何人都能解密,所以这个过程是不成立的。

举个例子来解释什么是双向加密 完成的功能是用户要查询账户(身份证)下还有多少余额?

  • 客户端生成 RSA 密钥对,然后连接服务器端,将自己的公钥(clientPublicKey)发给服务器端。

  • 服务器端接收请求后,保存客户端的公钥,然后生成另外RSA密钥对,并将公钥(serverPublicKey)发送给客户端。

  • 客户端使用服务器的公钥(serverPublicKey)加密身份证,加密的数据发送给服务器端,期待服务器端返回自己的账户余额。

  • 服务器端接收到数据后,用自己的私钥(serverPrivateKey)解密客户端身份证号,然后查询出用户的余额,并用客户端的公钥(clientPublicKey)解密余额并发送给客户端。

  • 客户端用自己的私钥 ClientPrivateKey)解密接收到的数据,这数据就是自己账户下的余额。

设计是不是很巧妙?可以看出来,如果不考虑性能问题,RSA确实可以完成数据加密解密的任务,但是现实中,往往数据量都比较大,RSA是不能作为大量数据加解密的算法方案的,比较现实的方案往往是考虑使用对称加密算法。

本文完,下篇见。