DES(Data Encryption Standard,数据加密标准)是一种对称加密算法。
算法概述
- 加密类型:对称加密(同一密钥用于加密和解密)。
- 密钥长度:64位(8字节),其中有效密钥长度为56位,另外8位用于奇偶校验。
- 数据块大小:64位(8字节),即DES将数据按64位的块进行加密。
DES加密过程
DES的加密过程主要包括以下几个步骤:初始置换(IP)、16轮Feistel结构加密、逆初始置换(IP⁻¹)。
明文处理
每次加密时,DES会将数据划分为64位的块进行处理。
- 如果需要加密的数据超过64位,DES会将数据分成多个64位的块,然后逐块加密。
- 如果数据长度不是64位的整数倍,则需要使用填充(padding)技术,使最后一块达到64位。
因此,DES本身只能处理64位的数据块,但通过分块和填充,可以用于加密任意长度的数据。
填充方式
- PKCS#5/PKCS#7 填充
适用范围:最常用的填充方式,适用于各种分组加密算法,包括DES。
规则:根据需要填充的字节数n,将每个填充字节都设置为n。- 例如,如果需要填充4个字节,每个填充字节为0x04。
如果数据长度已经是块大小的整数倍,则在块末尾填充一个完整的块,填充值为块大小。 - 示例:
数据:“ABC”(3字节)
填充后:“ABC\x05\x05\x05\x05\x05”(8字节)
数据:“ABCDEFGH”(8字节)
填充后:“ABCDEFGH\x08\x08\x08\x08\x08\x08\x08\x08”(16字节)
- 例如,如果需要填充4个字节,每个填充字节为0x04。
- Zero Padding(零填充)
适用范围:对一些特定数据可以使用,但不适用于所有情况,特别是数据中有可能包含0x00的情况。- 规则:使用0x00字节进行填充,直到数据达到64位的整数倍。如果原数据末尾恰好是0x00,则无法判断填充与数据的边界。
- 示例:
数据:“ABC”(3字节)
填充后:“ABC\x00\x00\x00\x00\x00”(8字节)
初始置换(IP)
输入和输出
- 输入:64位的数据块(例如,明文在加密时,密文在解密时)。
- 输出:经过置换后的64位数据块。
置换规则
初始置换根据一个固定的置换表进行,这个表规定了每一位输入数据的位置在输出数据中的新位置。具体来说,置换表会定义64位输入中的每一位如何重新排列,排列规则是固定的。
置换表
初始置换表
- 每个数字表示原始数据中位的位置。例如,第1个位置的位(最左上角的“58”)表示原始数据的第58位,现在被放置在置换后的第1位。
- 原始数据的第2位由第50位的输入组成,依此类推。
16轮Feistel结构加密
&emps; Feistel结构是一种常见的对称加密构造方法,其基本原理是将数据分为两部分(左部分和右部分),通过多轮相似的操作,每一轮使用一个子密钥进行数据的替换和交换。最终的结果经过逆操作就能还原到原始数据。
数据分割
&emps; 在进入16轮Feistel结构之前,经过初始置换(IP)的64位数据被分成两部分:左半部分 (L0) 和 右半部分 (R0),每部分为32位。
每一轮都会输出新的左部分和右部分,记为 Ln和 Rn,其中n 是轮次编号(从1到16)。
每一轮的操作
每一轮使用一个Feistel函数F和一个子密钥Kn ,将输入的右半部分 与子密钥结合,生成一个新的右半部分。
- 扩展置换
32位的 Rn 通过一个固定的扩展置换表变成48位的数据。 - 与子密钥异或(XOR)
扩展后的48位数据与当前轮的48位子密钥Kn进行异或运算。 - S盒替换
将异或后的48位数据分成8组,每组6位(48位 ÷ 8 = 6位)。
每一组6位数据通过一个固定的S盒(Substitution Box)转换为4位数据。DES总共有8个不同的S盒。
每个S盒将6位输入映射为4位输出,从而将48位数据压缩回32位。 - P置换(P-Permutation)
将32位的S盒输出通过P置换表进行重新排列,得到一个的32位输出。
- Feistel函数的输出
Feistel函数的输出是一个32位的结果,它与当前轮的左部分Ln进行异或,得到新的右部分 Rn+1。
数据交换
经过F函数处理之后,左右两部分交换位置:即Ln+1=Rn,Rn+1=Ln^F(Rn,Kn)
重复16轮
重复16轮得到最后的L16和R16
逆初始置换(IP⁻¹)
逆初始置换是初始置换的逆操作,用于在解密的最后一步将重新排列的数据恢复到原始顺序。它也有一个对应的置换表,IP⁻¹表如下:
这个表定义了如何将经过初始置换的数据重新排列回原来的顺序。
在第16轮完成后,不再进行左右交换,而是将R16和L16直接合并成64位的数据,然后通过逆初始置换得到最终64位的密文。
Python 实现
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad,unpad
import binascii
key = b'12345678'
cipher = DES.new(key,DES.MODE_ECB)
data = "Bileton"
padded_data = pad(data.encode("utf-8"),DES.block_size)
encrypted_data = cipher.encrypt(padded_data)
print(binascii.hexlify(encrypted_data).decode('utf-8'))
>>> 66b99748acc81443
加上iv向量
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad,unpad
import binascii
key = b'12345678'
iv = b'12345678'
cipher = DES.new(key,DES.MODE_CBC,iv)
data = "Bileton"
padded_data = pad(data.encode("utf-8"),DES.block_size)
encrypted_data = cipher.encrypt(padded_data)
print(binascii.hexlify(encrypted_data).decode('utf-8'))
>>> fa8669e459a42d7f
Java实现
Cipher cipher = Cipher.getInstance("DES");
byte[] keys = "12345678".getBytes("UTF-8");
Key key = new SecretKeySpec(keys, "DES");
cipher.init(Cipher.ENCRYPT_MODE,key);
String data = "Bileton";
cipher.update(data.getBytes("UTF-8"));
byte[] encryptData = cipher.doFinal();
String encodedEncryptedData = Base64.getEncoder().encodeToString(encryptData);
System.out.println(encodedEncryptedData);
>>> ZrmXSKzIFEM=
加上iv向量
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
byte[] iv = "12345678".getBytes("UTF-8");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
byte[] keys = "12345678".getBytes("UTF-8");
Key key = new SecretKeySpec(keys, "DES");
cipher.init(Cipher.ENCRYPT_MODE,key,ivParameterSpec);
String data = "Bileton";
cipher.update(data.getBytes("UTF-8"));
byte[] encryptData = cipher.doFinal();
String encodedEncryptedData = Base64.getEncoder().encodeToString(encryptData);
System.out.println(encodedEncryptedData);
>>> +oZp5FmkLX8=
Android Studio
String data = "Bileton";
String key_string = "12345678";
Key key =new SecretKeySpec(key_string.getBytes("UTF-8"),"DES");
String iv_string = "12345678";
IvParameterSpec iv = new IvParameterSpec(iv_string.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,key,iv);
cipher.update(data.getBytes("UTF-8"));
byte[] result = cipher.doFinal();
String encodeencrypteddata = Base64.encodeToString(result,Base64.DEFAULT);
Toast.makeText(MainActivity.this,encodeencrypteddata,Toast.LENGTH_SHORT).show();