AES加密学习笔记(二)

2 篇文章 1 订阅
2 篇文章 0 订阅
 接下来讲点编程实现上应该注意的具体问题。

先回顾一下每轮运算的操作步骤:

SubBytes(state)    对数据进行S字节变换
ShiftRows(state) 进行行变换
MixColumns(state)    进行列混合变换
AddRoundKey(state, Keys[ 当前轮密钥组] )  与当前轮的密钥进行异或

首先是S变换,当然计算机编程实现S变换的基本上都是用查表法,这没有什么好多说的。

接下来是行变换。其实由于计算机里面存储数据的顺序的习惯写法和State矩阵的定义是不一样的,比如数据在内存中按D0,D1,D2……D15的顺序存放,而state的定义是

D0    D4   D8   D12

D1    D5   D9   D13

D2    D6   D10 D14

D3    D7   D11 D15

行变换完后为:

D0    D4   D8   D12

D5    D9   D13 D1

D10  D14 D2   D6  

D15 D3   D7   D11

行变换完后在内存中存放顺序变为 {D0,D5,D10,D15}, {D4,D9,D14,D3}, {D8,D13,D2,D7} ,{ D12,D1,D6,D11} ,观察它们的规律,可知每个字的首字节为原来各字的首字节i=0,4,8,12,而每个字其后的字节为 (i+5n)mod 16 , n=1,2,3。行变换暂时先看到这里。

接下来是列变换,列变换是用矩阵

|  02  03  01  01  |                D0  D4   D8   D12

|  01  02  03  01  |         ×  D5  D9   D13 D1

|  01  01  02  03  |               D10 D14 D2   D6

|  03  01  01  02  |               D15 D3  D7   D11

这里我们一列一列的看。要生成第一列的结果,也就是内存中的第一个字,需要的变换为:

  [02    01 01  03 ]×D0

+[03  02  01  01]×D5

+[ 01  03 02  01]×D10

+[01  01  03  02]×D15

这里的×和+都是GF[2,8]域里的运算,+实际上就是异或。可以看到要生成一列,也就是内存中的一个32位字,需要先生成4个乘法结果字,再对它们做异或。注意到每个乘法结果字只和Dn有关,进一步注意到每行的系数实际上是可以通过循环移位来得到的,所以可以建立一个  [02    01  01  03 ]×Dn 的表,通过查表来得到每个行的结果向量,第一行可以直接查表得到,其余各行需要进行循环移位操作。

好了,再回头看看S变换和行变换。S变换是一对一的变换,如果我们把每轮开始的状态计为:

D0    D4   D8   D12

D1    D5   D9   D13

D2    D6   D10 D14

D3    D7   D11 D15

经过S变换后变为:

SD0  SD4      SD8   SD12

SD1  SD5      SD9   SD13

SD2  SD6      SD10  SD14

SD3  SD7      SD11  SD15

再经过行变换,然后是列变换,其中列变换的第一列为:

  [02     01  01  03 ]×SD0

+[03    02  01   01]×SD5

+[ 01   03  02  01]×SD10

+[01   01   03  02]×SD15

那么经过分析,发现其实可以建立一个 Dn到[02      01  01  03 ]×SDn的查找表,一次把三个步骤完成,设这个表为 SCol_Box,那么生成state的一列,也即结果中的一个32位字的过程为:

SCol_Box [ D[i*4]  ] ^

