一文详解对称密钥加密

TL;DR: 前面几篇文章我们介绍了密码学的两大基本单元 - 随机数和Hash算法。有了这两大基元的加持,我们就可以开始密码学中的加解密算法的介绍。本文我们将重点介绍密码学中的对称密钥算法,包括流密码算法,块密码算法;各种算法的基本原理,重点介绍了主流的块密码算法的补位,迭代模式,加密器的实现等,最后动手实践了在Java中如何使用这些算法为我们业务所用!

数据加密及类型

数据加密,指的是根据一定规则,将数据处理成不规则的数据,使得人们除非有了关键的钥匙以及得知这个规则,难于得知无规则数据的真实含义。这个一定规则 就是加密算法,这个钥匙就是密钥。

数据加密分为对称密钥加密以及非对称密钥加密:

  • 对称密钥加密: 双方共同持有这个密钥,发送方用这个密钥按照指定的算法将数据加密,再发出去;接收方用这个密钥将接收到的数据解密,以得到真实的数据含义。由于双方都持有这个密钥,而且内容相同,所以叫对称密钥
  • 非对称密钥加密:这种加密方式的密钥是一对,发送方用其中的一把钥匙将数据加密,再发出去;接收方用这对密钥的另一把钥匙将数据解密,以得到真实的数据含义。发送方持有密钥中的一把钥匙,接收方持有另外一把。接收方持有的钥匙叫 私钥, 而接收方持有的这把钥匙叫公钥 。两把钥匙不一样,所以叫做非对称密钥加密,也叫做公开密钥算法。

这篇文章我们主要介绍对称密码加密算法。对称加密算法可以简单地概括为通过一个算法和一个密钥,对明文进行处理,变成一个无规则无意义数据的算法。明文在算法里面叫plaintext,密钥叫做key,而最终生成的密文叫ciphertext. 下图形象地描述了对称机密算法的操作:
在这里插入图片描述

我们也可以用如下的公式来简单表述对称加解密算法:

​ 密文 = E(明文,算法,密钥)

​ 明文 = D(密文,算法,密钥)

流密码算法

流密码加密是指将明文信息按字符(具体是按二进制位)逐位地加密的一类密码学算法。在计算机存储中,所有的信息最终都是用0和1进行表示。所以可以采用一些算法,把某些0变成1,把某些1变成0,这样别人就很难知道真实的内容是什么。怎样把某些0变成1,把1变成0好呢?数学上的异或 XOR ⊕ 操作提供了极好的方案:

  • 0 ⊕ 0 = 0
  • 0 ⊕ 1 = 1
  • 1 ⊕ 0 = 1
  • 1 ⊕ 1 = 0

那么加密的时候,明文为M,密钥为K,这样加密的过程就是: 密文C = M ⊕ K, 而对应的解密过程则为 M = C ⊕ K . 破解者仅凭密文是很难破解出明文,即使采用一定的暴力手段,也无法确认破解的明文就是原始明文。

流密码算法的过程大概是这样的:

  • 用户提供一个原始密钥key
  • 算法将原始密钥key作为种子seed,生成一个伪随机数,这个伪随机数将作为加密的密码流。由于加解密双方知道算法以及种子seed,所以能生成相同的伪随机数
  • 算法将原文M与密码流进行异或操作,从而得到密文C
  • 信息的接受者同样根据密钥key以及约定的伪随机数生成算法, 生成一个密码流
  • 接着将密文C与密码流进行异或操作,从而得出原文

流密码算法可以并行进行处理,而且主要是简单的异或操作,所以性能非常好。

流密码算法的安全问题

但是流密码算法有个问题:如果原始key不变的话,之前的密文很容易被破解。比如有人用明文A,加密后得到的密文为EA。这时如果密钥不变,而我们拦截到密文EA, 那么我们怎样能破解密文呢? 我们可以用明文B去请求一次加密,得到密文EB。这时我们手头上有对方的密文EA,我们自己明文B以及对应的密文EB,所以我们就可以推算出明文 A = EA ⊕ EB ⊕ B。 这个过程怎么推导出来的呢?

  1. EA = A ⊕ Key
  2. EB = B ⊕ Key

