参考资料:
DES结构实现
各阶段函数结果验证


具体详细说明可以先参看参考资料,后面再补充详细的说明。
代码实现:

# 采用ECB方式加密,填充方式采用zeropadding
# 明文输入需要是ASCII在 0-255之间的字符,中文输入暂时不支持
# 密钥需要是8字节字符串
# IP置换表
IP_table=[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
]

# 扩展置换表
extend_table=[32,  1,  2,  3,  4,  5,
  4,  5,  6,  7,  8,  9,
  8,  9, 10, 11, 12, 13,
  12, 13, 14, 15, 16, 17,
  16, 17, 18, 19, 20, 21,
  20, 21, 22, 23, 24, 25,
  24, 25, 26, 27, 28, 29,
  28, 29, 30, 31, 32,1
]

# S盒中的S1盒
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]
]
# S盒中的S2盒
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]
]
# S盒中的S3盒
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]
]
# S盒中的S4盒
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]
]
# S盒中的S5盒
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]
]
# S盒中的S6盒
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]
]
# S盒中的S7盒
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]
]
# S盒中的S8盒
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]
]
S = [S1, S2, S3, S4, S5, S6, S7, S8]

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

# 压缩置换表1,不考虑每字节的第8位,将64位密钥减至56位。然后进行一次密钥置换。
PC_1=[ 57, 49, 41, 33, 25, 17,  9,
  1, 58, 50, 42, 34, 26, 18,
  10,  2, 59, 51, 43, 35, 27,
  19, 11,  3, 60, 52, 44, 36,
  63, 55, 47, 39, 31, 23, 15,
  7, 62, 54, 46, 38, 30, 22,
  14,  6, 61, 53, 45, 37, 29,
  21, 13,  5, 28, 20, 12,  4
]

# 压缩置换表2,用于将循环左移和右移后的56bit密钥压缩为48bit。
PC_2=[14, 17, 11, 24,  1,  5,
3, 28, 15,  6, 21, 10,
  23, 19, 12,  4, 26,  8,
  16,  7, 27, 20, 13,  2,
  41, 52, 31, 37, 47, 55,
  30, 40, 51, 45, 33, 48,
  44, 49, 39, 56, 34, 53,
  46, 42, 50, 36, 29, 32
]
# 逆置换IP表
_IP_table=[40,  8, 48, 16, 56, 24, 64, 32,
  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
]

# 循环移位位数表
moveNum=[1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]


# IP置换函数
def IP(message):
    result = ''
    # 用IP表进行置换即可
    for i in IP_table:
        result += message[i-1]
    return result


# 扩展置换函数
def extend(R0):
    result=''
    # 用扩展置换表进行置换即可
    for i in extend_table:
        result += R0[i-1]
    return result