rot_l ( SCol_Box[  D[ 0xf &(i*4+5) ] ,1 ) ^

rot_l ( SCol_Box[  D[ 0xf &(i*4+10) ] ,2 ) ^

rot_l ( SCol_Box[  D[ 0xf &(i*4+15) ] ,3 ) ^

其中rot_l( w , n)表示对一个32位字 w进行循环左移n个字节。(注意在小端模式的计算机中,第一个字节是最低的,[02 01 01 03]变为[ 03 02 01 01],实际上是在循环左移,当然大端模式的计算机就是右移)

因此,将一轮中的三个操作合成了一个操作,而且每次操作只需要查4*4次表和3*4循环移位和异或运算,大大减少了计算量。

当然最后与密钥的异或运算,是不能与其它步骤合并的,不过这个操作本身开销也非常小。

 

说完了加密过程的优化,再说说解密过程。回顾一下解密的一轮的过程:

invShiftRows(state)            进行反行变换
invSubBytes(state)             对数据进行反S字节变换
AddRoundKey(state, Keys[ 当前轮] ) 与当前轮的密钥进行异或
invMixColumns(state)             进行反列混合变换

注意到反行变换和反S变换的顺序是可以直接互换的,那么就变成

invSubBytes(state)              对数据进行反S字节变换
invShiftRows(state)             进行反行变换
AddRoundKey(state, Keys[ 当前轮] ) 与当前轮的密钥进行异或
invMixColumns(state)              进行反列混合变换

可见与加密过程的差别仅仅是 与密钥的异或和列变换的顺序不同,而正是这个不同使我们不能直接象加密过程那样将三种操作合并。但是如果能把
AddRoundKey(state, Keys[ 当前轮] )      与当前轮的密钥进行异或
invMixColumns(state)                               进行反列混合变换

这两个操作的顺序交换一下,那么就和加密过程一样,可以采用同样的优化手段了。那么怎么交换这两个操作呢?

注意到反列变换可以看作是一个矩阵乘法操作,即

[State_Col]= [ iMix_Mat] × [ State_Key ] , State_Key表示经过AddKey操作后的状态,加[ ]表示以一个矩阵操作来理解。

而 [State_Key] = [State_Row] +[ RoundKey]

即 [State_Col]= [ iMix_Mat] × (  [State_Row] +[ RoundKey] ),而在GF[2,8]运算中是可以写成:

[State_Col]= [ iMix_Mat] × [State_Row]  +  

 [ iMix_Mat] ×[ RoundKey]

也就是说,可以先对 State_Row进行反列变换,再与经过反列变换的RoundKey相加,从而把流程变为了:

invSubBytes(state)  对数据进行反S字节变换
invShiftRows(state) 进行反行变换
invMixColumns(state)  进行反列混合变换

AddRoundKey(state,  iMixCol(Keys[ 当前轮] )) 与经过反列变换的当前轮的密钥进行异或

这样解密的轮过程顺序就完全和加密过程一样了,我们只需要事先计算出各轮密钥的反列变换后的值,并建立 Dn到 [0e  09  0d  0b] invS(Dn)的查找表,就可以同样把解密的三个步骤合并为一个步骤。

 

基本上,AES算法编程中需要注意的问题基本上都说了,当然要继续深究的话还是有很多东西可发掘的,比如这个S_Box是不是最优的呢?这个列变换的正变换矩阵和反变换矩阵能不能一样,从而只需要建立一张表呢?很多专家们在研究这些问题,作为非专业人士,我们就不要太计较了,等着专家们拿出结论来就行了。比如有篇论文就说现在S_Box的生成是先求倒数,再进行一次(F1,63)变换这种方式可以再改进,先{ 6B,5D}一次,再求倒数,再 {6B,5D}会更好,还有资料说形如

[ a1+1 ,a0 , a1 , a0

  a0,   a1+1,a0 , a1

  a1,  a0 , a1+1 ,a0

  a0,  a1 , a0  , a1+1]

的列变换矩阵可以使正反变换矩阵一样,最简单的形式就是a0=1,a1=2。等等等等,大家如果想在AES上改进做自己独有的加密算法的话,完全可以采用不同的S_Box和变换矩阵,甚至每轮都换个S_Box和变换矩阵。

说了这么多,都是用软件来实现,如果用硬件来实现的话,要关心的内容就有些不同了,因为硬件查表很费资源,所以就要用其原始的生成关系来生成逻辑,这就更不是我的专业了。

当然,最快的方式是直接用CPU提供的硬件指令,最新的IntelCPU提供了几条AES专用指令,使APE算法的开发速度和效率提高到令人发指的地步,仅仅需要写一条指令就可以完成一轮过程,具体过程什么都不用管了。

 

最后附上我学习过程中编的Python程序,还有从网上下载的一个C程序。这个C程序水平很高,给了我很多启发,并且使用也很简单。

AES.py

#coding=gbk
'''学习AES加密算法,仅为学习明了其整个原理和过程,不考虑效率,全部根据最直接的定义来实现
若要提高效率,可对其中使用的大量表格预生成,通过查表来实现
特别注意,AES规范中使用的字节顺序是按照大端模式来处理的,密钥和明文的存放格式还不同,如
密码的输入顺序是k1,k2,k3,k4,k5,k6,k7,k8,k9,k10,k11,k12,k13,k14,k15,k16,在AES中分组方式为:
 k1,k2,k3,k4
 k5,k6,k7,k8
 k9,k10,k11,k12
 k13,k14,k15,k16
 其中每一行可看作一个32位字,如果按大端模式来看就是k1k2k3k4,按小端模式就是k4k3k2k1,而规范中都是按大端模式来理解
 对于数据,假设输入顺序为a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,存放格式则又变为
 a1 , a5, a9 , a13
 a2 , a6, a10, a14
 a3 , a7, a11, a15
 a4 , a8, a12, a16
 而在做与密钥的异或运算时,又是按照 a1-k1 ,a2-k2,a3-k3,....a16,k16的一一对应的顺序来进行
 因此一定要注意顺序的问题,并且注意在小端的处理器上行左右移动是与之相反的。
 本程序完全按照字节来操作,并遵照规范中的定义
'''
def f_mod(a,b):
    '''AES算法中的取模'''
    n=0x8000
    c=b
    while b&0x8000==0:
        b<<=1
    while b>=c:
        if a&n!=0:
            a^=b
        n>>=1
        b>>=1

    return a

def f_mul(a,b,m=0x11b):
    '''AES算法中使用的乘法,模因子为0x11b'''
    #a&=0xff
    #b&=0xff
    c=0
    while b!=0:
        if b&1!=0:
            c=c^a
        b>>=1
        a<<=1
    if m!=0:
        c=f_mod(c,m)
    return c
'''AES中的乘法以及模因子0x11b的选择,可以使任一数的256次方与其本身同模,
也即任一数的255次方模为1,根据这一特性,可定义一个数的倒数1/c为c的254次方,
但是为每一个数求254次方是很费时的。注意到某些数比如3的1~255次幂是遍历整个GF2.8空间的,
每个数都可以作为一个3的n次幂出现,又3**n × 3**(255-n)==1,所以只要找出一个数c是3的n幂,
则它的倒数就是3的255-n次幂,从而只要建立一个3的255次的表就可以通过查表的方式来确定C对于
3的幂次,同时算出1/C对于3的幂次从而查出1/C'''

pow_3=[]
c=1
for i in range(255):
    c=f_mul(c,3)
    pow_3.append(c)

def f_inv(a):
    '''通过查表,求a的倒数'''
    if a==0:
        return 0
    a&=0xff
    i=pow_3.index(a)
    return pow_3[253-i]

'''s 盒的生成 是对每个数CC,取其倒数c=1/CC,再对c进行如下操作:
c^rol(c,1)^rol(c,2)^rol(c,3)^rol(c,4)^99
写成式子形式为 0xF1*c + 0x63 mod 0x101 , 或写为{F1,63}
其中F1理解为多项式 x7+x6+x5+x4+1,按我的理解, 乘x7即表示循环左移1位,x6表示rol两位
有篇文章认为将计算方法改为先{ 6B,5D}一次,再求倒数,再 {6B,5D}一次会有更好的效果
'''
def ror(c,n):
    c&=0xff
    if n>7:
        n=n%8
    d=((c<<(8-n))&0xff)|(c>>(n))
    return d
def rol(c,n):
    c&=0xff
    if n>7:
        n=n%8
    d=((c<<(n))&0xff)|(c>>(8-n))
    return d

def s_trans(c,p1,p2):
    d=0
    a=c
    while p1!=0:
        if p1&1:
            d^=a
        p1>>=1
        a=ror(a,1)
    d^=p2
    return d

s_box=[]
for i in range(256):
    c=f_inv(i)
    #或者用 d= s_trans(c,0xf1,0x63)
    d=c
    c=rol(c,1)
    d^=c
    c=rol(c,1)
    d^=c
    c=rol(c,1)
    d^=c
    c=rol(c,1)
    d^=c
    d^=99
    s_box.append(d)
'''s_box=[
99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171,118,
202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156,164, 114, 192,
183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21,
4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117,
9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132,
83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207,
208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,
81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210,
205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115,
96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219,
224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,
231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174,
8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139,
138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29,
158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85,
40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84,187, 22]'''


'''列混合运算矩阵系数和反变换矩阵系数
将输入作为一个4*4的字节矩阵,对其进行矩阵式乘法
[Out]=[mix_box]* [In]
而 逆运算是 [imix_box]*[Out]==[In]
根据一篇文献,形如
[ a1+1 ,a0 , a1 , a0
  a0,   a1+1,a0 , a1
  a1,  a0 , a1+1 ,a0
  a0,  a1 , a0  , a1+1]
  的矩阵可实现正反变换矩阵相同,其中最简的方式是a0=1,a1=2
'''    
mix_box=[ [ 2,3,1,1],[1,2,3,1],[1,1,2,3],[3,1,1,2]]
imix_box=[ [ 14,11,13,9],[9,14,11,13],[13,9,14,11],[11,13,9,14]]

def f_mix( box1,box2):
    '''列混合函数,可以理解为一个矩阵乘法'''
    r=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
    for i in range(4):
        for j in range(4):
            c=0
            for k in range(4):
                c^=f_mul(box1[i][k],box2[k][j])
            r[i][j]=c
    return r

            
'''行变换,比较简单,第一行不变,第二行循环左移1字节
,第三行循环左移2字节,第四行循环左移3字节'''
def roll(line,n):
    t=line[n:]
    t+=line[:n]
    return t

    
def f_line(box):
    box[1]=roll(box[1],1)
    box[2]=roll(box[2],2)
    box[3]=roll(box[3],3)
    return box

'''扩展密钥的生成,首先需要一个轮常数数组,这个轮常数是2**(n-1)在GF(2,8)的值,n为轮数,从1开始,
密钥以32位字组织者,每轮操作需要一个128位的密钥,
轮常数是在扩展密钥的长度为输入密钥的整倍数(128/192/256)时的那个字的第一个字节要与之异或的一个字节
因为轮操作数量当密钥为128位时是10轮,192位12轮,256位时14轮,
所以128位时需要10个轮常数,192位时需要12/(192/128)=8个,256位时需要14/2=7个'''
round_P=[]
for i in range(10):
    c=1<<i
    round_P.append(f_mod(c,0x11b))

def s_line(line):
    '''对一行中的每个元素进行s变换'''
    l=line[:]
    for i in range(len(line)):
        l[i]=s_box[line[i]]
    return l

def is_line(line):
	'''对一行中的每个元素进行反S变换'''
	l=line[:]
	for i in range(len(line)):
		l[i]=s_box.index(line[i])
	return l

def xor_line(line1,line2):
    '''对每一行中的每一个元素进行异或'''
    line=[]
    for i in range(len(line1)):
        line.append( line1[i]^line2[i])
    return line

Keys=[]
Nk=0
Nr=0
def setKey( inKey):
    '''生成每轮的密钥,密钥按4字节进行分组,128位、192位、256位分别对应 Nk=4、6、8
    每一轮加密需要使用一组4个双字的密钥,加上初始一次异或,各Nk下一共需要 44、52、60组密钥,
    除前面的Nk个密钥为用户输入的外,其它密钥根据一定的规则生成,这个规则为:
    Nk为输入密钥组长度、Nb为每轮需要使用的密钥组数(在AES算法中固定为4),Nr为需要进行的轮数,即为10,12,14
    Keys为存放生成密钥的二维数组
    则前面的Nk个复制输入的密钥,以下标为0开始,从下标为Nk个,执行以下的操作 :
    i=Nk
    while i<Nb*(Nr+1):
        temp=Keys[i-1]
        if i % Nk==0:
            temp= s_line ( roll(temp) ) xor round_P[i/Nk-1] #即当i是Nk的倍数时,先将前一组循环左移一字节,
                                            再对每字节进行S变换,再对首字节异或当前轮常数
        elif Nk>6 and i%Nk==4:	#其实就是Nk==8时,对第12、20、28轮还要进行以下操作
            temp=s_line(temp)
        Keys.append( Keys[i-Nk] xor temp)
        i+=1
    '''
    global Keys,round_P,Nk,Nr
    inKey=list(inKey)
    Nk=len(inKey)/4
    if Nk<=4:
        Nk=4
        Nr=10
    elif Nk<=6:
        Nk=6
        Nr=12
    else:
        Nk=8
        Nr=14
    kLen=len(inKey)

    for i in range(kLen):
        if type(inKey[i])==str:
            inKey[i]=ord(inKey[i])
    if kLen>32:
        kLen=32
    i=0
    temp=[0]*(Nk*4-kLen) #以0补足输入密钥长度
    inKey+=temp
    Keys=[]
    while i<Nk:
        temp=[0]*4
        for j in range(4):
            temp[j]=inKey[4*i+j]
        Keys.append(temp)
        i+=1
    while i<4*(Nr+1):
        temp=Keys[i-1][:]
        if i%Nk==0:
            temp=s_line(roll(temp,1))
            temp[0]=temp[0]^round_P[i/Nk-1]
        elif Nk>6 and i%Nk==4:
            temp=s_line(temp)
        temp=xor_line(Keys[i-Nk],temp)
        Keys.append(temp)
        i+=1

'''定义几种基本变换操作函数'''
def xorkey(data,key):
    '''将data与key异或,注意它们的顺序'''
    for i in range(4):
        for j in range(4):
            data[j][i]^=key[i][j]
    return data

def s_state(data):
    '''对一个数据的state块进行s变换'''
    for i in range(4):
        data[i]=s_line(data[i])
    return data

def rol_state(data):
    '''对一个数据state块各行进行行变换'''
    data[1]=roll(data[1],1)
    data[2]=roll(data[2],2)
    data[3]=roll(data[3],3)
    return data

def groupKey(r):
    k=[ Keys[r*4],Keys[r*4+1],Keys[r*4+2],Keys[r*4+3]]
    return k

def outState(state):
    out=[]
    for i in range(4):
        for j in range(4):
            out.append( state[j][i])
    return out

def printState(desc,state):
    '''打印各步骤的数据供观察和比对'''
    return 0    #不用的时候关闭打印
    if desc:
        print desc,':',
    for i in outState(state):
        print hex(i),
    print ''
def encrypt( incode):
    '''加密过程,每次输入16个字节,输出16个字节
    加密过程为:分组,与密钥异或,然后循环Nr-1轮以下操作:
    S变换,行变换,列变换,与扩展密钥异或
    然后进行最后一轮,区别是没有列变换'''
    if len(Keys)==0:
        print '还没有设置密码!'
        return []
    ilen=len(incode)
    temp=[0]*16
    if ilen>=16:
        ilen=16
    i=0
    for i in range(ilen):
        if type(incode[i])==str:
            temp[i]=ord(incode[i])
        else:
            temp[i]=incode[i]
    #分组
    state=[[0]*4,[0]*4,[0]*4,[0]*4]
    for i in range(4):
        for j in range(4):
            state[j][i]=temp[i*4+j]
    print state
    #第一次xor key
    state=xorkey(state,groupKey(0))
    print state
    printState('r1state',state)
    round=1
    while round<Nr:
        state=s_state(state)
        printState('r'+`round`+'sbox',state)
        
        state=rol_state(state)
        printState('r'+`round`+'rline',state)
        state=f_mix(mix_box,state)
        printState('r'+`round`+'mixCol',state)
        state=xorkey(state,groupKey(round))
        printState('r'+`round`+'Key',state)

        round+=1
    state=s_state(state)
    printState('r'+`round`+'sbox',state)
    state=rol_state(state)
    printState('r'+`round`+'rline',state)
    state=xorkey(state,groupKey(round))
    printState('r'+`round`+'Key',state)
    out=outState(state)
    printState('Out',state)
    return out


def is_line(line):
    '''对一行中的每个元素进行s变换'''
    l=line[:]
    for i in range(len(line)):
        l[i]=s_box.index(line[i])
    return l


def is_state(data):
    '''对一个数据的state块进行反s变换'''
    for i in range(4):
        data[i]=is_line(data[i])
    return data

def irol_state(data):
    '''对一个数据state块各行进反行变换'''
    data[1]=roll(data[1],3)
    data[2]=roll(data[2],2)
    data[3]=roll(data[3],1)
    return data
    
def Decrypt( incode):
    '''解密过程,是加密过程的反过程'''
    if len(Keys)==0:
        print '还没有设置密码!'
        return []
    if len(incode)!=16:
        print '错误的长度,每次只能处理16个字节'
        return []
        #分组
    state=[[0]*4,[0]*4,[0]*4,[0]*4]
    for i in range(4):
        for j in range(4):
            state[j][i]=incode[i*4+j]
    state=xorkey(state,groupKey(Nr))
    round=Nr-1
    while round>0:
        state=irol_state(state)
        state=is_state(state)
        state=xorkey(state,groupKey(round))
        state=f_mix(imix_box,state)
        round-=1
    #最后一轮
    state=irol_state(state)
    state=is_state(state)
    state=xorkey(state,groupKey(0))
    return outState(state)
                     


C代码,把头文件、C文件和示例放在一起

------------------------------------------------------------------------------
AES加密 头文件
------------------------------------------------------------------------------
// rijndael.h: interface for the rijndael class.
//
//
#if !defined(AFX_RIJNDAEL_H__65AE854C_7E77_4488_AC14_66161F67B341__INCLUDED_)
#define AFX_RIJNDAEL_H__65AE854C_7E77_4488_AC14_66161F67B341__INCLUDED_
typedef unsigned char   u1byte; /* an 8 bit unsigned character type */
typedef unsigned short  u2byte; /* a 16 bit unsigned integer type   */
typedef unsigned long   u4byte; /* a 32 bit unsigned integer type   */
typedef signed char     s1byte; /* an 8 bit signed character type   */
typedef signed short    s2byte; /* a 16 bit signed integer type     */
typedef signed long     s4byte; /* a 32 bit signed integer type     */
/* 2. Standard interface for AES cryptographic routines             */
/* These are all based on 32 bit unsigned values and may require    */
/* endian conversion for big-endian architectures                   */
#define LITTLE_ENDIAN
class AES
{
public:
 virtual void set_key(const u1byte key[], const u4byte key_bits) = 0;
 virtual void encrypt(const u1byte in_blk[16], u1byte out_blk[16]) = 0;
 virtual void decrypt(const u1byte in_blk[16], u1byte out_blk[16]) = 0;
}; 
/* 3. Basic macros for speeding up generic operations               */
/* Circular rotate of 32 bit values                                 */
#ifdef _MSC_VER
#  include <stdlib.h>
#  pragma intrinsic(_lrotr,_lrotl)
#  define rotr(x,n) _lrotr(x,n)
#  define rotl(x,n) _lrotl(x,n)
#else
#define rotr(x,n)   (((x) >> ((int)(n))) | ((x) << (32 - (int)(n))))
#define rotl(x,n)   (((x) << ((int)(n))) | ((x) >> (32 - (int)(n))))
#endif
/* Invert byte order in a 32 bit variable                           */
#define bswap(x)    (rotl(x, 8) & 0x00ff00ff | rotr(x, 8) & 0xff00ff00)
/* Extract byte from a 32 bit quantity (little endian notation)     */
#define byte(x,n)   ((u1byte)((x) >> (8 * n)))
/* Input or output a 32 bit word in machine order     */
#ifdef LITTLE_ENDIAN
#define u4byte_in(x)  (*(u4byte*)(x))
#define u4byte_out(x, v) (*(u4byte*)(x) = (v))
#else
#define u4byte_in(x)  bswap(*(u4byte)(x))
#define u4byte_out(x, v) (*(u4byte*)(x) = bswap(v))
#endif
 

class rijndael 
{
public:
 rijndael();
 virtual ~rijndael();
public:
 char *name(void);
 void set_key(const u1byte key[], const u4byte key_len);
 void encrypt(const u1byte in_blk[16], u1byte out_blk[16]);
 void decrypt(const u1byte in_blk[16], u1byte out_blk[16]);

private:
 u4byte  k_len;
 u4byte  e_key[64];
 u4byte  d_key[64];
};
 
#endif // !defined(AFX_RIJNDAEL_H__65AE854C_7E77_4488_AC14_66161F67B341__INCLUDED_)



--------------------------------------------------------------------------------------
c 文件
--------------------------------------------------------------------------------------
// rijndael.cpp: implementation of the rijndael class.
//
//
#include "stdafx.h"
//#include "MainFrm.h"
#include "rijndael.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//
// Construction/Destruction
//
rijndael::rijndael()
{
}
rijndael::~rijndael()
{
}
#define LARGE_TABLES
namespace
{
u1byte  pow_tab[256];
u1byte  log_tab[256];
u1byte  sbx_tab[256];
u1byte  isb_tab[256];
u4byte  rco_tab[ 10];
u4byte  ft_tab[4][256];
u4byte  it_tab[4][256];
#ifdef  LARGE_TABLES
  u4byte  fl_tab[4][256];
  u4byte  il_tab[4][256];
#endif
u4byte  tab_gen = 0;
#define ff_mult(a,b)    (a && b ? pow_tab[(log_tab[a] + log_tab[b]) % 255] : 0)
#define f_rn(bo, bi, n, k)                          \
    bo[n] =  ft_tab[0][byte(bi[n],0)] ^             \
             ft_tab[1][byte(bi[(n + 1) & 3],1)] ^   \
             ft_tab[2][byte(bi[(n + 2) & 3],2)] ^   \
             ft_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n)
#define i_rn(bo, bi, n, k)                          \
    bo[n] =  it_tab[0][byte(bi[n],0)] ^             \
             it_tab[1][byte(bi[(n + 3) & 3],1)] ^   \
             it_tab[2][byte(bi[(n + 2) & 3],2)] ^   \
             it_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n)
#ifdef LARGE_TABLES
#define ls_box(x)                \
    ( fl_tab[0][byte(x, 0)] ^    \
      fl_tab[1][byte(x, 1)] ^    \
      fl_tab[2][byte(x, 2)] ^    \
      fl_tab[3][byte(x, 3)] )
#define f_rl(bo, bi, n, k)                          \
    bo[n] =  fl_tab[0][byte(bi[n],0)] ^             \
             fl_tab[1][byte(bi[(n + 1) & 3],1)] ^   \
             fl_tab[2][byte(bi[(n + 2) & 3],2)] ^   \
             fl_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n)
#define i_rl(bo, bi, n, k)                          \
    bo[n] =  il_tab[0][byte(bi[n],0)] ^             \
             il_tab[1][byte(bi[(n + 3) & 3],1)] ^   \
             il_tab[2][byte(bi[(n + 2) & 3],2)] ^   \
             il_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n)
