- DES发明人
美国IBM公司W. Tuchman 和 C. Meyer1971-1972年研制成功。
IP初始置换与逆置换
-
在DES中使用的都是发明者固定的表进行置换与逆置换,这里不知道为啥没人说是固定的,因为学习过程中没有这个提示真的很容易搞混(当然这对我来说是这样),如图所示
-
上图中可以发现初始置换的表与逆置换的表对应着,比如IP初始中第一个数字为58(这里是初始置换后的数字) ,然后我们还原的时候就要去逆置换中找第58个(这里找的是第58个而不是58这个数字) ,因此在逆置换表中第58个的数字就是写着1,所以这两个表是对应着的。
-
说实在点的就是初始置换中给的都是下标,然后初始置换的时候按照给出的数字下标在原数据中找到对应下标,然后放进表中即可。(比如58,那就在原数据中找到下标为58的放在第一位以此类推,逆置换也如此操作)
-
注意事项:
- 一定不要忘记的就是IP置换与逆置换的表都不用我们弄,是人家弄好了按照这个来就行了,我们不用自己去特定生成两个表来弄。
- 置换必须要转化为二进制数
- 置换中二进制数必须每一次置换都为64位,64位进64位出。
编程想法
转二进制过程中的提取一些数据
全为字符串形式
-
声明:我这里的代码块全部导出自我写的类中的函数,因此会带有一些类的变量。
-
首先将数据全部转化为二进制数,我首先就很简单将其去哪不使用bin转为二进制数据,然后将其去掉ob前缀之后再使用join转为字符串最后全部添加进一个字符串变量里,并且join之前用了一个空格将其隔开(因为待会分组记录下标的时候要用空格进行推理,下一个数据对应的下标就是空格的下标减去前面空格出现的次数就是解码的时候要用到的下标)
-
为啥要减去前面空格出现的次数,因为空格出现的次数就是我添加下标的次数,然后解码的时候是不用空格的,是用空格spilt掉然后空格就没有了,所以在这里添加的下标应该是预测到后面解码要用到的下标。(这是一段解释)
-
添加好下标之后就要将其组合了,因为我们的空格已经发挥了他的作用了,所以就将空格全部去掉,然后将其所有二进制数据组合成一串连续的二进制数字,这也是我们要将其下标记录的原因,我们一旦将其组合成串二进制数字的时候,如果直接转回字符就转不回来了,因为我们需要的不是一串而是分开的一段一段的字母或者中文等等。
-
去掉空格之后就是一串01数字了,由于我们IP置换需要满足64的,所以我们要确保这一堆二进制数字长度是64的倍数到时候才好分组,如果不够64位的就在后面补0将其补齐64位
这里不用担心补0在逆置换还原回来的时候会出错,虽然补0是补在后面但是我们是有记录下标的,所以我们只要使用下标还原回数据的时候后面多余的去掉即可def enByte(self, s): str_bin = [] tmp = [] s = str(s) index_list = [] temp_index = 0 for c in s: t = bin(ord(c)).replace('0b', '') temp_index += (len(t) - 1) # index_list.append(temp_index) # 存储每一个数据的下标,还原数据的时候需要 tmp.append(t) # 将0b前缀去掉 str_bin = ' '.join(tmp) for i in range(len(str_bin)): if i == 0: index_list.append(0) continue if str_bin[i - 1] == ' ' and str_bin[i] != ' ': '''空格在这里的作用就是记录下标然后 len(index_list)计算出一共添加了几次下标 就是一共有几个空格了, 因此在这里可以直接推出该数据开始的下标''' index_list.append(i - len(index_list)) lastTail = 0 str_bin = str_bin.replace(' ', '') str_len = len(str_bin) index_list.append(str_len) for i in range(64 - ((len(str_bin) % 64))): # 补0 lastTail += 1 str_bin += '0' self.lastTail = lastTail self.length = len(str_bin) return str_bin, index_list
64为一组
转二进制过程中提取了一些有用的数据之后,现在就是需要将这一坨64倍数的二进制数据分组了,每组64,在这里不用担心是否满足64倍数,因为在转二进制的时候已经补0了(现在我觉得还是把补0操作放到分组这个函数里面比较好,因为补0比较适合分组的这个功能,转二进制的时候就干干净净的进行二进制转换就好了,果然我还是太年轻了)
- 遍历数据,64为一组即可(模64存一组数据)
def groupBy64(self, data): temp = '' mess = [] for i in range(len(data)): temp += data[i] if (i + 1) % 64 == 0 and i != 0: mess.append(temp) temp = '' return mess
IP置换
当上述工作完成之后就轻松很多了。我当时也松了一口气,没想到DES这么一步花了我这么多心思。
-
IP置换函数
很明显这里需要使用固定的IP初始置换表,因为规定的就是这样(不要自己弄一个表出来,因为在DES中其他表没有意义。)def IP(self, data): ip = [ # left 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, # right 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置换表 data_ = [] for i in ip: data_.append(str(data[i - 1])) time = 1 self.message += ('\nIP置换:\n') for i in data_: self.message += (i + ',') if time % 8 == 0: self.message += '\n' time += 1 data_ = ''.join(data_) return data_
-
下面代码是使用了上面做的工作之后,直接调用的函数.
- 说明
里面有一段这样的代码,这里是为了我打印出来方便对比的,这不就是纯属打印出来给我看的测试代码,可以不写,也可以对应到其他功能:比如我这个就是哟啊给到pyqt5界面的数据打印出来。
temp_byte = '' for i in range(1, len(self.index_list)): temp_byte += bin_byte[self.index_list[i - 1]:self.index_list[i]] + ' ' else: temp_byte += bin_byte[self.index_list[i]:] + ' '
- 说明
-
这里是最终IP置换函数
def goto_IP(self): str_mess = self.input_text.toPlainText() #接收输入的文本 #这里采用了一个记录下标的方法,因为我们是一次性置换然后再逆置换,记录下标方便使用 data, self.index_list = self.enByte(str_mess) data_list = self.groupBy64(data) # .replace(' ','') bin_byte = '' for m in data_list: bin_byte += self.IP(m) temp_byte = '' for i in range(1, len(self.index_list)): temp_byte += bin_byte[self.index_list[i - 1]:self.index_list[i]] + ' ' else: temp_byte += bin_byte[self.index_list[i]:] + ' ' self.end_byte = temp_byte result = '=============IP置换===========\n' + self.message + \ '\n\n=====IP置换后的二进制数据======\n' + temp_byte return result
IP逆置换
-
解码函数
里面有一个try catch,原因是因为我在解码的时候发现IP初始置换后置换出的二进制数据是一些奇奇怪怪的字符或者说根本不在utf-8字符集中,因此如果含有不在utd-8字符集中就会报错,但是由于我这个是给IP初始置换后将其置换的二进制数据转为字符的,因此需要做这么一步操作,如果是IP逆置换回原本的数据就不用try catch这一步,但是因为我要代码复用这个函数所以有好过没有。 -
解码必须要用到的就是下标,因此刚刚存全局的下标变量列表就起到作用了,在这里用下标将其一坨二进制数据切分成对应原本二进制字符串的数据才能进行转一个个的ASCII码值然后才能由正确的ASCII转为原字符数据
def deByte(self, mess, index): data = '' byte_s = [] for i in range(1, len(index)): s = '' s = mess[index[i - 1]:index[i]] byte_s.append(s) # 开始解码 Error_Mess = '' for bs in byte_s: try: s = chr(int(bs, 2)) data += s except: Error_Mess = 'utf-8没有尾部补0的编码映射报错,不建议直接使用字符进一步加密' continue """使用该方式将过于长的补0尾部去掉,因为0不影响, 所以即使打印出来也不会有影响, 在这里只能采用这种方式进行优化, 否则utf-8没有尾部补0的编码映射会报错""" return data, Error_Mess
-
IP逆置换
依旧是DES设计者给出的固定置换表,不能更改。def IP_1(self, data): # 最后一步IP逆置换 ip_1 = [ 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 ] re_data = '' for i in ip_1: re_data += data[i - 1] return re_data
-
最后汇总IP逆置调用的函数依旧是上述准备工作中弄好的函数
def backto_IP(self): data = self.end_byte data = data.replace(' ', '') data_list = self.groupBy64(data) # .replace(' ','') bin_byte = '' for m in data_list: bin_byte += self.IP_1(m) str_mess ,Error_Mess = self.deByte(bin_byte,self.index_list) return str_mess
不知道各位道友是否觉得这个IP置换有种让人非常不舒服感觉。