游戏
每当手里握着魔方的时候,我都会想起和她玩过的一个游戏,游戏很简单:
她会把4×4的魔方给打乱,每一次她都只会转动八次来打乱魔方,如果我不能刚好用八次转动复原魔方,就意味着我今晚得不到她给我辅导作业的机会。
她的数学很好,可是我翻遍数学课本也找不到解决魔方的方法。直到毕业后的某一天,我发现如果转动第一行 第三行,再转动第一列 第四列,接着再第一行 第三行 第一列 第四列正好就是他每次打乱后的魔方。虽然我知道了他的魔方规则,但我再也没机会问她,是否想告诉我这串数字——1314的含义。但是我很感谢她,因为这个游戏对我后来学习AES加密算法有着很大的帮助。
(我是ς男人👨)
轮密钥的生成
没错,AES其实可以简单看作是一个魔方游戏。AES在进行加密前,需要先把数据进行分组,每一组都按照128比特长度进行切分 (长度不足 需要填充)这些128比特会进行各自的AES加密,但是他们都会采用同样的方式加密。
假设我这里有一串字符要加密,在不考虑空格的情况下,长度刚好是16字节。 16字节,其实就是128比特,也就是说我们不用再分组了,长度刚刚好。
接着我们把这16字节,分别填入4×4的方形里面,顺序分别是从上到下,从左到右。这个方形也就是我们常说的矩阵了。但是AES的加密,还需要把字符转换成16进制来表示,用两个16进制数就刚好可以占一个格子。
现在加密前的准备工作就做好了。要把明文加密变成密文,肯定是需要密钥的,AES提供了三种不同长度的密钥供选择。理论上使用越长的密钥,密文会越难被破解,因为越长的密钥需要进行更多轮次的加密运算,使用密钥和多轮的运算,就相当于他给我打乱魔方的规则了。
可是不管我们选择哪种长度的密钥,其实也就只有一把密钥。如此多轮运算,难道就只用一把密钥吗? 神奇的就是,一开始的这把密钥其实是一把种子密钥,然后通过算法生根发芽,生成每一轮需要的轮密钥。
在这里我随机生成了16字节的种子密钥,128比特的密钥长度意味着十轮的加密操作,也就是说我们需要生成十个轮密钥。轮密钥不是一轮一轮算出来的,实际上是按列计算出来的,因此我们需要给它们标上序号。注意第1列用0表示不是1。要计算出每一列可以分为两种情况 ,4的倍数以及非4的倍数。其中4的倍数列的计算相对复杂一些,不是4的倍数,列的计算就很简单了。
首先说复杂的吧。先来计算第四列吧,第四列是基于第三列进行计算出来的,我们先把第三列复制一份出来,然后把复制的第三列里最顶格挪到最底格。其实就是旋转一下字节的位置而已,我喜欢把这一部叫做旋转字节。
但是如果只有这样的字节旋转,就跟没有加密似的,因此我们需要在此基础上增加一点混淆,也就是把这些字节全部给替换掉。这一步只需要查表就能完成了。这个表只需要搜索S-BOX就很容易能找到了(是S-BOX不是XBOX) 如果要替换第一个格子69就是查第六行和第九列,这里相交的格子F9就是替换后的新字节了。于是我们就可以逐个格子进行查询替换了,我们就把这一步称为字节替换。
为什么我非得要给前面两个步骤起名字呢?其实四的倍数列是按照一个公式进行的,这个公式需要三个特殊的列来进行异或运算。听到异或运算不要怕,其实简单的要命,首先公式里其中一个特殊列我们已经有了,也就是字节替换。前面已经说了,字节替换是根据旋转字节来的,那为什么旋转字节的时候,我们要用种子密钥的第三列来进行呢?那是因为我们需要用第i-1列来运算,ℹ️是index序号的缩写。我们要算第4列,i就等于4,i减1就是3, 所以我们用了第三列来进行旋转字节。另一个特殊的列在这里就是第0列,因为这一列要求i-4列,i等于4,因此i-4就是0。 最后一个特殊列是一个常量,也就是说查表就可以了。如果是128比特的种子密钥,这个轮常量表就有十列,每一轮用一列。这个表是从一开始数的,不是零 ,因为我们没有第零轮,而是从第一轮开始的。我们正在进行第一轮的轮密钥生成,毫无疑问就是用第一轮的轮常量了,虽然这个公式很长,但其实每个步骤都可以很简单理解。
公式如下:
现在要计算异或运算,先由16进制转换为二进制,计算完最后我们把二进制转换成16进制,这就是我们要求的第四列了。
如果你们觉得刚刚是在地狱,那么现在就要进入天堂了。因为不都是四的倍数列,就非常简单了。比如要算出第五列,我们只需要把第一列和第四列复制出来,然后进行异或运算就可以了。为什么是这两列呢? 因为不是四的倍数列,是按照这个公式来的:
剩下要计算的列,我们只要用这两个公式便可以神挡杀神,佛挡杀佛般的算出来了。
轮密钥加
有了明文、种子密钥和多轮的轮密钥,现在就能看看正式的加密过程是如何进行的了。首先我们需要把分好组的明文和种子密钥,来个异或运算。虽然全世界中文材料都把这一步称为轮密钥加,但是我个人觉得种子密钥加更方便记忆和理解(👍), 把知识转成自己的语言才算是学进去了。
要对两个矩阵进行异或运算,只需要逐个格子对应进行异或运算即可,比如第一列第一格的45和6A进行异或运算 ;第一列第二个格子的67和69进行异或运算,就这样依次计算,就可以得到一个全新状态的矩阵了。
行移位
这个矩阵会进入到字节代换的步骤里面,也就是说还是一样把字节替换掉,还是一样查表来替换,还是一样,用那张s-box表,还是那种熟悉的味道。
但在查表的时候,我想让大家注意:这个矩阵里是有两个相同字节的格子0C, 在查表后 它们都会替换为相同的字节,这也是字节代换的一个弊端。因此还是得打乱这个矩阵,也就是进行行移位。 我们先为每一行标注行号,我们肉眼看到的第一行就是第0行 第0行向左移动0个格子 也就是不移动 第一行向左移动一个格子 第二行向左移动两个格子 第三行向左移动三个格子 我们把所有超出矩阵圆原本范围的格子,往回挪动 ,这样就拼凑成新状态的矩阵了.
列混淆
字节代换和行移位虽然都打乱了矩阵,但他们都只是在字节层面进行处理,要进一步打乱,我们还需要处理更底层。 此时就要进入被誉为最难的一步列混淆 。
这一步需要用一个固定的矩阵,把它和行移位的矩阵进行模乘处理即可 ,我们首先知道矩阵的乘法规则 注意这里的相加实际是异或运算,因此用异或符号来表示运算后的结果 。以得到新矩阵第一列第一行的元素了,我们需要分开来计算:
先计算02和15的模乘结果,因为要进行比特层面的处理,因此先转换为二进制。现在神奇的事情发生了,我们可以用多项式来表示这里的二进制,多项式里的a我们直接代入二进制数即可,于是我们就可以把02的二进制,用多项式来表示。 这里就像乘法一样,遇到零的时候就直接消灭掉那一项;遇到一的时候,那都是自己人,相当于什么都没发生。再来吧,15的二进制转成多项式还是一样,遇到零就消灭,到一就没啥事。接着我们就把两边跟乘法处理一样扩展开来 然后再进行乘法处理,就可以得到一个处理好的多项式了。既然二进制可以用多项式来表示,多项式也可以反过来用二进制表示。因为如果那一项不存在 ,当于多项式里的A等于零;如果那一项存在,相当于多项式里的A等于一,于是我们就可以把这个多项式。刚好转换成八位二进制数了
以此类推,继续算下去,完成列混淆以后,我们生成的轮密钥终于可以派上用场了。这一步和种子密钥加是一样的原理 我们只需要用列混淆后的矩阵,和第一轮的轮密钥进行异或运算即可,毕竟我们目前依旧处于第一轮的加密操作中。异或运算完以后,我们的第一轮加密操作也完成了。这个矩阵要作为第二轮加密操作的初始矩阵。
加密
现在我们来看整个AES加密过程就不难了。在最开始我们把明文分好组,接着我们让种子密钥和明文来次异或运算,为了方便大家理解,我还特意起了一个种子密钥加的名字,来表示这一步异或得到的结果,就正式进入第一轮的加密操作中。第一轮的第一步就是字节代换,也就是查表来替换字节,查表以后我们进入行移位,把字节进一步打乱,把字节都打乱以后,我们还要利用列混淆来把比特打乱。最后我们会用到第一轮的轮密钥,把密钥和列混淆后的矩阵来一次异或运算,这样就完成了第一轮的加密操作了。
接着我们把第一轮操作后的矩阵给到第二轮进行加密操作,还是一样,分别进行字节代换 行移位 列混淆和轮密钥加的操作,要注意,这一轮的轮密钥加用的是第二轮的轮密钥(虽然这听起来好像废话)。
1到9轮的加密操作都是一样,但是第十轮就不是了,简单来说就是最后一轮加密没有列混淆,最后一轮不用对比特层面进行处理,完成第十轮就加密成功了,就有我们的密文了。
解密
如果要解密,其实原理也是一样的,只不过是逆向操作。首先我们的密文需要和第十轮的轮密钥来个异或操作。接着就会进入1到9轮的解密操作。
这九轮的操作都采用一样的步骤:
全文终。
原文来自B站UP技术蛋老师的视频 BV1seFJeHEnm
仅作为复习的笔记记录