#else
#define ls_box(x)                            \
    ((u4byte)sbx_tab[byte(x, 0)] <<  0) ^    \
    ((u4byte)sbx_tab[byte(x, 1)] <<  8) ^    \
    ((u4byte)sbx_tab[byte(x, 2)] << 16) ^    \
    ((u4byte)sbx_tab[byte(x, 3)] << 24)
#define f_rl(bo, bi, n, k)                                      \
    bo[n] = (u4byte)sbx_tab[byte(bi[n],0)] ^                    \
        rotl(((u4byte)sbx_tab[byte(bi[(n + 1) & 3],1)]),  8) ^  \
        rotl(((u4byte)sbx_tab[byte(bi[(n + 2) & 3],2)]), 16) ^  \
        rotl(((u4byte)sbx_tab[byte(bi[(n + 3) & 3],3)]), 24) ^ *(k + n)
#define i_rl(bo, bi, n, k)                                      \
    bo[n] = (u4byte)isb_tab[byte(bi[n],0)] ^                    \
        rotl(((u4byte)isb_tab[byte(bi[(n + 3) & 3],1)]),  8) ^  \
        rotl(((u4byte)isb_tab[byte(bi[(n + 2) & 3],2)]), 16) ^  \
        rotl(((u4byte)isb_tab[byte(bi[(n + 1) & 3],3)]), 24) ^ *(k + n)
