AES是分组密钥,算法输入128位数据,密钥长度也是128位。用Nr表示对一个数据分组加密的轮数(加密轮数与密钥长度的关系如表1所列)。每一轮都需要一个与输入分组具有相同长度的扩展密钥Expandedkey(i)的参与。由于外部输入的加密密钥K长度有限,所以在算法中要用一个密钥扩展程序(Keyexpansion)把外部密钥K扩展成更长的比特串,以生成各轮的加密和解密密钥。\x0d\x0a1.1圈变化\x0d\x0aAES每一个圈变换由以下三个层组成:\x0d\x0a非线性层——进行Subbyte变换;\x0d\x0a线行混合层——进行ShiftRow和MixColumn运算;\x0d\x0a密钥加层——进行AddRoundKey运算。\x0d\x0a① Subbyte变换是作用在状态中每个字节上的一种非线性字节转换,可以通过计算出来的S盒进行映射。\x0d\x0a\x0d\x0a② ShiftRow是一个字节换位。它将状态中的行按照不同的偏移量进行循环移位,而这个偏移量也是根据Nb的不同而选择的[3]。\x0d\x0a\x0d\x0a③ 在MixColumn变换中,把状态中的每一列看作GF(28)上的多项式a(x)与固定多项式c(x)相乘的结果。 b(x)=c(x)*a(x)的系数这样计算:\x0d\x0a*运算不是普通的乘法运算,而是特殊的运算,即 b(x)=c(x)·a(x)(mod x4+1) 对于这个运算 b0=02。a0+03。a1+a2+a3 令xtime(a0)=02。a0\x0d\x0a其中,符号“。”表示模一个八次不可约多项式的同余乘法[3]。\x0d\x0a\x0d\x0a对于逆变化,其矩阵C要改变成相应的D,即b(x)=d(x)*a(x)。\x0d\x0a④ 密钥加层运算(addround)是将圈密钥状态中的对应字节按位“异或”。\x0d\x0a\x0d\x0a⑤ 根据线性变化的性质[1],解密运算是加密变化的逆变化。
详细过程如下图:
AES加密标准又称为高级加密标准Rijndael加密法,是美国国家标准技术研究所NIST旨在取代DES的21世纪的加密标准。AES的基本要求是,采用对称分组密码体制,密钥长度可以为128、192或256位,分组长度128位,算法应易在各种硬件和软件上实现。
1998年NIST开始AES第一轮分析、测试和征集,共产生了15个候选算法。
1999年3月完成了第二轮AES2的分析、测试。2000年10月2日美国政府正式宣布选中比利时密码学家Joan Daemen和Vincent Rijmen提出的一种密码算法Rijndael作为AES的加密算法。
AES加密数据块和密钥长度可以是128b、192b、256b中的任意一个。AES加密有很多轮的重复和变换。
AES的S盒:
把该字节的高4位作为行值,低4位作为列值,取出S盒或者逆S盒中对应的行的元素作为输出。例如,加密时,输入的字节S1为0x12,则查S盒的第0x01行和0x02列,得到值0xc9,然后替换S1原有的0x12为0xc9。状态矩阵经字节代换后的图如下:
AES的S逆盒:
例子:
列混合变换是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵,如下图的公式所示:
这个位置有点不好理解,我们举一个简单的例子(都是我翻阅资料,最后通过计算总算是知道怎么算了)
计算
输入密钥直接被复制到扩展密钥数组的前4个字。然后每次用4个字填充扩展密钥余下的部分。公式描述
明文:0123456789abcdeffedcba9876543210
密钥:0f1571c947d9e8509cb7add6af7f6798
密文:ff0b844a0853bf7c6934ab4364148fb9
这里引用书上的例子,自己可以去算一遍加深理解
AES采用分组密码体制,首先将明文分成以16个字节长度为基准字节段,如果最后不足16字节则同样使用填充。然后分组对每段字节段进行加密得到密文,再将最后得到的密文拼接在一起形成最终的密文。AES算法的密钥长度可以分为128位,256位,512位。
整个加密过程如下图所示:
功能实现,在客户端中将明文进行AES加密后通过TCP链接发送至另一个客户端,另一端通过密钥对密文进行解密得到明文
AES, 高级加密标准, 是采用区块加密的一种标准, 又称Rijndael加密法. 严格上来讲, AES和Rijndael又不是完全一样, AES的区块长度固定为128比特, 秘钥长度可以是128, 192或者256. Rijndael加密法可以支持更大范围的区块和密钥长度, Rijndael使用的密钥和区块长度均可以是128,192或256比特. AES是对称加密最流行的算法之一.
我们不去讨论具体的AES的实现, 因为其中要运用到大量的高等数学知识, 单纯的了解AES流程其实也没什么意义(没有数学基础难以理解), 所以我们今天着重来总结一些使用过程中的小点.
当然了分组密码的加密模式不仅仅是ECB和CBC这两种, 其他的我们暂不涉及.
上面说的AES是一种区块加密的标准, 那加密模式其实可以理解为处理不同区块的方式和联系.
ECB可以看做最简单的模式, 需要加密的数据按照区块的大小分为N个块, 并对每个块独立的进行加密
此种方法的缺点在于同样的明文块会被加密成相同的密文块, 因此, 在某些场合, 这种方法不能提供严格的数据保密性. 通过下面图示例子大家就很容易明白了
我们的项目中使用的就是这种模式, 在CBC模式中, 每个明文块与前一个块的加密结果进行异或后, 在进行加密, 所以每个块的加密都依赖前面块的加密结果的, 同时为了保证第一个块的加密, 在第一个块中需要引入初始化向量iv.
CBC是最常用的模式. 他的缺点是加密过程只能是串行的, 无法并行, 因为每个块的加密要依赖到前一个块的加密结果, 同时在加密的时候明文中的细微改变, 会导致后面所有的密文块都发生变化. 但此种模式也是有优点的, 在解密的过程中, 每个块的解密依赖上一个块的加密结果, 所以我们要解密一个块的时候, 只需要把他前面一个块也一起读取, 就可以完成本块的解密, 所以这个过程是可以并行操作的.
AES加密每个块blockSize是128比特, 那如果我们要加密的数据不是128比特的倍数, 就会存在最后一个分块不足128比特, 那这个块怎么处理, 就用到了填充模式. 下面是常用的填充模式.
PKCS7可用于填充的块大小为1-255比特, 填充方式也很容易理解, 使用需填充长度的数值paddingSize 所表示的ASCII码 paddingChar = chr(paddingSize)对数据进行冗余填充. (后面有解释)
PKCS5只能用来填充8字节的块
我们以AES(128)为例, 数据块长度为128比特, 16字节, 使用PKCS7填充时, 填充长度为1-16. 注意, 当加密长度是16整数倍时, 反而填充长度是最大的, 要填充16字节. 原因是 "PKCS7" 拆包时会按协议取最后一个字节所表征的数值长度作为数据填充长度, 如果因真实数据长度恰好为16的整数倍而不进行填充, 则拆包时会导致真实数据丢失.
举一个blockSize为8字节的例子
第二个块中不足8字节, 差4个字节, 所以用4个4来填充
严格来讲 PKCS5不能用于AES, 因为AES最小是128比特(16字节), 只有在使用DES此类blockSize为64比特算法时, 考虑使用PKCS5
我们的项目最开始加解密库使用了CryptoSwift, 后来发现有性能问题, 就改为使用IDZSwiftCommonCrypto.
这里咱们结合项目中边下边播边解密来提一个点, 具体的可以参考之前写的 边下边播的总结 . 因为播放器支持拖动, 所以我们在拖拽到一个点, 去网络拉取对应数据时, 应做好range的修正, 一般我们都会以range的start和end为基准, 向前后找到包含这个range的所有块范围. 打比方说我们需要的range时10-20, 这是我们应该修正range为0-31, 因为起点10在0-15中, 20 在16-31中. 这是常规的range修正.(第一步 找16倍数点).
但是在实际中, 我们请求一段数据时, 还涉及到解密器的初始化问题, 如果我们是请求的0-31的数据, 因为是从0开始, 所以我们的解密器只需要用key和初始的iv来进行初始化, 那如果经过了第一步的基本range修正后, 我们请求的数据不是从0开始, 那我们则还需要继续往前读取16个字节的数据, 举个例子, 经过第一步修正后的range为16-31, 那我们应该再往前读取16字节, 应该是要0-31 这32个字节数据, 拿到数据后,使用前16个字节(上一个块的密文)当做iv来初始化解密器.
还有一个要注意的点是, 数据解密的过程中, 还有可能会吞掉后面16个字节的数据, 我暂时没看源码, 不知道具体因为什么, 所以保险起见, 我们的range最好是再向后读取6个字节.
感谢阅读
参考资料
AES加密过程涉及到 4 种操作,分别是字节替代、行移位、列混淆和轮密钥加。
1.字节替换:字节代替的主要功能是通过S盒完成一个字节到另外一个字节的映射。
2.行移位:行移位的功能是实现一个4x4矩阵内部字节之间的置换。
4.轮密钥加:加密过程中,每轮的输入与轮密钥异或一次(当前分组和扩展密钥的一部分进行按位异或);因为二进制数连续异或一个数结果是不变的,所以在解密时再异或上该轮的密钥即可恢复输入。
5.密钥扩展:其复杂性是确保算法安全性的重要部分。当分组长度和密钥长度都是128位时,AES的加密算法共迭代10轮,需要10个子密钥。AES的密钥扩展的目的是将输入的128位密钥扩展成11个128位的子密钥。AES的密钥扩展算法是以字为一个基本单位(一个字为4个字节),刚好是密钥矩阵的一列。因此4个字(128位)密钥需要扩展成11个子密钥,共44个字。