Crypto-对称密码(一)

对称密码概述

  • 对称密码:其实就是使用同一种密钥进行加密解密。

question : 非对称密码是对称密码之后提出来的,我们知道非对称密码比对称密码安全,那么为什么还要用对称密码呢?

answer:对称密码的计算速度比非对称密码快多了,非对称密码考虑的东西比较多,例如密钥过长、算法较复杂、需要分块填充算法等等。

大部分 对称密码都是基于加法(ADD)、移位(Rotate)、异或(Xor)。简称ARX操作。 非对称密码会有很多像幂运算、指数运算等等需要算力的的操作。

Des加密

  • Des是一种典型的块加密,什么是块加密呢,就是将明文分为一块一块的,分别对明文的每一块进行加密,然后拼接得到密文。
  • Des的特点
    • 输入的是64位
    • 输出的是64位
    • 密钥是64位,使用的密钥中的56位是用来加密的,后8位是作为奇偶校验位,或者丢弃掉。
    • 采用Feistel迭代结构
      • 明文经过16轮迭代得到密文
      • 密文经过类似的16轮迭代得到明文

image-20240117142613578

明文初始置换
  • 64位的明文分组x经过一个初始置换函数,函数名称叫做IP,通过该函数生成64位的x0,分为前半部分和后半部分,分别是L0和R0。
    • 将第58位换到第1位,第50位换到第2位…以此类推。
    • 得到的结果是前面32位为L0,后面32位为R0。
 1, 2, 3, 4, 5, 6, 7, 8
 9,10,11,12,13,14,15,16
17,18,19,20,21,22,23,24
25,26,27,28,29,30,31,32
33,34,35,36,37,38,39,40
41,42,43,44,45,46,47,48
49,50,51,52,53,54,55,56
57,58,59,60,61,62,63,64
得到结果
58,50,42,34,26,18,10,2
60,52,44,36,28,20,12,4
62,54,46,38,30,22,14,6
64,56,48,40,32,24,16,8
57,49,41,33,25,17,9 ,1
59,51,43,35,27,19,11,3
61,53,45,37,29,21,13,5
63,55,47,39,31,23,15,7

那么我们可以构造一个IP函数,python代码如下:

_IP = [57, 49, 41, 33, 25, 17, 9,  1,
	59, 51, 43, 35, 27, 19, 11, 3,
	61, 53, 45, 37, 29, 21, 13, 5,
	63, 55, 47, 39, 31, 23, 15, 7,
	56, 48, 40, 32, 24, 16, 8,  0,
	58, 50, 42, 34, 26, 18, 10, 2,
	60, 52, 44, 36, 28, 20, 12, 4,
	62, 54, 46, 38, 30, 22, 14, 6
]

def IP(plain: List[int]):
    return list(map(lambda x: plain[x], _IP))

为什么每个都减1呢,那是因为数组下标从0开始的。

获取子密钥
  • 在进行第一轮加密之前,我们还需要通过密钥生成16轮的子密钥,之前讲了参与运算的密钥长度只有56位,后8位为奇偶校验,那么如何生成呢?
    • 1、将64位密钥去掉8个校验位,用56位密钥[7*8]按照如下表格进行置换。
      image-20240117110427455
    • 2、置换的结果也是[7*8],那么取前28位为C0,后28位为D0。
    • 3、需要分别对C0,D0进行循环移位(例如:123456移位得到234561)。移位的次数是一次或者两次,移动多少次跟轮次有关系,轮次关系如下图:
      image-20240117111315983
    • 4、移动后,将C0和D0进行合并,然后通过置换得到48位子密钥(去掉了8个),置换表格如下:
      image-20240117111818442
      实现代码如下:
__pc1 = [56, 48, 40, 32, 24, 16,  8,
	  0, 57, 49, 41, 33, 25, 17,
	  9,  1, 58, 50, 42, 34, 26,
	 18, 10,  2, 59, 51, 43, 35,
	 62, 54, 46, 38, 30, 22, 14,
	  6, 61, 53, 45, 37, 29, 21,
	 13,  5, 60, 52, 44, 36, 28,
	 20, 12,  4, 27, 19, 11,  3
]

__pc2 = [
	13, 16, 10, 23,  0,  4,
	 2, 27, 14,  5, 20,  9,
	22, 18, 11,  3, 25,  7,
	15,  6, 26, 19, 12,  1,
	40, 51, 30, 36, 46, 54,
	29, 39, 50, 44, 32, 47,
	43, 48, 38, 55, 33, 52,
	45, 41, 49, 35, 28, 31
]
ROTATIONS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]

def PC_1(key: List[int]):
    return list(map(lambda x: key[x], __pc1))

def PC_2(key: List[int]):
    return list(map(lambda x: key[x], __pc2))