#endif
void gen_tabs(void)
{   u4byte  i, t;
    u1byte  p, q;
    // log and power tables for GF(2**8) finite field with 
    // 0x011b as modular polynomial - the simplest prmitive
    // root is 0x03, used here to generate the tables      
    for(i = 0,p = 1; i < 256; ++i)
    {
        pow_tab[i] = (u1byte)p; log_tab[p] = (u1byte)i;
        p = p ^ (p << 1) ^ (p & 0x80 ? 0x01b : 0);
    }
    log_tab[1] = 0; p = 1;
    for(i = 0; i < 10; ++i)
    {
        rco_tab[i] = p;
        p = (p << 1) ^ (p & 0x80 ? 0x1b : 0);
    }
    for(i = 0; i < 256; ++i)
    {  
        p = (i ? pow_tab[255 - log_tab[i]] : 0); q = p;
        q = (q >> 7) | (q << 1); p ^= q;
        q = (q >> 7) | (q << 1); p ^= q;
        q = (q >> 7) | (q << 1); p ^= q;
        q = (q >> 7) | (q << 1); p ^= q ^ 0x63;
        sbx_tab[i] = p; isb_tab[p] = (u1byte)i;
    }
    for(i = 0; i < 256; ++i)
    {
        p = sbx_tab[i];
#ifdef  LARGE_TABLES       
       
        t = p; fl_tab[0][i] = t;
        fl_tab[1][i] = rotl(t,  8);
        fl_tab[2][i] = rotl(t, 16);
        fl_tab[3][i] = rotl(t, 24);
#endif
        t = ((u4byte)ff_mult(2, p)) |
            ((u4byte)p <<  8) |
            ((u4byte)p << 16) |
            ((u4byte)ff_mult(3, p) << 24);
       
        ft_tab[0][i] = t;
        ft_tab[1][i] = rotl(t,  8);
        ft_tab[2][i] = rotl(t, 16);
        ft_tab[3][i] = rotl(t, 24);
        p = isb_tab[i];
#ifdef  LARGE_TABLES       
       
        t = p; il_tab[0][i] = t;
        il_tab[1][i] = rotl(t,  8);
        il_tab[2][i] = rotl(t, 16);
        il_tab[3][i] = rotl(t, 24);
#endif
        t = ((u4byte)ff_mult(14, p)) |
            ((u4byte)ff_mult( 9, p) <<  8) |
            ((u4byte)ff_mult(13, p) << 16) |
            ((u4byte)ff_mult(11, p) << 24);
       
        it_tab[0][i] = t;
        it_tab[1][i] = rotl(t,  8);
        it_tab[2][i] = rotl(t, 16);
        it_tab[3][i] = rotl(t, 24);
    }
    tab_gen = 1;
}
#define star_x(x) (((x) & 0x7f7f7f7f) << 1) ^ ((((x) & 0x80808080) >> 7) * 0x1b)
#define imix_col(y,x)       \
    u   = star_x(x);        \
    v   = star_x(u);        \
    w   = star_x(v);        \
    t   = w ^ (x);          \
   (y)  = u ^ v ^ w;        \
   (y) ^= rotr(u ^ t,  8) ^ \
          rotr(v ^ t, 16) ^ \
          rotr(t,24)
} // end of anonymous namespace
char *rijndael::name(void)
{
    return "rijndael";
}
// initialise the key schedule from the user supplied key  
#define loop4(i)                                    \
{   t = ls_box(rotr(t,  8)) ^ rco_tab[i];           \
    t ^= e_key[4 * i];     e_key[4 * i + 4] = t;    \
    t ^= e_key[4 * i + 1]; e_key[4 * i + 5] = t;    \
    t ^= e_key[4 * i + 2]; e_key[4 * i + 6] = t;    \
    t ^= e_key[4 * i + 3]; e_key[4 * i + 7] = t;    \
}
#define loop6(i)                                    \
{   t = ls_box(rotr(t,  8)) ^ rco_tab[i];           \
    t ^= e_key[6 * i];     e_key[6 * i + 6] = t;    \
    t ^= e_key[6 * i + 1]; e_key[6 * i + 7] = t;    \
    t ^= e_key[6 * i + 2]; e_key[6 * i + 8] = t;    \
    t ^= e_key[6 * i + 3]; e_key[6 * i + 9] = t;    \
    t ^= e_key[6 * i + 4]; e_key[6 * i + 10] = t;   \
    t ^= e_key[6 * i + 5]; e_key[6 * i + 11] = t;   \
}
#define loop8(i)                                    \
{   t = ls_box(rotr(t,  8)) ^ rco_tab[i];           \
    t ^= e_key[8 * i];     e_key[8 * i + 8] = t;    \
    t ^= e_key[8 * i + 1]; e_key[8 * i + 9] = t;    \
    t ^= e_key[8 * i + 2]; e_key[8 * i + 10] = t;   \
    t ^= e_key[8 * i + 3]; e_key[8 * i + 11] = t;   \
    t  = e_key[8 * i + 4] ^ ls_box(t);              \
    e_key[8 * i + 12] = t;                          \
    t ^= e_key[8 * i + 5]; e_key[8 * i + 13] = t;   \
    t ^= e_key[8 * i + 6]; e_key[8 * i + 14] = t;   \
    t ^= e_key[8 * i + 7]; e_key[8 * i + 15] = t;   \
} 
 