# S盒子压缩函数
def sbox(afterRoundKey):
    result = ''
    for m in range(0,48,6):
        # 通过第1位和第6位算出列号
        i = int(afterRoundKey[m])*2+int(afterRoundKey[m+5])
        # 通过第2、3、4、5位算出行号
        j = int(afterRoundKey[m+1])*8+int(afterRoundKey[m+2])*4+int(afterRoundKey[m+3])*2+int(afterRoundKey[m+4])
        result += str('{:04b}'.format(S[m//6][i][j]))
    return result


# 轮密钥加函数
def addRoundKey(afterExtend,roundKey):
    result = ''
    for i in range(48):
        # 进行按位异或运算
        result += str(int(afterExtend[i])^int(roundKey[i]))
    return result


# 线性置换P盒
def pbox(afterSbox):
    result = ''
    # 用P盒表进行置换即可
    for i in range(32):
        result += str(afterSbox[P_table[i]-1])
    return result


# F函数
def F(R0,key):
    # 扩展将32位扩展为48位
    R0 = extend(R0)
    # 与轮密钥进行按位异或
    afterRoundKey = addRoundKey(R0,key)
    # 放入S盒压缩
    afterSbox = sbox(afterRoundKey)
    # 放入P盒进行线性替换
    result = pbox(afterSbox)
    return result


#轮密钥生成
def generateKey(key):
    result = ''
    roundKey=[]
    # 进行第一次压缩置换,将64位密钥减至56位,即去除8个校验位
    for i in PC_1:
        result += key[i-1]
    # 分位左右两部分,每部分28位
    C0=result[:28]
    D0=result[28:]
    for i in range(16):
        # 按循环移位位数表进行移位操作
        C0=C0[moveNum[i]:]+C0[:moveNum[i]]
        D0=D0[moveNum[i]:]+D0[:moveNum[i]]
        tmp=C0+D0
        ret = ''
        # 进行第二次压缩将56位压缩减至48位
        for i in PC_2:
            ret+=tmp[i-1]
        # 生成轮密钥存储到列表中
        roundKey.append(ret)
    return roundKey


# IP逆置换
def inverseIP(afterF):
    ciphertext=''
    # 用逆IP表进行置换即可
    for i in _IP_table:
        ciphertext+=afterF[i-1]
    return ciphertext


# 加密函数
def encrypt(plaintext,roundKey):
    # 进行IP置换
    result = IP(plaintext)
    # 分左右32bit
    L0=result[:32]
    R0=result[32:]
    # 进行16轮加密
    for i in range(16):
        tmp=R0
        results = F(R0, roundKey[i])
        # print(results)
        # print(roundKey[0])
        R0=''
        for j in range(32):
            R0+=str(int(L0[j])^int(results[j]))
        L0=tmp
        # print(R0)
    # 最后一轮结束后结果要左右交换
    afterF = R0+L0
    # 进行逆IP置换得到密钥
    ciphertext=inverseIP(afterF)
    return ciphertext


# 解密函数
def decrypt(ciphertext, roundKey):
    result = IP(ciphertext)
    Li = result[:32]
    Ri = result[32:]
    # 解密函数对轮密钥使用要倒序使用
    for i in range(15,-1,-1):
        tmp=Ri
        results = F(Ri,roundKey[i])
        Ri = ''
        for j in range(32):
            Ri += str(int(Li[j])^int(results[j]))
        Li = tmp
    afterF = Ri+Li
    plaintext = inverseIP(afterF)
    return plaintext

# 0填充函数
def zeroPadding(message):
    # UTF-8解码
    message = list(bytes(message,'utf8'))
    # 先填充一个0,这使得如果明文本来就是64位仍然要填充64个0到末尾
    message.append(0)
    # 填充0到8字节的整数倍
    while len(message)%8!=0:
        message.append(0)
    ret = []
    strs=''
    # 分组,化为二进制串,然后没8字节分组放入ret列表中
    for i in range(len(message)):
        strs+=str('{:08b}'.format(message[i]))
        if (i+1)%8==0:
            ret.append(strs)
            strs=''
    return ret

# 处理密文函数
def progress(cipher):
    lens=len(cipher)
    # 因为密文输入是16进制,所以直接按没16个字符分一组即可,最后会被解释成16*4=64bit
    m=lens//16
    ret=[]
    for i in range(m):
        ret.append(cipher[i*16:(i+1)*16])
    return ret

# 将16进制位串变为字符
def to_chr(text):
    lens = len(text)
    # 每两个个16进制数被解释成一个ASCII码字符,2**8=256
    m = lens // 2
    lists = []
    for i in range(m):
        lists.append(text[i * 2:(i + 1) * 2])
    strs = ''
    for s in lists:
        tmp=int(s, 16)
        # 因为ascii码0不会被输入,因此认为是补位值,当存在补位值,后继必为补位值,直接舍弃即可
        # 对中文进行加解密时这里需要优化
        if tmp==0:
            break
        strs += chr(tmp)
    return strs



if __name__ == "__main__":
    print("请选择1、加密,2、解密:")
    choose = input()
    if choose == '1':
        message = input("请输入要加密的文本:")
        key = input("请输入密钥:")
        keylist = list(bytes(key, 'utf8')) # 将密钥变为ascii码
        if len(keylist)!=8:# 密钥应该为8字节的整数倍
            print("错误!请输入8字节密钥!")
        else:
            key=''
            for i in keylist:
                key += str('{:08b}'.format(i)) # 将密钥的ascii码转为2进制
            roundKey = generateKey(key)# 生成轮密钥,用列表存储
            lists=zeroPadding(message) # 进行明文0填充,并分组,每64位一组
            cipher=''
            for p in lists:
                cipher+=encrypt(p, roundKey) # 每组分别加密,因为采用ECB方式,直接将各段加密结果链接即可
            cipher=str(hex(int(cipher,2)))[2:] # 将结果2进制串转化为16进制串输出
            print(cipher)
    elif choose == '2':
        text = input("请输入密文文本:")
        key = input("请输入密钥:")
        keylist = list(bytes(key, 'utf8'))
        if len(keylist)!=8:
            print("错误!请输入8字节密钥!")
        else:
            key = ''
            for i in keylist:
                key += str('{:08b}'.format(i))
            roundKey = generateKey(key)
            cipherlist=progress(text)
            plaintext=''
            for c in cipherlist:
                message = str('{:064b}'.format(int(c, 16)))
                tmp=decrypt(message, roundKey)
                if c==cipherlist[-1] and  int(tmp,2)==0: # 如果当前段位最后一段且当前段值为0,认为是补位段,直接舍弃
                    break
                plaintext+=tmp
            plaintext=str(hex(int(plaintext,2)))[2:]
            plaintext = to_chr(plaintext) # 将解密结果化为字符输出
            print(plaintext)
    else:
        print("错误输入!")

加解密结果经过在线加密验证正确:
加密:
在这里插入图片描述
验证结果:
在这里插入图片描述

解密:
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