转自 : http://www.qmailer.net/archives/188.html
一. DES加密原理
DES 使用一个 56 位的密钥以及附加的 8 位奇偶校验位(每组的第8位作为奇偶校验位),产生最大 64 位的分组大小。这是一个迭代的分组密码,使用称为 Feistel 的技术,其中将加密的文本块分成两半。使用子密钥对其中一半应用循环功能,然后将输出与另一半进行“异或”运算;接着交换这两半,这一过程会继续下去,但最后一个循环不交换。DES 使用 16 轮循环,使用异或,置换,代换,移位操作四种基本运算。
二. DES API
1. 基本数据结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
typedef
unsigned
char
DES_cblock[8];
typedef
/* const */
unsigned
char
const_DES_cblock[8];
typedef
struct
DES_ks
{
union
{
DES_cblock cblock;
DES_LONG deslong[2];
} ks[16];
} DES_key_schedule;
sizeof
(DES_cblock) = 8字节
sizeof
(const_DES_cblock ) = 8字节
sizeof
(DES_key_schedule) = 128字节
|
2. 基本宏定义
1
2
|
#define DES_ENCRYPT 1
#define DES_DECRYPT 0
|
3. 设置密钥函数
1
2
3
4
5
6
7
8
9
10
11
|
//根据字符串生成key
void
DES_string_to_key(
const
char
*str, DES_cblock *key);
//will check that the key passed is of odd parity and is not a week or semi-weak key.
//If the parity is wrong, then -1 is returned. If the key is a weak key, then -2 is returned.
//If an error is returned, the key schedule is not generated
//设置密码表,并进行校验
int
DES_set_key_checked(const_DES_cblock *key, DES_key_schedule *schedule);
//设置密码表,不需要校验
void
DES_set_key_unchecked(const_DES_cblock *key, DES_key_schedule *schedule);
|
4. DES ECB模式加解密API
1
2
3
4
5
6
7
|
void
DES_ecb_encrypt(const_DES_cblock *input, DES_cblock *output, DES_key_schedule *ks,
int
enc);
参数说明:
input:输入数据,8字节
output:输出数据,8字节
ks:密钥
enc:加密-DES_ENCRYPT,解密-DES_DECRYPT
|
5. DES CBC模式加解密API
1
2
3
4
5
6
7
8
9
|
void
DES_ncbc_encrypt(
const
unsigned
char
*input, unsigned
char
*output,
long
length, DES_key_schedule *schedule, DES_cblock *ivec,
int
enc);
参数说明:
input: 输入参数,8字节倍数
output: 输出参数,8字节倍数
length: input的长度
schedule: 密钥
ivec: 初始向量, 8字节
enc: 加密-DES_ENCRYPT,解密-DES_DECRYPT
|
三. DES 示例
1. DES ECB模式示例
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
65
66
67
68
69
70
71
72
73
74
75
76
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>
#include "hex.h"
/************************************************************************
* DES-ECB加密方式
* 8位密钥,加密内容8位补齐,补齐方式为:PKCS7。
*
* file: test_des_ecb.c
* gcc -Wall -O2 -o test_des_ecb test_des_ecb.c hex.c -lcrypto
*
* author: tonglulin@gmail.com by www.qmailer.net
************************************************************************/
int
main(
int
argc,
char
*argv[])
{
DES_key_schedule ks;
int
i = 0;
int
len = 0;
int
nlen = 0;
int
count = 0;
char
*data =
"12345678123456781234567812345678"
;
/* 原始明文, 十六进制字符串 */
char
*okey =
"0000000000000000"
;
/* 原始密钥, 十六进制字符串 */
unsigned
char
ch =
'\0'
;
unsigned
char
*ptr = NULL;
unsigned
char
src[16] = {0};
/* 补齐后的明文, data补齐后的1/2长度 */
unsigned
char
dst[16] = {0};
/* 解密后的明文, data补齐后的1/2长度 */
unsigned
char
out[8] = {0};
unsigned
char
tmp[8] = {0};
unsigned
char
block[8] = {0};
/* 设置密码表 */
ptr = hex2bin(okey,
strlen
(okey), &nlen);
memcpy
(block, ptr, 8);
free
(ptr);
DES_set_key_unchecked((const_DES_cblock*)block, &ks);
/* 分析补齐明文所需空间及补齐填充数据 */
len =
strlen
((
char
*)data);
ptr = hex2bin(data, len, &nlen);
len = (nlen / 8 + (nlen % 8 ? 1: 0)) * 8;
memcpy
(src, ptr, len);
free
(ptr);
ch = 8 - nlen % 8;
memset
(src + nlen, ch, 8 - nlen % 8);
printf
(
"加密之前: "
);
for
(i = 0; i < len; i++) {
printf
(
"%.2X"
, *(src + i));
}
printf
(
"\n"
);
/* 分组加密,每8字节一组 */
count = len / 8;
for
(i = 0; i < count; i++) {
memcpy
(tmp, src + 8 * i, 8);
DES_ecb_encrypt((const_DES_cblock*)tmp, (DES_cblock*)out, &ks, DES_ENCRYPT);
memcpy
(dst + 8 * i, out, 8);
}
printf
(
"加密之后: "
);
for
(i = 0; i < len; i++) {
printf
(
"%.2X"
, *(dst + i));
}
printf
(
"\n"
);
return
0;
}
|
2. DES CBC模式示例
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
65
66
67
68
69
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>
#include "hex.h"
/************************************************************************
* DES-CBC加密方式
* 8位密钥,加密内容8位补齐,补齐方式为:PKCS7。
*
* file: test_des_cbc.c
* gcc -Wall -O2 -o test_des_cbc test_des_cbc.c hex.c -lcrypto
*
* author: tonglulin@gmail.com by www.qmailer.net
************************************************************************/
int
main(
int
argc,
char
*argv[])
{
DES_key_schedule ks;
DES_cblock ivec = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int
i = 0;
int
len = 0;
int
nlen = 0;
char
*data =
"12345678123456781234567812345678"
;
/* 原始明文, 十六进制字符串 */
char
*okey =
"0000000000000000"
;
/* 原始密钥, 十六进制字符串 */
unsigned
char
ch =
'\0'
;
unsigned
char
*ptr = NULL;
unsigned
char
src[16] = {0};
/* 补齐后的明文, data补齐后的1/2长度 */
unsigned
char
dst[16] = {0};
/* 解密后的明文, data补齐后的1/2长度 */
unsigned
char
block[8] = {0};
/* 设置密码表 */
ptr = hex2bin(okey,
strlen
(okey), &nlen);
memcpy
(block, ptr, 8);
free
(ptr);
DES_set_key_unchecked((const_DES_cblock*)block, &ks);
/* 分析补齐明文所需空间及补齐填充数据 */
len =
strlen
((
char
*)data);
ptr = hex2bin(data, len, &nlen);
len = (nlen / 8 + (nlen % 8 ? 1: 0)) * 8;
memcpy
(src, ptr, len);
free
(ptr);
ch = 8 - nlen % 8;
memset
(src + nlen, ch, 8 - nlen % 8);
printf
(
"加密之前: "
);
for
(i = 0; i < len; i++) {
printf
(
"%.2X"
, *(src + i));
}
printf
(
"\n"
);
/* 加密块链式加密 */
DES_ncbc_encrypt(src, dst,
sizeof
(src), &ks, &ivec, DES_ENCRYPT);
printf
(
"加密之后: "
);
for
(i = 0; i < len; i++) {
printf
(
"%.2X"
, *(dst + i));
}
printf
(
"\n"
);
return
0;
}
|
3. 输出结果
1
2
3
4
5
6
7
|
# ECB 模式
加密之前: 12345678123456781234567812345678
加密之后: 4A438AC15D8074B54A438AC15D8074B5
# CBC模式
加密之前: 12345678123456781234567812345678
加密之后: 4A438AC15D8074B58244AE0E7477AF78
|
由结果可见,ECB和CBC模式的第一个8字节分组结果是一致的
四. HEX转换函数
1. HEX头文件 hex.h
1
2
3
4
5
6
7
8
|
#ifndef _HEX_H_
#define _HEX_H_
char
*bin2hex(unsigned
char
*data,
int
size);
unsigned
char
*hex2bin(
const
char
*data,
int
size,
int
*outlen);
#endif
|
1. HEX定义文件 hex.c
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/************************************************************************
* 二进制字节数组转换十六进制字符串函数
* 输入:
* data 二进制字节数组
* size 二进制字节数组长度
* 输出:
* 十六进制字符串,需要free函数释放空间,失败返回NULL
*
* author: tonglulin@gmail.com by www.qmailer.net
************************************************************************/
char
*bin2hex(unsigned
char
*data,
int
size)
{
int
i = 0;
int
v = 0;
char
*p = NULL;
char
*buf = NULL;
char
base_char =
'A'
;
buf = p = (
char
*)
malloc
(size * 2 + 1);
for
(i = 0; i < size; i++) {
v = data[i] >> 4;
*p++ = v < 10 ? v +
'0'
: v - 10 + base_char;
v = data[i] & 0x0f;
*p++ = v < 10 ? v +
'0'
: v - 10 + base_char;
}
*p =
'\0'
;
return
buf;
}
/************************************************************************
* 十六进制字符串转换二进制字节数组
* 输入:
* data 十六进制字符串
* size 十六进制字符串长度,2的倍数
* outlen 转换后的二进制字符数组长度
* 输出:
* 二进制字符数组,需要free函数释放空间,失败返回NULL
*
* author: tonglulin@gmail.com by www.qmailer.net
************************************************************************/
unsigned
char
*hex2bin(
const
char
*data,
int
size,
int
*outlen)
{
int
i = 0;
int
len = 0;
char
char1 =
'\0'
;
char
char2 =
'\0'
;
unsigned
char
value = 0;
unsigned
char
*out = NULL;
if
(size % 2 != 0) {
return
NULL;
}
len = size / 2;
out = (unsigned
char
*)
malloc
(len *
sizeof
(
char
) + 1);
if
(out == NULL) {
return
NULL;
}
while
(i < len) {
char1 = *data;
if
(char1 >=
'0'
&& char1 <=
'9'
) {
value = (char1 -
'0'
) << 4;
}
else
if
(char1 >=
'a'
&& char1 <=
'f'
) {
value = (char1 -
'a'
+ 10) << 4;
}
else
if
(char1 >=
'A'
&& char1 <=
'F'
) {
value = (char1 -
'A'
+ 10) << 4;
}
else
{
free
(out);
return
NULL;
}
data++;
char2 = *data;
if
(char2 >=
'0'
&& char2 <=
'9'
) {
value |= char2 -
'0'
;
}
else
if
(char2 >=
'a'
&& char2 <=
'f'
) {
value |= char2 -
'a'
+ 10;
}
else
if
(char2 >=
'A'
&& char2 <=
'F'
) {
value |= char2 -
'A'
+ 10;
}
else
{
free
(out);
return
NULL;
}
data++;
*(out + i++) = value;
}
*(out + i) =
'\0'
;
if
(outlen != NULL) {
*outlen = i;
}
return
out;
}
|