void rijndael::set_key(const u1byte in_key[], const u4byte key_len)
{   u4byte  i, t, u, v, w;
    if(!tab_gen)
        gen_tabs();
    k_len = (key_len + 31) / 32;
    e_key[0] = u4byte_in(in_key     );
 e_key[1] = u4byte_in(in_key +  4);
    e_key[2] = u4byte_in(in_key +  8);
 e_key[3] = u4byte_in(in_key + 12);
    switch(k_len)
    {
        case 4: t = e_key[3];
                for(i = 0; i < 10; ++i)
                    loop4(i);
                break;
        case 6: e_key[4] = u4byte_in(in_key + 16); t = e_key[5] = u4byte_in(in_key + 20);
                for(i = 0; i < 8; ++i)
                    loop6(i);
                break;
        case 8: e_key[4] = u4byte_in(in_key + 16); e_key[5] = u4byte_in(in_key + 20);
                e_key[6] = u4byte_in(in_key + 24); t = e_key[7] = u4byte_in(in_key + 28);
                for(i = 0; i < 7; ++i)
                    loop8(i);
                break;
    }
    d_key[0] = e_key[0]; d_key[1] = e_key[1];
    d_key[2] = e_key[2]; d_key[3] = e_key[3];
    for(i = 4; i < 4 * k_len + 24; ++i)
    {
        imix_col(d_key[i], e_key[i]);
    }
    return;
}
// encrypt a block of text 
#define f_nround(bo, bi, k) \
    f_rn(bo, bi, 0, k);     \
    f_rn(bo, bi, 1, k);     \
    f_rn(bo, bi, 2, k);     \
    f_rn(bo, bi, 3, k);     \
    k += 4
