转自 : 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; } |