def get_sub_key(key: List[int]):
    key = PC_1(key) # PC-1置换
    L, R = key[:28], key[28:] # 分成两半

    skeys = []

    for i in range(16):
        for j in range(ROTATIONS[i]): # 根据轮次左移
            L = L[1:] + L[:1]
            R = R[1:] + R[:1]

        skeys.append(PC_2(L+R)) # PC-2置换
    
    return skeys

密码函数F
  • 该函数的作用就是将(明文数据的右半部分)32位的数据和48位的数据进行加密得到32位的数据。

    • 拓展置换E:将明文数据的右半部分进行32位扩展为48位。可以叫做位选择函数,也称为E盒。(会发现有些重复取值导致增加了16位)
    __expansion_table = [
      31,  0,  1,  2,  3,  4,
       3,  4,  5,  6,  7,  8,
       7,  8,  9, 10, 11, 12,
      11, 12, 13, 14, 15, 16,
      15, 16, 17, 18, 19, 20,
      19, 20, 21, 22, 23, 24,
      23, 24, 25, 26, 27, 28,
      27, 28, 29, 30, 31,  0
    ]
    def EP(data: List[int]):  # 扩展置换
      return list(map(lambda x: data[x], __expansion_table))
    
    
    • 异或:将扩展的48位的输出结果 我们把它叫做E(Ri),再与48位的子密钥进行异或运算。
      R = list(map(lambda x, y: x ^ y, R, skeys[index]))  # 异或
    
    • S盒替换:将异或得到的48位结果分为8个6位的块,然后每一块通过对应的一个S盒产出一个4位的输出,最终就是32位。
    __sbox = [...]  # 内容过长,请完整代码文件
    B = [R[:6], R[6:12], R[12:18], R[18:24], R[24:30], R[30:36], R[36:42], R[42:]]  # 分成八份
    Bn = [0] * 32
    pos = 0
    for i in range(8):
        #  计算该使用S盒的行坐标和列坐标
        row = (B[j][0] << 1) + B[j][5]
        col = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
    
        sb = __sbox[j][row][col]
    
        Bn[pos + 0] = (sb & 8) >> 3  # 四位输出
        Bn[pos + 1] = (sb & 4) >> 2
        Bn[pos + 2] = (sb & 2) >> 1
        Bn[pos + 3] = (sb & 1)
    
        pos += 4
    
    
    • P盒替换:将S盒中得到的32位结果进行P盒置换。
    __p = [
    15, 6, 19, 20, 28, 11,
    27, 16, 0, 14, 22, 25,
    4, 17, 30, 9, 1, 7,
    23,13, 31, 26, 2, 8,
    18, 12, 29, 5, 21, 10,
    3, 24
    ]
    def P(data: List[int]):  # P置换
      return list(map(lambda x: data[x], __p))
    
逆置换
  • 也叫最终置换,就是第一步的置换的逆操作,我们记作
    I P − 1 IP^{-1} IP1
    例如,第一次置换的时候我们把将第1位换到了第40位,那么通过逆置换,就会把第40位换到第1位。置换表格如下。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    _FP = [
    	39,  7, 47, 15, 55, 23, 63, 31,
    	38,  6, 46, 14, 54, 22, 62, 30,
    	37,  5, 45, 13, 53, 21, 61, 29,
    	36,  4, 44, 12, 52, 20, 60, 28,
    	35,  3, 43, 11, 51, 19, 59, 27,
    	34,  2, 42, 10, 50, 18, 58, 26,
    	33,  1, 41,  9, 49, 17, 57, 25,
    	32,  0, 40,  8, 48, 16, 56, 24
    ]
    
    def FP(plain: List[int]):
        return list(map(lambda x: plain[x], _FP))
    
    
导入密码学库加密脚本如下:
from Crypto.Cipher import DES

import pyDes
des = DES.new(b'12345678',DES.MODE_ECB)

print(des.encrypt(b"12345678"))
des = pyDes.des(b'12345678')
print(des.encrypt(b'12345678'))
加密脚本如下:
from operator import add
from typing import List
from functools import reduce

_IP = [57, 49, 41, 33, 25, 17, 9,  1,
	59, 51, 43, 35, 27, 19, 11, 3,
	61, 53, 45, 37, 29, 21, 13, 5,
	63, 55, 47, 39, 31, 23, 15, 7,
	56, 48, 40, 32, 24, 16, 8,  0,
	58, 50, 42, 34, 26, 18, 10, 2,
	60, 52, 44, 36, 28, 20, 12, 4,
	62, 54, 46, 38, 30, 22, 14, 6
]

def IP(plain: List[int]):
    return list(map(lambda x: plain[x], _IP))

__pc1 = [56, 48, 40, 32, 24, 16,  8,
	  0, 57, 49, 41, 33, 25, 17,
	  9,  1, 58, 50, 42, 34, 26,
	 18, 10,  2, 59, 51, 43, 35,
	 62, 54, 46, 38, 30, 22, 14,
	  6, 61, 53, 45, 37, 29, 21,
	 13,  5, 60, 52, 44, 36, 28,
	 20, 12,  4, 27, 19, 11,  3
]