#define f_lround(bo, bi, k) \
    f_rl(bo, bi, 0, k);     \
    f_rl(bo, bi, 1, k);     \
    f_rl(bo, bi, 2, k);     \
    f_rl(bo, bi, 3, k)
void rijndael::encrypt(const u1byte in_blk[16], u1byte out_blk[16])
{   u4byte  b0[4], b1[4], *kp;
    b0[0] = u4byte_in(in_blk    ) ^ e_key[0]; b0[1] = u4byte_in(in_blk +  4) ^ e_key[1];
    b0[2] = u4byte_in(in_blk + 8) ^ e_key[2]; b0[3] = u4byte_in(in_blk + 12) ^ e_key[3];
    kp = e_key + 4;
    if(k_len > 6)
    {
        f_nround(b1, b0, kp); f_nround(b0, b1, kp);
    }
    if(k_len > 4)
    {
        f_nround(b1, b0, kp); f_nround(b0, b1, kp);
    }
    f_nround(b1, b0, kp); f_nround(b0, b1, kp);
    f_nround(b1, b0, kp); f_nround(b0, b1, kp);
    f_nround(b1, b0, kp); f_nround(b0, b1, kp);
    f_nround(b1, b0, kp); f_nround(b0, b1, kp);
    f_nround(b1, b0, kp); f_lround(b0, b1, kp);
    u4byte_out(out_blk,      b0[0]); u4byte_out(out_blk +  4, b0[1]);
    u4byte_out(out_blk +  8, b0[2]); u4byte_out(out_blk + 12, b0[3]);
}
// decrypt a block of text 
#define i_nround(bo, bi, k) \
    i_rn(bo, bi, 0, k);     \
    i_rn(bo, bi, 1, k);     \
    i_rn(bo, bi, 2, k);     \
    i_rn(bo, bi, 3, k);     \
    k -= 4