根据相同的值异或为0 这个规则,那么我们可以推出:

  1. EA ⊕ EB = A ⊕ Key ⊕ B ⊕ Key = A ⊕ B ⊕ (Key ⊕ Key) = A ⊕ B

这时两边同时异或B:

  1. EA ⊕ EB ⊕ B = A ⊕ B ⊕ B = A ⊕ (B ⊕ B ) = A

所以明文A = EA ⊕ EB ⊕ B

流密码算法的关键是一次性的密码本(One-time Pad), 这样才能确保消息不被破解。 由于速度快以及实现简单,在确保一次性密码的前提下,流密码算法一直活跃于各种业务场景下。RC4 (Rivest Cipher 4)就是一种常见的流密码加密算法的实现。之前一直用于HTTPS的TLS,对用户请求的内容进行对称加密,而其密钥则由RSA或者DH等密码协商算法一次性生成使用。 但由于后来有攻击者可以在4个小时破解一个HTTPS保护的cookie的内容。所以RC4现在基本被移除出 HTTPS的加密套件。在实际项目当中,大家也尽量能不用就不用RC4。可以采用我们下面要介绍的块密码加密算法,目前更为安全一点。

块密码算法

块密码(Block Cipher)算法加解密的时候,不是一次性完成,而是把原文分成固定长度的块,每次对这些数据块进行处理。所以块密码算法也叫分组密码算法。

块密码算法的流程大致如下:

  • 将全量明文M按照算法要求拆解成固定大小的明文块m-0 ~ m-n, 也就是一小块一小块的分组
  • 对于每一个明文块通过加密器进行加密
  • 加个明文块加密的结果合并起来,成为最终的明文C
    在这里插入图片描述
Padding

分组很有可能会发生全量明文不是块大小的倍数,比如全量明文是64bits : {1,2,3,4,5,a,b,c,d,e} , 块大小是128bits,那么明文块0就只有64bits。这时怎么办呢?其中一种解决方式就是进行补位了。然后当解密成功后,将补位的内容去掉就得到完整的明文。补位的类型有下面几种:

1) NoPadding

第一种最霸气,加密算法说“我不补位!” 。 你们的明文都必须是我指定的块大小的倍数,否则我就不干了!比如上面的情况,你提供一个128bits 倍数的明文,那我乖乖地加密,如果不是128bits的倍数,我就报错! 这样我解密也不用费事去除补位的内容了!

我相信大多数的码农打心里都想这样干,同时我也相信这样写大概率会被项目经理追杀!

2) ZeroPadding

第二种比较乖,明文不是块大小的倍数是没问题的,我来帮你补齐,你差几位,我补几个0. 比如上面明文块差64bits,那我就补8个0字符。变成 {1,2,3,4,5,a,b,c,d,e,0,0,0,0,0,0,0,0}. 这样补齐128bits。

这种补位方式加密的时候没问题,但是在解密的时候有可能会遇到问题:万一明文最后几个字符也是0,比如你的存款3000000000。这样如果银行的程序员小哥的加密算法补位是ZeroPadding,当你要去取款时系统解密余额,发现后面全是0,好家伙全干掉了. 小哥 + 项目经理卒…

3) PCKS#5 / PKCS#7

既然有问题,那我们就改进。标准化组织IETF(Internet Engineering Task Force)的PKIX工作组维护了一系列的标准,从PKCS#1到PKCS#14,其中的PKCS#5 和PKCS#7里面有定义了关于补位的标准。PKCS#5 / #7的补位标准是:补位的字符等于缺少的字符数。比如上面明文 {1,2,3,4,5,a,b,c,d,e},块大小是128bits,那么明文块缺少64bits,也就是8个字符,那么补位的字符就是8。补位后的明文变成 {1,2,3,4,5,a,b,c,d,e,8,8,8,8,8,8,8,8}。

如果明文大小刚好了块大小的倍数呢?那就补齐一整个块。最后一块变成了 {16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16}.

PKCS#5 /PCKS#7在补位方面是一样的,他们的区别在于明文块的大小:PKCS#5 限定了明文块的大小是128bits,而PKCS#7的分组大小可以1-255任意大小。

迭代模式

补位完成后,我们可以开始加密了。全量明文分成这么N跟明文块,那么需要迭代对每一个明文块进行加密。块密码算法有很多种不同的迭代方式,下面我们介绍常用的以及推荐的迭代方式:

1) ECB - Electronic CodeBook

ECB模式时最简单的一种迭代模式。它的加密过程如下:

  • 将全量明文分成多个明文块,最后一块不够块大小的需要补位
  • 一次对每个数据块进行迭代,得到每一个数据块对应的明文,最后将所有的密文块组合成完整的密文。

其解密的过程刚好是相反的。下图展示了ECB的加密过程图示:

在这里插入图片描述

ECB的优点是: 简单,由于每一块都是独立加密运算的,可以并行运算,并且误差不会被传递

ECB的缺点是:不能隐藏明文的模式,比如原文重复出现有“珍珠港”字样,那么密文也相同的密文字符重复出现。而且有可能被进行明文攻击,具体可以搜索一下ECB模式攻击

不推荐使用ECB作为对称密码加密的迭代模式。

2) CBC - Cipher Block Chaining

CBC是一种比较常见的迭代模式,基本解决了ECB模式的安全问题。它的迭代步骤如下:

  • 同样地将明文分组,最后一块进行补位
  • 处理第一个数据块前,生成一个初始化向量 IV (Initialization Vector). IV一般是随机数
  • IV与明文块0进行异或,运算的结果经传给密码器进行加密,得出密文C0
  • 接着处理后续明文块,将上一个明文块的输出C0 作为下个明文块加密的IV,进行与第一个块相同的操作,以此迭代下去。

下图展示CBC的迭代模式:

在这里插入图片描述

CBC模式的优点是:引入了IV,如果采用随机IV,可以做到同样的明文和密钥,最终得到的明文不一样,更为安全一些。初始化向量IV是对着密文一起发送给解密者的,IV的长度是跟密码块的长度一致,一般作为第一个密码块传递给解密者

CBC的缺点是:每一块的解密都需要上一块解密的结果作为输入IV,所以只能串行处理。

推荐使用CBC作为对称密码加密的迭代模式。

3) CTR - Counter

CTR模式在迭代的时候,类似于流密码的运行模式。每次迭代的时候要生成一个密码流。生成密码流的方法可以是任意的,但是各个密码之间是有关系的,最简单的方式就是密码流不断递增,所以这个就叫 计数器模式 Counter。它的处理过程大致如下:

  • 将明文拆成N个明文块,但是不用补位
  • 生成N个密码流,简单的方式比如递增
  • 接下来进行迭代加密,密码流与密码进行加密操作,然后与明文进行异或XOR运算得到密文块
  • 迭代运行每一个明文块,得到最终的密文

下图展示了CTR模式的大致流程:
在这里插入图片描述

CTR的优点是: 引入Nonce,随机的Nonce可以确保同样的明文和密钥,可以产生不同的密文。由于密钥流按照一定的规则生成,所以个加密单元可以预先知道所有的输入 密钥, Nonce,明文块,那么就可以并行进行处理。 CTR每一块里面采用流加密算法,将明文与对应的流密钥位进行异或XOR操作就行,所以不需要补位填充

推荐使用CTR作为对称密码加密的迭代模式。

加密器

上面这几种不同的迭代方式都说到了加密器,但到目前为止,这一部分仍然像个黑盒子一样,只知道给他密钥跟明文,他就能加密。不同的算法有不同的加密器的实现,即使相同算法,密钥长度不同加密器的实现也有不同,比如AES加密器里面,AES128的密钥迭代次数是10,AES192是12轮,而AES256是14轮。但加密器的基本操作的类型大致有,有字节替换,行位移,列混淆,加轮密钥。不一定每一个算法都实现了这4个步骤。

1) 字节替换

字节替换是将明文的字符根据一定的规则替换成另外的字符,根据什么规则呢?一般会有与明文块大小一致的二维常量数组,这个二维常量数组也叫替换表 Substation Box, 简称S盒。 算法实现根据明文的位置从S盒中找到对应的字符替换掉明文字符。比如AES128的块长度是16字节,那么它的S盒就是一个4x4的二维数组。如下图:

在这里插入图片描述

2) 行位移

替换还不够,这里还要来个行位移,怎么弄呢?就是每一行的元素 “预备起”,按照要求向左或者向右移动几位。比如第0行不动,第1行左移1位,第2行左移2位,第3行左移3位。下图描述了这个过程:

  • 17
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值