__pc2 = [
	13, 16, 10, 23,  0,  4,
	 2, 27, 14,  5, 20,  9,
	22, 18, 11,  3, 25,  7,
	15,  6, 26, 19, 12,  1,
	40, 51, 30, 36, 46, 54,
	29, 39, 50, 44, 32, 47,
	43, 48, 38, 55, 33, 52,
	45, 41, 49, 35, 28, 31
]
ROTATIONS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]

def PC_1(key: List[int]):
    return list(map(lambda x: key[x], __pc1))

def PC_2(key: List[int]):
    return list(map(lambda x: key[x], __pc2))

def get_sub_key(key: List[int]):
    key = PC_1(key) # PC-1置换
    L, R = key[:28], key[28:] # 分成两半

    skeys = []

    for i in range(16):
        for j in range(ROTATIONS[i]): # 根据轮次左移
            L = L[1:] + L[:1]
            R = R[1:] + R[:1]

        skeys.append(PC_2(L+R)) # PC-2置换
    
    return skeys

__expansion_table = [
	31,  0,  1,  2,  3,  4,
	 3,  4,  5,  6,  7,  8,
	 7,  8,  9, 10, 11, 12,
	11, 12, 13, 14, 15, 16,
	15, 16, 17, 18, 19, 20,
	19, 20, 21, 22, 23, 24,
	23, 24, 25, 26, 27, 28,
	27, 28, 29, 30, 31,  0
]
__sbox = [
		# S1
		[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
		 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
		 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
		 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],

		# S2
		[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
		 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
		 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
		 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],

		# S3
		[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
		 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
		 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
		 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],

		# S4
		[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
		 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
		 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
		 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],

		# S5
		[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
		 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
		 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
		 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],

		# S6
		[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
		 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
		 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
		 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],

		# S7
		[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
		 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
		 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
		 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],

		# S8
		[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
		 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
		 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
		 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
	]
__p = [
	15, 6, 19, 20, 28, 11,
	27, 16, 0, 14, 22, 25,
	4, 17, 30, 9, 1, 7,
	23,13, 31, 26, 2, 8,
	18, 12, 29, 5, 21, 10,
	3, 24
]

def EP(data: List[int]):  # 扩展置换
    return list(map(lambda x: data[x], __expansion_table))

def P(data: List[int]):  # P置换
    return list(map(lambda x: data[x], __p))

def F(index: int, R: List[int], skeys: List[List[int]]):
    """
    index: 代表这是第几轮
    R: 输入数据
    skeys: 子密钥数组
    """
    R = EP(R)  # 扩展置换
    R = list(map(lambda x, y: x ^ y, R, skeys[index]))  # 异或

    B = [R[:6], R[6:12], R[12:18], R[18:24], R[24:30], R[30:36], R[36:42], R[42:]]  # 分成八份

    Bn = [0] * 32
    pos = 0
    for i in range(8):
        #  计算该使用S盒的行坐标和列坐标
        row = (B[i][0] << 1) + B[i][5]
        col = (B[i][1] << 3) + (B[i][2] << 2) + (B[i][3] << 1) + B[i][4]

        sb = __sbox[i][(row << 4) + col]

        Bn[pos + 0] = (sb & 8) >> 3  # 四位输出
        Bn[pos + 1] = (sb & 4) >> 2
        Bn[pos + 2] = (sb & 2) >> 1
        Bn[pos + 3] = (sb & 1)

        pos += 4
    R = P(Bn)
    return R

_FP = [
	39,  7, 47, 15, 55, 23, 63, 31,
	38,  6, 46, 14, 54, 22, 62, 30,
	37,  5, 45, 13, 53, 21, 61, 29,
	36,  4, 44, 12, 52, 20, 60, 28,
	35,  3, 43, 11, 51, 19, 59, 27,
	34,  2, 42, 10, 50, 18, 58, 26,
	33,  1, 41,  9, 49, 17, 57, 25,
	32,  0, 40,  8, 48, 16, 56, 24
]

def FP(plain: List[int]):
    return list(map(lambda x: plain[x], _FP))


key = b'12345678'
plain = b'12345678'

# 转为二进制数组
key = reduce(add, [list(map(int, bin(i)[2:].zfill(8))) for i in key])
plain = reduce(add, [list(map(int, bin(i)[2:].zfill(8))) for i in plain])
skeys = get_sub_key(key)

block = IP(plain)

L, R = block[:32], block[32:]
for i in range(16):
    tpR = R[:]
    R = F(i, R, skeys)
    R = list(map(lambda x, y: x ^ y, R, L))
    L = tpR
block = R + L
block = FP(block)
enc = bytes([int(''.join(map(str,block[i*8:(i+1)*8])),2) for i in range(8)])
    
print(enc)

解密脚本如下:
from Crypto.Util.number import *
from operator import add
from typing import List
from functools import reduce
# 已知密文c
c = b'\x96\xd0\x02\x88x\xd5\x8c\x89'
# 已知密钥为key    最终结果m为b'12345678'
key = b'12345678'

__pc1 = [56, 48, 40, 32, 24, 16, 8,
         0, 57, 49, 41, 33, 25, 17,
         9, 1, 58, 50, 42, 34, 26,
         18, 10, 2, 59, 51, 43, 35,
         62, 54, 46, 38, 30, 22, 14,
         6, 61, 53, 45, 37, 29, 21,
         13, 5, 60, 52, 44, 36, 28,
         20, 12, 4, 27, 19, 11, 3
         ]

__pc2 = [
    13, 16, 10, 23, 0, 4,
    2, 27, 14, 5, 20, 9,
    22, 18, 11, 3, 25, 7,
    15, 6, 26, 19, 12, 1,
    40, 51, 30, 36, 46, 54,
    29, 39, 50, 44, 32, 47,
    43, 48, 38, 55, 33, 52,
    45, 41, 49, 35, 28, 31
]
ROTATIONS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]