#define i_lround(bo, bi, k) \
    i_rl(bo, bi, 0, k);     \
    i_rl(bo, bi, 1, k);     \
    i_rl(bo, bi, 2, k);     \
    i_rl(bo, bi, 3, k)
void rijndael::decrypt(const u1byte in_blk[16], u1byte out_blk[16])
{   u4byte  b0[4], b1[4], *kp;
    b0[0] = u4byte_in(in_blk     ) ^ e_key[4 * k_len + 24];
 b0[1] = u4byte_in(in_blk +  4) ^ e_key[4 * k_len + 25];
    b0[2] = u4byte_in(in_blk +  8) ^ e_key[4 * k_len + 26];
 b0[3] = u4byte_in(in_blk + 12) ^ e_key[4 * k_len + 27];
    kp = d_key + 4 * (k_len + 5);
    if(k_len > 6)
    {
        i_nround(b1, b0, kp); i_nround(b0, b1, kp);
    }
    if(k_len > 4)
    {
        i_nround(b1, b0, kp); i_nround(b0, b1, kp);
    }
    i_nround(b1, b0, kp); i_nround(b0, b1, kp);
    i_nround(b1, b0, kp); i_nround(b0, b1, kp);
    i_nround(b1, b0, kp); i_nround(b0, b1, kp);
    i_nround(b1, b0, kp); i_nround(b0, b1, kp);
    i_nround(b1, b0, kp); i_lround(b0, b1, kp);
    u4byte_out(out_blk,     b0[0]); u4byte_out(out_blk +  4, b0[1]);
    u4byte_out(out_blk + 8, b0[2]); u4byte_out(out_blk + 12, b0[3]);
}

