对称加密,是指使用单一密匙来进行加密和解密。目前较常见的对称加密有DES,3DES, AES等。原理类似,加密方式不同。下面以DES为例,简单的总结一下对称分组加密算法的原理及过程。
DES(Data Encryption Standard)加密算法是对称加密的一种,诞生年代较早,由IBM开发,1977年被美国政府采纳为数据加密标准。DES最初形式已不再安全,目前使用的多是其改良后的3DES等。
DES 原理简介
DES 是分组加密的一种,以64bit长度对数据进行分组加密(就像上一篇Base64,其第一步是以6bit为分组),加密的算法完全公开,所以数据的保密性完全依赖于密匙。密匙是一个64bit的数,每八位都用作奇偶校验位,实际使用到的只有56bit。
DES的算法特点是混乱和扩散的组合,加密过程基于密匙作用于明文,DES算法中会经历16轮这样的操作。
64bit的数据经过初始置换后分为了左右两部分(L₀和R₀, 也就是前32bit和后32bit),与密匙结合进行了16轮完全相同的运算(函数f)。最后再进行一次末置换(与初始置换相反)就可以得到加密后的密文了。
每一轮的运算过程如下图:
密匙部分:
密匙经过“密匙置换表” 得到一个56位的数据,将密匙分为左右两部分,再根据“每轮移动的位数表”,左右两部分循环左移1位或2位,最后根据“压缩置换表”从56位中提取出48位用于本轮函数f的密匙。
左侧明文加密部分:
左右两部分(L₀、R₀)经历了不同处理,R₀部分经过“扩展置换表”从32bit扩展为48bit,然后通过异或运算与密匙结合,结合的结果再通过8个S盒将这48bit替换成新的32bit,将S盒的结果经过P盒置换后的与左半部分L₀通过异或运算结合得到新的右半部分R₁,而原本的右半部分R₀成为了新的左半部分L₁
具体步骤
- 数据的初始置换
数据需要进行初始变换,将64bit的数据根据初始置换表(如下表)直接置换,下表中的数值代表数据原始位置,例如将原本第58位的数据放到第1位来,第50位的数据放到第2位,以此表位参照置换所有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 |
左右两部分就是前32bit,和后32bit,体现在置换表上就是:
L: 58, 50, 42......24, 15, 8
R: 57, 49, 42......23, 15, 7
- 密匙的置换
64bit的密匙,需要经过下列置换表进行置换,可以发现置换表没有第8,16,24,32,40,48,56位,因为密匙的每8位用于奇偶校验,经过置换表置换后的数据只有56bit,省去了校验位。
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 |
每轮在进行加密时,密匙都会被分为左右两部分,各28bit。每轮都会根据轮数移动规定的位数(如下表:)
轮 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
位数 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 |
轮 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
位数 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 1 |
【插一句嘴:将所有位数加起来是多少?后面解密时会提到。】
移动后,从56bit密匙中根据压缩置换表(如下表)选出48bit。由于对密匙进行了“移动”和“置换”,在密匙非弱密匙的情况下能够保证每次取出的密匙都不相同。
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 |
3.数据的扩展置换
我们从加密原理上得知,数据被分为了左右两部分L和R,而R在加密前会先经过一次扩展置换(E盒)扩展至与密匙长度相同的48bit。
32bit如何扩展至48bit呢?通过“扩展置换表”(如下表)以重复某些位的方式扩展至48bit。
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 |
- 将通过扩展置换后的密匙与数据R进行异或运算。
- S盒处理
将第4步的结果使用S盒处理。S盒共有8个,且各不相同。第4步异或处理后的48bit数据,每6bit为一条分别被对应的S盒处理。每个S盒都是6bit输入,4bit输出。也就是说通过8个S盒处理后,数据长度将缩短为32bit。而S盒的内部处理与之前的换位不同。我们用S1盒作为例子:
假设S盒如下:
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盒是一个4行16列的表(这里要注意,计数从0开始,行是0-3,列是0-15),我们假设6bit的输入为“010101”, 第一位和第六位组成一个二位的数“01”,其对应着第1行,从第二位到第五位构成了一个四位的书“1010”,其对应着第10列。这样我们就得到了该S盒中第1行第10列的“12”,最后,S₁盒输出为“1100”(12的二进制)。
在很多DES加密的变种中,对S盒有所修改。
- P盒置换
将经过S盒后的32bit数据用P盒(如下表)进行置换,可以看到,并没有任何重复的位数,仅是单纯的置换。
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 |
- 通过异或运算得到新的R部分
将P盒置换的结果与此轮最初分组的左半部分进行异或运算得到新的右半部分R,而原本的右半部分直接成为新的左半部分。
到这里一轮运算就结束了
- 末置换
在经过16轮的操作后,需要进行一个末置换,其意义在于可以使该算法技能用作加密,也能用作解密。此时的左右部分并未交换,只是将R16和L16并在一起形成的一个分组。末置换就是将这个分组作为输入的。末置换表如下:
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 |
经过上面的步骤就得到了DES加密后的最终密文。
总结:
我们由整个加密过程可以知道,DES加密的过程透明,数据的隐秘性完全取决于密匙。密匙的长度为64bit(实际使用56bit),单纯的DES已经不能较好的满足目前行业发展的要求,目前使用较多的是强化过后的DES(如3DES)。
解密
在加密的过程中我们在最后进行末置换时提到,进行末置换是为了能让这套算法技能用作加密,也能用作解密。是的,就是这么神奇,因为加密过程的各个运算都是经过了精心的设计才能达到这样的效果。
而解密与加密唯一的不同之处就是密匙的次序相反,也就是说各轮加密的密匙分别是K1, K2, K3......K16, 那么解密时就是K16, K15, K14......K1, 为各轮产生密匙的算法也是循环的,密匙改为右移。每次移动的个数为: 0,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1。
【插一句嘴:解密时,密匙向右移动的位数第一次移动0位,总数是27,和加密的移动过程有何关系?(这也是为什么解密需要使用次序相反的密匙)】
相关问题
什么是弱密匙
由于DES加密完全取决于密匙,且DES的每轮加密都将密匙分组移位再置换以保证密匙的不唯一性,那么当我们每轮所得到的密匙都一样时,这样的密匙称为“弱密匙”,当密匙全是1或0,或者一半是全1,或一半是全0时,就会发生这样的情况。
同理,半弱密匙是指那些在密匙的16轮移位置换中,大部分结果都相同的密匙。
什么是补密匙
补密匙就是密匙的每一位取反(用1代替0,用0代替已)。
如果密匙作用于明文得到密文,那么补密匙作用于明文的补,将得到密文的补。这是因为在每一轮的加密中,密匙经过扩展置换后都将于右半部分异或运算,使其具有互补的特性。
因为这个特性,使得DES在遭遇选择明文攻击时,只需要使用到 2⁵⁶ 个密匙的一半2⁵⁵个密匙即可。最好不要使用补密匙。
为什么要强化DES加密
DES加密诞生年代较早,设计方式上也受限于当时硬件,密匙有效长度仅为56bit,随着破解技术的发展,主要还是因为硬件设备性能的提升,破解一个DES的密匙成本越来越低,这也使得最初的DES加密变得不再那么安全,常见的各种变型DES加密有 3DES,DESX等等。
其中3DES采用了三重DES加密,使得其加密后的密文更难用穷举搜索破译。3DES加密过程如下:
C = Ek3(Dk2(Ek1(P)))
(其中P代表明文,C为密文,Ek和Dk为加密和解密)
什么是ECB模式与CBC模式
ECB(电子密本)是最常采用的方式,分块并行处理,速度极快,但同样的原文会得到相同的密文,安全性较低。
CBC(密码分组链接)比ECB复杂一点,每一块的加密依赖前一块密文,同样的原文能得到不同的密文,相对于ECB能提供更好的安全性,但因为串行加密,会传递误差。
其他两种模式为 OFB(输出反馈), CFB(秘文反馈)。
所以使用时可以根据具体业务要求选择不同的模式以求在效率和安全性上达到最佳效果。
什么是补位填充
DES加密块长度为8字节(64bit),当末尾块不足8字节时就需要补位来填充,通常补00或FF,实际情况补位方式可能不同。
iOS 中的实现
在 iOS 中,使用苹果封装了 CCCrypt() 方法供开发者进行常规的加解密操作。
需要包含 <CommonCrypto/CommonCryptor.h>
3DES 加密示例
+ (NSString *)encrypt3DesHexWithData:(NSString *)data byteKey:(Byte[])key {
NSString *ciphertext = nil;
const char *dataBytes = [data UTF8String];
size_t dataLength = [data length];
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
bufferPtrSize = (dataLength + kCCBlockSizeDES) & ~(kCCBlockSizeDES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
Byte iv[] = {1, 2, 3, 4, 5, 6, 7, 8};
CCCryptorStatus cryptStatus = CCCrypt(//加密or解密
kCCEncrypt,
//算法方式,此处为3DES
kCCAlgorithm3DES,
//工作方式,此处为CBC
kCCOptionPKCS7Padding,
//密匙
key,
//密匙长度
kCCKeySize3DES,
//CBC模式下的初始向量,ECB模式下可设置为nil
iv,
//明文数据
dataBytes,
//明文长度
dataLength,
//数据输出的缓冲区
(void *)bufferPtr,
//数据输出缓冲区大小
bufferPtrSize,
//成功时所返回的字节数
&movedBytes);
if (cryptStatus == kCCSuccess) {
//此处添加了16进制转换
ciphertext = [self encryptByteToHexString:bufferPtr length:(int)movedBytes];
}
free(bufferPtr);
//转换为大写
ciphertext = [ciphertext uppercaseString];
return ciphertext;
}
// 16进制转换
+ (NSString *)encryptByteToHexString:(Byte *)bytes length:(int)length{
NSString * hexString = @"";
if(bytes) {
for(int i = 0; i < length; i ++) {
//16进制数
NSString * temp = [NSString stringWithFormat:@"%x", bytes[i] & 0xff];
if([temp length] == 1) {
hexString = [NSString stringWithFormat:@"%@0%@", hexString, temp];
} else {
hexString = [NSString stringWithFormat:@"%@%@", hexString, temp];
}
}
}
return hexString;
}
AES 加密示例
高级加密标准(AES)与DES都属于对称分组加密,原理类似,详细过程可参考以下几篇文章,这里就不再篇赘述了。
高级加密标准-Wiki
AES
- (id)decryptAESWithData:(NSString *)data aesKey:(NSString *)aesKey {
if (data == nil || aesKey == nil) {
return nil;
}
id response = nil;
NSData * contentData = [[NSData alloc] initWithBase64EncodedString:data options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSData * keyData = [aesKey dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = contentData.length;
size_t encryptSize = dataLength + kCCBlockSizeAES128;
void *encryptedBytes = malloc(encryptSize);
size_t actualOutSize = 0;
NSData *initVector = nil;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES,
kCCOptionECBMode | kCCOptionPKCS7Padding,
keyData.bytes,
kCCKeySizeAES128,
initVector.bytes,
contentData.bytes,
dataLength,
encryptedBytes,
encryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
NSData * data = [NSData dataWithBytes:encryptedBytes length:actualOutSize];
response = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
}
free(encryptedBytes);
return response;
}
这里的两个例子分别为 3DES 和 AES 的一个示例,实际开发中项目要求不同,例如选择ECB或CBC模式,又或是加密结果需要进行16进制或Base64转换等。