def PC_1(key: List[int]):
    return list(map(lambda x: key[x], __pc1))


def PC_2(key: List[int]):
    return list(map(lambda x: key[x], __pc2))


def get_sub_key(key: List[int]):
    key = PC_1(key)  # PC-1置换
    L, R = key[:28], key[28:]  # 分成两半

    skeys = []

    for i in range(16):
        for j in range(ROTATIONS[i]):  # 根据轮次左移
            L = L[1:] + L[:1]
            R = R[1:] + R[:1]

        skeys.append(PC_2(L + R))  # PC-2置换

    return skeys
_IP = [57, 49, 41, 33, 25, 17, 9, 1,
       59, 51, 43, 35, 27, 19, 11, 3,
       61, 53, 45, 37, 29, 21, 13, 5,
       63, 55, 47, 39, 31, 23, 15, 7,
       56, 48, 40, 32, 24, 16, 8, 0,
       58, 50, 42, 34, 26, 18, 10, 2,
       60, 52, 44, 36, 28, 20, 12, 4,
       62, 54, 46, 38, 30, 22, 14, 6
       ]

__sbox = [
    # S1
    [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
     0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
     4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
     15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],

    # S2
    [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
     3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
     0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
     13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],

    # S3
    [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
     13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
     13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
     1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],

    # S4
    [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
     13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
     10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
     3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],

    # S5
    [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
     14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
     4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
     11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],

    # S6
    [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
     10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
     9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
     4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],

    # S7
    [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
     13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
     1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
     6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],

    # S8
    [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
     1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
     7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
     2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
]
__p = [
    15, 6, 19, 20, 28, 11,
    27, 16, 0, 14, 22, 25,
    4, 17, 30, 9, 1, 7,
    23, 13, 31, 26, 2, 8,
    18, 12, 29, 5, 21, 10,
    3, 24
]
__expansion_table = [
    31, 0, 1, 2, 3, 4,
    3, 4, 5, 6, 7, 8,
    7, 8, 9, 10, 11, 12,
    11, 12, 13, 14, 15, 16,
    15, 16, 17, 18, 19, 20,
    19, 20, 21, 22, 23, 24,
    23, 24, 25, 26, 27, 28,
    27, 28, 29, 30, 31, 0
]
_FP = [
	39,  7, 47, 15, 55, 23, 63, 31,
	38,  6, 46, 14, 54, 22, 62, 30,
	37,  5, 45, 13, 53, 21, 61, 29,
	36,  4, 44, 12, 52, 20, 60, 28,
	35,  3, 43, 11, 51, 19, 59, 27,
	34,  2, 42, 10, 50, 18, 58, 26,
	33,  1, 41,  9, 49, 17, 57, 25,
	32,  0, 40,  8, 48, 16, 56, 24
]

def FP(plain: List[int]):
    return list(map(lambda x: plain[x], _FP))
def EP(data: List[int]):  # 扩展置换
    return list(map(lambda x: data[x], __expansion_table))
def IP(plain: List[int]):
    return list(map(lambda x: plain[x], _IP))
def P(data: List[int]):  # P置换
    return list(map(lambda x: data[x], __p))
def F(index: int, R: List[int], skeys: List[List[int]]):
    """
    index: 代表这是第几轮
    R: 输入数据
    skeys: 子密钥数组
    """
    R = EP(R)  # 扩展置换
    R = list(map(lambda x, y: x ^ y, R, skeys[index]))  # 异或

    B = [R[:6], R[6:12], R[12:18], R[18:24], R[24:30], R[30:36], R[36:42], R[42:]]  # 分成八份

    Bn = [0] * 32
    pos = 0
    for i in range(8):
        #  计算该使用S盒的行坐标和列坐标
        row = (B[i][0] << 1) + B[i][5]
        col = (B[i][1] << 3) + (B[i][2] << 2) + (B[i][3] << 1) + B[i][4]

        sb = __sbox[i][(row << 4) + col]

        Bn[pos + 0] = (sb & 8) >> 3  # 四位输出
        Bn[pos + 1] = (sb & 4) >> 2
        Bn[pos + 2] = (sb & 2) >> 1
        Bn[pos + 3] = (sb & 1)

        pos += 4
    R = P(Bn)
    return R

# 已知key,那么我们可以通过求出16轮每一轮的子密钥。不过key为字节,需要转为2进制。
key = reduce(add, [list(map(int, bin(i)[2:].zfill(8))) for i in key])
skeys = get_sub_key(key)

#  将密文转化为二进制
c = reduce(add, [list(map(int, bin(i)[2:].zfill(8))) for i in c])
# 由于密文是通过逆置换来的,所以我们需要顺置换一次。也就是加密的IP函数。
c = IP(c)
# 将c分成左半部分和右半部分 因为求出的结果前部分为右半,所以记为R,同理后半记为L。
R = c[:32]
L = c[32:]
#  先分析第16轮到第15轮,也就是分析L16、R16  如何到L15、R15,首先L16就是R15 ,然后R16是R15与k16记性F函数加密然后与L15异或得到的。这样的话
#  我们知道R16  R15 就 可以得到L15 。

for i in range(16):

    tmpR = R  # L16赋值给R15 ,因为他两相等
    R = L
    tmp = F(15-i, R, skeys)
    L = list(map(lambda x, y: x ^ y, tmpR, tmp))

# 得到第一轮的L 和R ,需要 再置换一下,明文初始置换的逆置换就是加密的最终置换。
block = L + R
block = FP(block)
m = (long_to_bytes(int((''.join(map(str, block))), 2))) #将得到的结果转化为字符。
print(m)

Aes加密

  • 和Des一样,为了取代Des而出现的新的对称密码标准。最终在2000年选用了名为Rijndael对称密码,将其确认为AES。

  • 基本信息如下

    • 1、输入:128bit
    • 2、输出:128bit
    • 3、SPN(Substitution-permutation network)网络结构(代换-置换网络)
  • Aes和Des的区别

    • Des输入的是64位,Aes是128位
    • Des将明文分成两部分,左边和右边,每次都使用密钥对右边的数据进行F函数加密然后和左边数据进行异或得到下一轮右边的数据,然后直接将上一轮右边的数据放在左边。Aes则不会分为左右两边,而是直接对明文进行处理。
    • Des的Feilstel中的F函数可以选择可逆,也可以不可逆,因为解密的时候不影响。Aes的SPN需要可逆运算。
  • AES加密算法图解

    image-20240119101031626
    如上图所示,分为左中右三部分,右边是密钥的生成图解,中间是对明文加密的图解,左边是对明文加密中每一个加密的算法图解。
    明文加密涉及的操作有4种,密钥如果是128位,进行10轮迭代,如果是192位,进行12轮迭代,如果是256位,进行256轮迭代。这四个操作分别是

    1、轮密钥加(AddRoundKey)

    2、字节替换(SubBytes)

    3、行置换(ShiftRows)

    4、列混淆(MixColumn)

    那么我们一次来看看是如何操作的

轮密钥加(AddRoundKey)

将明文的每一字节(8bit为一个字节,一共128位,也就是16个字节)放入矩阵中,设得到的矩阵为P,设得到的子密钥矩阵为K(子密钥最后再看看怎么生成)。
P = [ p 1 p 2 p 3 p 4 p 5 p 6 p 7 p 8 p 9 p 10 p 11 p 12 p 13 p 14 p 15 p 16 ] P = \left[ \begin{matrix} p_{1} & p_{2} & p_{3} & p_{4} \\ p_{5} & p_{6} & p_{7} & p_{8} \\ p_{9} & p_{10} & p_{11} & p_{12} \\ p_{13} & p_{14} & p_{15} & p_{16} \end{matrix} \right] P= p1p5p9p13p2p6p10p14p3p7p11p15p4p8p12p16

K = [ k 1 k 2 k 3 k 4 k 5 k 6 k 7 k 8 k 9 k 10 k 11 k 12 k 13 k 14 k 15 k 16 ] K = \left[ \begin{matrix} k_{1} & k_{2} & k_{3} & k_{4} \\ k_{5} & k_{6} & k_{7} & k_{8} \\ k_{9} & k_{10} & k_{11} & k_{12} \\ k_{13} & k_{14} & k_{15} & k_{16} \end{matrix} \right] K= k1k5k9k13k2k6k10k14k3k7k11k15k4k8k12k16

那么这个操作就是将P中的每一位与K中的每一位进行异或得到一个新的矩阵。
P ⊕ K = [ p 1 ⊕ k 1 p 2 ⊕ k 2 p 3 ⊕ k 3 p 4 ⊕ k 4 p 5 ⊕ k 5 p 6 ⊕ k 6 p 7 ⊕ k 7 p 8 ⊕ k 8 p 9 ⊕ k 9 p 10 ⊕ k 10 p 11 ⊕ k 11 p 12 ⊕ k 12 p 31 ⊕ k 13 p 14 ⊕ k 14 p 15 ⊕ k 15 p 16 ⊕ k 16 ] P \oplus K = \left[ \begin{matrix} p_{1}\oplus k_{1} & p_{2}\oplus k_{2} & p_{3}\oplus k_{3} & p_{4}\oplus k_{4} \\ p_{5}\oplus k_{5} & p_{6}\oplus k_{6} & p_{7}\oplus k_{7} & p_{8}\oplus k_{8} \\ p_{9}\oplus k_{9} & p_{10}\oplus k_{10} & p_{11}\oplus k_{11} & p_{12}\oplus k_{12} \\ p_{31}\oplus k_{13} & p_{14}\oplus k_{14} & p_{15}\oplus k_{15} & p_{16}\oplus k_{16} \end{matrix} \right] PK= p1k1p5k5p9k9p31k13p2k2p6k6p10k10p14k14p3k3p7k7p11k11p15k15p4k4p8k8p12k12p16k16
python代码如下

def sub_bytes(s):
    for i in range(4):
        for j in range(4):
            s[i][j] = s_box[s[i][j]]

字节替换(SubByte)

对于这个操作首先需要引入一个S盒(S box),也就是一个替换表。如下:

0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,

经过轮密钥加后得到的结果在S盒中找到对应的数据进行替换

python代码如下:

def sub_bytes(s):
    for i in range(4):
        for j in range(4):
            s[i][j] = s_box[s[i][j]]

行置换(ShiftRows)

进行扩散处理。防止攻击者找到明文和密文之间的关系。

核心原理就是第一行不变、第二行左移2位,第三行左移3位,第四行左移4位。如下图:
t e s t P = [ p 1 p 2 p 3 p 4 p 5 p 6 p 7 p 8 p 9 p 10 p 11 p 12 p 13 p 14 p 15 p 16 ] ⟶ P ′ = [ p 1 p 2 p 3 p 4 p 6 p 8 p 8 p 5 p 11 p 12 p 9 p 10 p 16 p 13 p 14 p 15 ] testP = \left[ \begin{matrix} p_{1} & p_{2} & p_{3} & p_{4} \\ p_{5} & p_{6} & p_{7} & p_{8} \\ p_{9} & p_{10} & p_{11} & p_{12} \\ p_{13} & p_{14} & p_{15} & p_{16} \end{matrix} \right] \longrightarrow P^{'} = \left[ \begin{matrix} p_{1} & p_{2} & p_{3} & p_{4} \\ p_{6} & p_{8} & p_{8} & p_{5} \\ p_{11} & p_{12} & p_{9} & p_{10} \\ p_{16} & p_{13} & p_{14} & p_{15} \end{matrix} \right] testP= p1p5p9p13p2p6p10p14p3p7p11p15p4p8p12p16 P= p1p6p11p16p2p8p12p13p3p8p9p14p4p5p10p15

python代码如下:

def shift_rows(s):
    s[0][1], s[1][1], s[2][1], s[3][1] = s[1][1], s[2][1], s[3][1], s[0][1]
    s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2]
    s[0][3], s[1][3], s[2][3], s[3][3] = s[3][3], s[0][3], s[1][3], s[2][3]

正常情况下s[0][1] 代表第1行第2个位置的数据,但是我们这里表示第1列第二个位置的数据,例如:
s = [ 1 2 3 2 2 2 ] ⟶ s ′ [ 1 2 3 2 2 2 ] s = \left[ \begin{matrix} 1 & 2 & 3 \\ 2 & 2 & 2 \end{matrix} \right] \longrightarrow s_{'}\left[ \begin{matrix} 1 & 2 \\ 3 & 2 \\ 2 & 2 \end{matrix} \right] s=[122232]s 132222
我们把s变成s’,这样我们通过上面python代码就可以实现行置换了,这样的好处就是可以通过s[i]直接获取某一列。

列混淆(MixColumn)

也是扩散操作

将行置换得到的矩阵和P在 GF(2^8) 做乘法,GF代表伽罗瓦域,其实就是计算的结果模上一个2^8即可。

python代码如下:

# 参考 http://cs.ucsb.edu/~koc/cs178/projects/JT/aes.c
xtime = lambda a: (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1)

def mix_single_column(a):
    # see Sec 4.1.2 in The Design of Rijndael
    t = a[0] ^ a[1] ^ a[2] ^ a[3]
    u = a[0]
    a[0] ^= t ^ xtime(a[0] ^ a[1])
    a[1] ^= t ^ xtime(a[1] ^ a[2])
    a[2] ^= t ^ xtime(a[2] ^ a[3])
    a[3] ^= t ^ xtime(a[3] ^ u)

def mix_columns(s):
    for i in range(4):
        mix_single_column(s[i])
子密钥生成

image-20240119111733320

分析一下子密钥如何生成。

  • 1、左上角为密钥,右上角为生成的子密钥,最下面为异或表。
  • 2、异或表有10列,每一列会生成一次子密钥,所以最后会有10个子密钥,每个子密钥都是一个4*4的矩阵
  • 3、从左上角开始看起,先取密钥的第1列,然后将第4进行移位(Shift)然后字节替换(SubBytes),将得到的结果和第一列异或。
  • 4、将第三步得到的结果和异或表中的第一列异或得到子密钥的第一列。
  • 5、将子密钥第1列和密钥第2列异或得到子密钥第2列。
  • 6、将子密钥第2列和密钥第3列异或得到子密钥第3列。
  • 7、将子密钥第3列和密钥第4列异或得到子密钥第4列。

而对于192或是256位密钥会稍微又些不一样,但大致流程相同,这里我们以128位的密钥为例

python脚本如下:

r_con = (0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,0x80, 0x1B, 0x36)

def xor_bytes(a, b):
    return bytes(i^j for i, j in zip(a, b))

def _expand_key(s):
    for i in range(10):
        word = list(s[-1])  # 取得最后一列
        word.append(word.pop(0)) # 将首位移动到最后
        word = [s_box[b] for b in word]  # SubBytes操作
        word[0] ^= r_con[i]  # 和异或表内数据异或

        s.append(xor_bytes(word, s[-4]))  # 得到新的子密钥
        s.append(xor_bytes(s[-1], s[-4]))  # 因为直接在s中添加,所以本该和上一轮第二列异或的位置还是-4
        s.append(xor_bytes(s[-1], s[-4]))
        s.append(xor_bytes(s[-1], s[-4]))

    return [s[4*i : 4*(i+1)] for i in range(len(s) // 4)]

导入密码学库加密脚本如下:
from Crypto.Cipher import AES
aes = AES.new(b'1234567812345678', AES.MODE_ECB)
print(aes.encrypt(b'1234567812345678'))  # b'm\xac\x1cV\xe7G\xfa\xe0:\xcf\x8ch\x91\xe4(\xe0'

加密脚本如下:
#  轮密钥加(AddRoundKey)
def add_round_key(s, k):
    for i in range(4):
        for j in range(4):
            s[i][j] ^= k[i][j]


s_box = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]

# 字节替换(SubByte)
def sub_bytes(s):
    for i in range(4):
        for j in range(4):
            s[i][j] = s_box[s[i][j]]

# 行置换(ShiftRows)
def shift_rows(s):
    s[0][1], s[1][1], s[2][1], s[3][1] = s[1][1], s[2][1], s[3][1], s[0][1]
    s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2]
    s[0][3], s[1][3], s[2][3], s[3][3] = s[3][3], s[0][3], s[1][3], s[2][3]


# 列混淆(MixColumn)
# 参考 http://cs.ucsb.edu/~koc/cs178/projects/JT/aes.c
xtime = lambda a: (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1)

def mix_single_column(a):
    # see Sec 4.1.2 in The Design of Rijndael
    t = a[0] ^ a[1] ^ a[2] ^ a[3]
    u = a[0]
    a[0] ^= t ^ xtime(a[0] ^ a[1])
    a[1] ^= t ^ xtime(a[1] ^ a[2])
    a[2] ^= t ^ xtime(a[2] ^ a[3])
    a[3] ^= t ^ xtime(a[3] ^ u)

def mix_columns(s):
    for i in range(4):
        mix_single_column(s[i])

# 子密钥生成
r_con = (0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,0x80, 0x1B, 0x36)

def xor_bytes(a, b):
    return bytes(i^j for i, j in zip(a, b))

def _expand_key(s):
    for i in range(10):
        word = list(s[-1])  # 取得最后一列
        word.append(word.pop(0)) # 将首位移动到最后
        word = [s_box[b] for b in word]  # SubBytes操作
        word[0] ^= r_con[i]  # 和异或表内数据异或

        s.append(xor_bytes(word, s[-4]))  # 得到新的子密钥
        s.append(xor_bytes(s[-1], s[-4]))  # 因为直接在s中添加,所以本该和上一轮第二列异或的位置还是-4
        s.append(xor_bytes(s[-1], s[-4]))
        s.append(xor_bytes(s[-1], s[-4]))

    return [s[4*i : 4*(i+1)] for i in range(len(s) // 4)]

# 字节转矩阵
def bytes2matrix(text):
    """ Converts a 16-byte array into a 4x4 matrix.  """
    return [list(text[i:i+4]) for i in range(0, len(text), 4)]

# Aes加密

key = b'1234567812345678'
plain = b'1234567812345678'


skeys = _expand_key(bytes2matrix(key))
plain = bytes2matrix(plain)
add_round_key(plain, skeys[0])

for i in range(1,10):
    sub_bytes(plain)
    shift_rows(plain)
    mix_columns(plain)
    add_round_key(plain,skeys[i])

sub_bytes(plain)
shift_rows(plain)
add_round_key(plain,skeys[-1])
enc = bytes(plain[0]+plain[1]+plain[2]+plain[3])
print(enc)

解密脚本如下:
## AES和DES解密不一样,需要找到逆函数,所以这里的有些逆函数我直接抄wp上的。

#  轮密钥加(AddRoundKey)
def add_round_key(s, k):
    for i in range(4):
        for j in range(4):
            s[i][j] ^= k[i][j]

s_box = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]

# 行置换的逆操作
def inv_shift_rows(s):
    s[0][1], s[1][1], s[2][1], s[3][1] = s[3][1], s[0][1], s[1][1], s[2][1]
    s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2]
    s[0][3], s[1][3], s[2][3], s[3][3] = s[1][3], s[2][3], s[3][3], s[0][3]

inv_s_box = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]
def inv_sub_bytes(s):
    for i in range(4):
        for j in range(4):
            s[i][j] = inv_s_box.index(s[i][j])

# 子密钥生成
r_con = (0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,0x80, 0x1B, 0x36)

def xor_bytes(a, b):
    return bytes(i^j for i, j in zip(a, b))



def _expand_key(s):
    for i in range(10):
        word = list(s[-1])  # 取得最后一列
        word.append(word.pop(0)) # 将首位移动到最后
        word = [s_box[b] for b in word]  # SubBytes操作
        word[0] ^= r_con[i]  # 和异或表内数据异或

        s.append(xor_bytes(word, s[-4]))  # 得到新的子密钥
        s.append(xor_bytes(s[-1], s[-4]))  # 因为直接在s中添加,所以本该和上一轮第二列异或的位置还是-4
        s.append(xor_bytes(s[-1], s[-4]))
        s.append(xor_bytes(s[-1], s[-4]))

    return [s[4*i : 4*(i+1)] for i in range(len(s) // 4)]

xtime = lambda a: (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1)


def mix_single_column(a):
    # see Sec 4.1.2 in The Design of Rijndael
    t = a[0] ^ a[1] ^ a[2] ^ a[3]
    u = a[0]
    a[0] ^= t ^ xtime(a[0] ^ a[1])
    a[1] ^= t ^ xtime(a[1] ^ a[2])
    a[2] ^= t ^ xtime(a[2] ^ a[3])
    a[3] ^= t ^ xtime(a[3] ^ u)

def mix_columns(s):
    for i in range(4):
        mix_single_column(s[i])
def inv_mix_columns(s):
    # see Sec 4.1.3 in The Design of Rijndael
    for i in range(4):
        u = xtime(xtime(s[i][0] ^ s[i][2]))
        v = xtime(xtime(s[i][1] ^ s[i][3]))
        s[i][0] ^= u
        s[i][1] ^= v
        s[i][2] ^= u
        s[i][3] ^= v

    mix_columns(s)

def bytes2matrix(text):
    """ Converts a 16-byte array into a 4x4 matrix.  """
    return [list(text[i:i+4]) for i in range(0, len(text), 4)]
# 由于AES解密就是对加密的逆过程。那么第一步先将密文转化为矩阵
c  = b'm\xac\x1cV\xe7G\xfa\xe0:\xcf\x8ch\x91\xe4(\xe0'
key = b'1234567812345678'
c = bytes2matrix(c)
# 然后 轮密钥加的逆操作。轮密钥加的操作是将P和K的每一项进行异或,可以我们找到K,在进行一次轮密钥加即可得到源数据。所以我们这里直接引入轮密钥加方法。放在前面函数区。
# 生成子密钥
skeys = _expand_key(bytes2matrix(key))
# 进行最后一轮的轮密钥加逆操作
add_round_key(c,skeys[-1])

# 接着是行置换的逆操作,行置换目的是起到扩散作用的,将矩阵的位置做了一个移动,那么我们找到一个函数可以移动回去的即可。
inv_shift_rows(c)
# 接着是字节替换的逆操作,那么就是找到其逆表进行替换即可。
inv_sub_bytes(c)
for i in range(9,0,-1):
    add_round_key(c,skeys[i])
    # 列混淆的逆操作
    inv_mix_columns(c)
    # print(c)
    inv_shift_rows(c)
    inv_sub_bytes(c)
add_round_key(c, skeys[0])
m = bytes(c[0]+c[1]+c[2]+c[3])
print(m)
参考

https://www.nssctf.cn/(探索工坊)

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值