---------------------------------------------------------------------------
调用方法
---------------------------------------------------------------------------
void Caesproject2Dlg::OnBnClickedButton1()//加密按钮按下
{
 int i;
 CString s,s1;//s用来接收明文,s1用来接收密钥
 rijndael rij;//加密类的对象
 mingwen.GetWindowText(s);//从文本框获取明文
 length=s.GetLength();//明文长度length是Caesproject2Dlg类的成员变量
 N=length/16;//每16字节一组,N是Caesproject2Dlg类的成员变量
  if(length%16==0) //作用是当明文长度不是128bit整数倍时调整N值
   {
    N=N;
   }
   else
   {
    length=length+16-length%16;
    N=N+1;
   }
  LPTSTR lpsz = new TCHAR[s.GetLength()+1];
        _tcscpy(lpsz,s);//将明文拷贝到lpsz指向的内存

    if(s.GetLength()%16!=0) //如果明文长度不是128bit的倍数用空格补齐剩余的位
   {
    for(i=0;i<(int)(16-s.GetLength()%16);i++)
    {
     lpsz[length-16+s.GetLength()%16+i]=' ';
    }
   }
 unsigned char *e_bit;//用于指向加密后的密文
 e_bit=new unsigned char [length];

 miyao.GetWindowText(s1);//从文本框中取得密钥
  LPTSTR k_bit = new TCHAR[256];
        _tcscpy(k_bit,s1);//将密钥拷贝到k_bit指向的内存

  rij.set_key((unsigned char *)k_bit,256);//设置密钥
  for(int j=0;j<N;j++)//循环加密N次,每次128bit
  {
   rij.encrypt((unsigned char *)lpsz,e_bit);
   lpsz+=16;
   e_bit+=16;
  }
  lpsz=lpsz-16*N;//明文指针回到明文头
  e_bit=e_bit-16*N;//密文指针回到密文头
  miwen.SetWindowText((LPCTSTR)e_bit);//显示密文
 // TODO: 在此添加控件通知处理程序代码
}
void Caesproject2Dlg::OnBnClickedButton2()//解密按钮按下
{
 CString c,s1;//c用来接收密文,s1用来接收密钥//length密文长度,N分组数都为成员变量
 rijndael rij;//加密类的对象
 miwen.GetWindowText(c);//从文本框取得密文
 N=length/16;//取得组数做为循环次数
 LPTSTR lpsz = new TCHAR[c.GetLength()+1];
    _tcscpy(lpsz,c);//将密文拷贝到lpsz指向的内存
 miyao.GetWindowText(s1);//从文本框获取密钥
  LPTSTR k_bit = new TCHAR[256];
        _tcscpy(k_bit,s1);//将密钥拷贝到k_bit指向的内存

 rij.set_key((unsigned char *)k_bit,256);//设置密钥
 unsigned char *m_bit;//用于指向解密后的明文
 m_bit=new unsigned char [length];
 
  for(int j=0;j<N;j++) //循环解密N次
  {
   rij.decrypt((unsigned char *)lpsz,m_bit);
   m_bit+=16;
   lpsz+=16;
  }
  m_bit=m_bit-16*N;//解密后的明文指针回到明文头
  lpsz=lpsz-16*N;//密文指针回到密文头
  jmmingwen.SetWindowText((LPCTSTR)m_bit);//显示明文

 // TODO: 在此添加控件通知处理程序代码
}

/*说明:Caesproject2Dlg类为对话框类由MFC向导自动生成,需要在Caesproject2Dlg类添加两个成员变量(添加在头文件的类定义中):
int length,N;//length密文长度,N分组数*/


 

  • 0
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
AES(Advanced Encryption Standard)加密是一种对称加密算法,也是目前应用非常广泛的加密算法之一。它采用分组加密方式,将明文分为固定长度的数据块,并通过多轮的加密运算来进行加密。其核心运算包括字节替换、行移位、列混淆和轮密钥加等步骤,通过多轮运算来实现高强度的加密。 相比之下,异或加密是一种简单的位运算。异或运算是指对两个进制数相同位置的位进行比较,若位相同则结果为0,若位不同则结果为1。异或加密是通过将明文与密钥进行异或运算得到密文的一种加密方式。由于异或运算具有逆运算的性质,所以可以通过再一次对密文与密钥进行异或运算,得到原始的明文。 相比而言,AES加密更加安全可靠。AES加密采用复杂的运算过程和密钥扩展机制,其安全性经过广泛的验证和应用。而异或加密相对简单,容易受到频率分析等攻击手段的威胁。异或加密的安全性依赖于密钥的安全性,对密钥的管理和保护要求较高。 因此,在实际应用中,为了保证数据的安全性,一般更倾向于选择使用AES加密。不过,异或加密在一些特殊情况下也可作为一种简单加密方式来使用,如在一些简单的防护算法、校验算法和简易数据加密等场景中。但需要注意的是,异或加密的安全性相对较低,不适用于对抗一些高级攻击手段的场景。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值