AES加密java底层实现

mac2024-01-28  39

文章目录

基本结构基本操作详解字节替代行移位列混合轮密钥加密 任意长度字符串加密过程&任意文件加密

基本结构

AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文,

下面实现AES-128,也就是密钥长度为128位,加密轮数为10轮。 上面说到,AES的加密公式为 C = E (K,P),在加密函数E中,会执行一个轮函 数,并且会执行10次这个轮函数,这个轮函数的前9次执行的操作是一样的,只有第10次有所不同,也就是说,一个明文分组会被加密10轮。 AES的核心就是实现一轮中的所有操作。 AES的处理单位是字节,128位的输入明文分组P和输入密钥K都被分成16字节, 分别记为P=P0 … P15和K =K0 K1 … K15。 例如明文分组P = abcdefghijklmnop,其中的字符 a对应P0,p对应P15,一般的,明文分组用字节为单位的正方形矩阵描述,称为状态矩阵。在算法的每一轮中,状态矩阵的内容不断发生变化,最后的结果作为密文输出。 该矩阵中字节的排列顺序为从上到下,从左到右依次排列,如下图所示

以刚才的例子为例假设现在的明文分组P为“abcdefghijklmnop”,则对应上面 的生成状态矩阵图如下: 以刚才的例子为例假设现在的明文分组P为“abcdefghijklmnop”,则对应上面 的生成状态矩阵图如下: 上图中0x61为字符a的十六进制表示,可以看出,明文经过AES加密后,已经面目全非。 类似的,128位密钥也是用字节为单位的矩阵表示,矩阵的每一列被成为1个32位比特字。通过密钥编排函数该密钥矩阵被拓展成为一个44个字节组成的序列 W[0],W[1],…,W[43],该序列的前4个元素W[0],W[1],W[2],W[3],是原始密钥,用 于加密算法中初始密钥加(下面还会写到);后面40个字节分为10组,每组4个字(128比特)分别用于10轮加密运算中的轮密钥加,如下图所示: 上图中,设K = “abcdefghiklmnop”,则K0 = a,K15 = p, W[0]=K0 K1 K2 K3=“abcd”。 AES的整体结构如下图所示,其中W[0,3]是指W[0],W[1],W[2]和W[3]串联组成的 128位密钥。加密的第1轮到第9轮的轮函数一样, 包括4个操作:字节代换,行位移,列混合和轮密钥加,最后一轮迭代不执行列混合。另外,在第一轮迭代之前,先将明文和原始密钥进行一次异或加密操作。 第十轮: 第1-9轮:

基本操作详解

字节替代

在进行字节代替之前,首先将明文和初始秘钥构成4*4的字节矩阵,并且将两个矩阵进行异或运算得到进行字节代替之前的正确输入矩阵 行移位 S盒代替 加密时,输出的字节S1作为0x12,则查S盒的第0x01行和0x02列,得到值0xc9,然后替换S1原有的0x12为0xc9 字节代替逆操作 逆字节代换也就是S盒来变换,逆S盒如下: 字节代换的代码

行移位

行移位操作: 行移位是一个简单的左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0个字节,第1行左移1字节,第2行左移2字节,第3行左移3字节,如下图所示 行移位的逆变换:

行移位的逆变换是将状态矩阵中的每一行执行相反的只为操作,例如AES-128中,状态矩阵的第0行右移0字节,第一行右移1字节,第二行右移2字节,第三行右移3字节

行移位的代码:

这里把字节代换和行移位一起演示:

package aES; public class testSubBytesAndRowShift { public static void main(String[] args) { String[] liStrings = { "4D","34","55","CB", "12","3D","A1","5F", "4D","34","55","CB", "12","3D","A1","5F", }; System.out.println("要加密的值为:"); for(int i=0;i<liStrings.length;i++){ System.out.print(liStrings[i]+" "); if((i+1)%4==0) System.out.println(); } //字节代换 String[] li = SubBytes.subBytes(liStrings); System.out.println("字节代换结果:"); for(int i=0;i<li.length;i++){ System.out.print(li[i]+" "); if((i+1)%4==0) System.out.println(); } //行移位 String string[] = RowShift.rowShift(li); System.out.println("行移位结果:"); for(int i=0;i<string.length;i++){ System.out.print(string[i]+" "); if((i+1)%4==0) System.out.println(); } } }

列混合

列混合变换 是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵,如下图的公式所示: 列混合的逆变换: 逆向列混合变换可由下图的矩阵乘法定义: 列混合代码: 可以验证,逆变换矩阵同正变换矩阵的乘积恰好为单位矩阵。 演示列混合:

package aES; public class testMixCol { public static void main(String[] args) { String[] strings = { "87", "6E", "46", "A6" , "F2", "4C", "E7", "8C" , "4D", "90", "4A", "D8" , "97", "EC", "C3", "95" , }; System.out.println("要加密的值为:"); for(int i=0;i<strings.length;i++){ System.out.print(strings[i]+" "); if((i+1)%4==0) System.out.println(); } System.out.println("列混淆的结果:"); String[] strings3=MixColumn.MatrixMultiple(strings); for(int i=0;i<strings3.length;i++){ System.out.print(strings3[i]+" "); if((i+1)%4==0) System.out.println(); } } }

轮密钥加密

在首次进行轮密钥加之前,要对16字节明文进行预处理, 将其组成一个4*4的矩阵,然后与初始密钥进行异或运算。 之后每次轮密钥加都是与对应扩展的密钥进行异或 所以得先有扩展密钥 那么先来密钥扩展:

AES密钥扩展算法的输入值是4个字(16字节), 输出值是一个由44个字组成(176字节)的一维线性数组。

Rcon[i]是轮常量,代表一个字,这个字最右边三个字节总是0,因此字与Rcon异或,其结果只是与该字最左边的那个字节相异或。每一轮的轮常量都不相同,其定义Rcon[i] = (RC[i],0,0,0),其中RC[1] = 1,RC[i] = 2•RC[i-1] 乘法是定义在域GF(28)上的。

AES首先将初始密钥输入到一个44的状态矩阵中,如下图所示 这个44矩阵的每一列的4个字节组成一个字,矩阵4列的4个字依次命名为W[0]、W[1]、W[2]和W[3], 它们构成一个以字为单位的数组W。 一共16个字128字节: 接着,对W数组扩充40个新列,构成总共44列的扩展密钥数组。 新列以如下的递归方式产生: 1.如果i不是4的倍数,那么第i列由如下等式确定: W[i]=W[i-4]⨁W[i-1] 2.如果i是4的倍数,那么第i列由如下等式确定: W[i]=W[i-4]⨁T(W[i-1]) 其中,T是一个有点复杂的函数。 函数T由3部分组成:字循环、字节代换和轮常量异或,这3部分的作用分别如下。 a.字循环:将1个字中的4个字节循环左移1个字节。即将输入字[b0, b1, b2, b3]变 换成 [b1,b2,b3,b0]。 b.字节代换:对字循环的结果使用S盒进行字节代换。 c.轮常量异或:将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。 轮常量Rcon[j]是一个字,其值见下表。 下面举个例子: 设初始的128位密钥为: 3C A1 0B 21 57 F0 19 16 90 2E 13 80 AC C1 07 BD 那么4个初始值为: W[0] = 3C A1 0B 21 W[1] = 57 F0 19 16 W[2] = 90 2E 13 80 W[3] = AC C1 07 BD 下面求扩展的第1轮的子密钥(W[4],W[5],W[6],W[7])。 由于4是4的倍数,所以: W[4] = W[0] ⨁ T(W[3]) T(W[3])的计算步骤如下:

循环地将W[3]的元素移位:AC C1 07 BD变成C1 07 BD AC;将 C1 07 BD AC 作为S盒的输入,输出为78 C5 7A 91;将78 C5 7A 91与第一轮轮常量Rcon[1]进行异或运算,将得到79 C5 7A 91, 因此,T(W[3])=79 C5 7A 91,故 W[4] = 3C A1 0B 21 ⨁ 79 C5 7A 91 = 45 64 71 B0 其余的3个子密钥段的计算如下: W[5] = W[1] ⨁ W[4] = 57 F0 19 16 ⨁ 45 64 71 B0 = 12 94 68 A6 W[6] = W[2] ⨁ W[5] =90 2E 13 80 ⨁ 12 94 68 A6 = 82 BA 7B 26 W[7] = W[3] ⨁ W[6] = AC C1 07 BD ⨁ 82 BA 7B 26 = 2E 7B 7C 9B 所以,第一轮的密钥为 45 64 71 B0 12 94 68 A6 82 BA 7B 26 2E 7B 7C 9B。

密钥扩展代码以及测试

扩展了密钥之后再来谈轮密钥加密 以后每次轮密钥加都是与对应扩展的密钥进行异或 轮密钥加密 代码和测试

任意长度字符串加密过程&任意文件加密

字符串的难点在于填充: 常见几种填充:

这里采用 PKCS5Padding 进行填充(默认): 如果明文块少于16个字节(128bit), 在明文块末尾补足相应数量的字符,差几个字节就补几。比如明文: {1,2,3,4,5,a,b,c,d,e},缺少6个字节, 则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6} eg: 这里我定义的 自立立人兴安安 有七个字,差一个字也就是16bit也就是两个字节。 下面的代码会一直自动补2:直到补全128位为止。

package aES; //加密任意长度字符串 // 提示:a占两个bit,长度为2,a占四个bit,长度为四 public class andlong { public String str; private StringToChars sun; public static StringBuilder hex; public static void main(String[] args) { String str="自李立国兴安安"; StringToChars sun =new StringToChars(); hex=sun.strTOhex(str); int b= (128-(hex.length()*4)%128)/8; while((hex.length()*4)%128!=0) { hex.append(b); } System.out.println(hex); } }

演示 :m.txt 读取之后补全,再做等分。 0d0a 6161 6161 6161 6177 7777 7777 7777

但是我等分之后再做加密 会报错。

最新回复(0)