-
Notifications
You must be signed in to change notification settings - Fork 106
/
Copy pathauth.c
673 lines (580 loc) · 19.7 KB
/
auth.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
/* File: auth.c
* INode 3.60-E6208
* System: Windows 7
* From: njit-1.2
* Change by vrqq
* Only for NCEPU
* ------------
* 注:核心函数为Authentication(),由该函数执行801.1X认证
*/
int Authentication(const char *UserName, const char *Password, const char *DeviceName);
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <stdbool.h>
#include <pcap.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <arpa/inet.h>
#include "debug.h"
// 自定义常量
typedef enum {REQUEST=1, RESPONSE=2, SUCCESS=3, FAILURE=4, H3CDATA=10} EAP_Code;
typedef enum {IDENTITY=1, NOTIFICATION=2, MD5=4, AVAILABLE=20} EAP_Type;
typedef uint8_t EAP_ID;
const uint8_t BroadcastAddr[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; // 广播MAC地址
const uint8_t MultcastAddr[6] = {0x01,0x80,0xc2,0x00,0x00,0x03}; // 多播MAC地址
//const char H3C_VERSION[16]="EN V3.60-6301"; // 华为客户端版本号
const char H3C_VERSION[16]="CH V3.60-6208"; //windows客户端版本号
const char H3C_KEY[] ="Oly5D62FaE94W7"; // H3C的固定密钥
const char H3C_KEY2[] ="HuaWei3COM1X"; // H3C的固定密钥
const int AutoReConnect=1; //失败后自动重试
unsigned char *response1620;
int have1620CODE=0;
// 子函数声明
static void SendStartPkt(pcap_t *adhandle, const uint8_t mac[]);
static void SendLogoffPkt(pcap_t *adhandle, const uint8_t mac[]);
static void SendResponseIdentity(pcap_t *adhandle,
const uint8_t request[],
const uint8_t ethhdr[],
const uint8_t ip[4],
const char username[],
const int connectFlag);//1:start 2:keepalive.
static void SendResponseMD5(pcap_t *adhandle,
const uint8_t request[],
const uint8_t ethhdr[],
const char username[],
const char passwd[]);
static void SendResponseAvailable(pcap_t *adhandle,
const uint8_t request[],
const uint8_t ethhdr[],
const uint8_t ip[4],
const char username[]);
static void SendResponseNotification(pcap_t *handle,
const uint8_t request[],
const uint8_t ethhdr[]);
static void GetMacFromDevice(uint8_t mac[6], const char *devicename);
static void FillClientVersionArea(uint8_t area[]);
static void FillWindowsVersionArea(uint8_t area[]);
static void FillBase64Area(char area[]);
// From fillmd5.c
extern void FillMD5Area(uint8_t digest[],
uint8_t id, const char passwd[], const uint8_t srcMD5[]);
// From ip.c
extern void GetIpFromDevice(uint8_t ip[4], const char DeviceName[]);
// From handleDES.c
uint8_t* HandleKeepOnline(const uint8_t data[]);
/**
* 函数:Authentication()
*
* 使用以太网进行802.1X认证(802.1X Authentication)
* 该函数将不断循环,应答802.1X认证会话,直到遇到错误后才退出
*/
int Authentication(const char *UserName, const char *Password, const char *DeviceName)
{
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *adhandle; // adapter handle
uint8_t MAC[6];
char FilterStr[100];
struct bpf_program fcode;
const int DefaultTimeout=60000;//设置接收超时参数,单位ms
int i;
// NOTE: 这里没有检查网线是否已插好,网线插口可能接触不良
/* 打开适配器(网卡) */
adhandle = pcap_open_live(DeviceName,65536,1,DefaultTimeout,errbuf);
if (adhandle==NULL) {
fprintf(stderr, "%s\n", errbuf);
exit(-1);
}
/* 查询本机MAC地址 */
GetMacFromDevice(MAC, DeviceName);
/*
* 设置过滤器:
* 初始情况下只捕获发往本机的802.1X认证会话,不接收多播信息(避免误捕获其他客户端发出的多播信息)
* 进入循环体前可以重设过滤器,那时再开始接收多播信息
*/
sprintf(FilterStr, "(ether proto 0x888e) and (ether dst host %02x:%02x:%02x:%02x:%02x:%02x)",
MAC[0],MAC[1],MAC[2],MAC[3],MAC[4],MAC[5]);
pcap_compile(adhandle, &fcode, FilterStr, 1, 0xff);
pcap_setfilter(adhandle, &fcode);
START_AUTHENTICATION:
{
int retcode,start_count=0;
struct pcap_pkthdr *header;
const uint8_t *captured;
uint8_t ethhdr[14]={0}; // ethernet header
uint8_t ip[4]={0}; // ip address
/* 主动发起认证会话 */
SendStartPkt(adhandle, MAC);
DPRINTF("[0] Client: Start.\n");
/* 等待认证服务器的回应 */
bool serverIsFound = false;
while (!serverIsFound)
{
retcode = pcap_next_ex(adhandle, &header, &captured);
if (retcode==1 && (EAP_Code)captured[18]==REQUEST)
serverIsFound = true;//Found "Request Identity".;
else
{ // 延时后重试
sleep(1); DPRINTF(".");
SendStartPkt(adhandle, MAC);
// NOTE: 这里没有检查网线是否接触不良或已被拔下
if (start_count++>5)
{
DPRINTF("\nServer is not responding...\n");
if (AutoReConnect)
goto START_AUTHENTICATION;
else
exit(-1);
}
}
}
have1620CODE=0;
if (start_count)
printf ("\n");
start_count=0;
// TODO: 完成响应ctrl+C
//signal(SIGINT, SendLogoffPkt);
// 填写应答包的报头(以后无须再修改)
// 默认以单播方式应答802.1X认证设备发来的Request
memcpy(ethhdr+0, captured+6, 6);
memcpy(ethhdr+6, MAC, 6);
ethhdr[12] = 0x88;
ethhdr[13] = 0x8e;
// 分情况应答下一个包
if ((EAP_Type)captured[22] == IDENTITY)
{ // 通常情况会收到包Request Identity,应回答Response Identity
DPRINTF("[%d] Server: Request Identity! >>Connect.\n", captured[19]);
GetIpFromDevice(ip, DeviceName);
SendResponseIdentity(adhandle, captured, ethhdr, ip, UserName, 1);
DPRINTF("[%d] Client: Response Identity. >>Connect.\n", (EAP_ID)captured[19]);
}
// 重设过滤器,只捕获华为802.1X认证设备发来的包(包括多播Request Identity / Request AVAILABLE)
sprintf(FilterStr, "(ether proto 0x888e) and (ether src host %02x:%02x:%02x:%02x:%02x:%02x)",
captured[6],captured[7],captured[8],captured[9],captured[10],captured[11]);
pcap_compile(adhandle, &fcode, FilterStr, 1, 0xff);
pcap_setfilter(adhandle, &fcode);
// 进入循环体
for (;;)
{
// 调用pcap_next_ex()函数捕获数据包
while (pcap_next_ex(adhandle, &header, &captured) != 1)
{
DPRINTF("."); // 若捕获失败,则等1秒后重试
sleep(1); // 直到成功捕获到一个数据包后再跳出
// NOTE: 这里没有检查网线是否已被拔下或插口接触不良
if (start_count++>2)
{
DPRINTF("\nWait for handshake package timed out...\n");
if (AutoReConnect)
goto START_AUTHENTICATION;
else
exit(-1);
}
}
// 根据收到的Request,回复相应的Response包
if ((EAP_Code)captured[18] == REQUEST)
{
switch ((EAP_Type)captured[22])
{
case IDENTITY:
DPRINTF("[%d] Server: Request Identity!\n", (EAP_ID)captured[19]);
GetIpFromDevice(ip, DeviceName);
SendResponseIdentity(adhandle, captured, ethhdr, ip, UserName, 2);
DPRINTF("[%d] Client: Response Identity.\n", (EAP_ID)captured[19]);
break;
case AVAILABLE:
DPRINTF("[%d] Server: Request AVAILABLE!\n", (EAP_ID)captured[19]);
GetIpFromDevice(ip, DeviceName);
SendResponseAvailable(adhandle, captured, ethhdr, ip, UserName);
DPRINTF("[%d] Client: Response AVAILABLE.\n", (EAP_ID)captured[19]);
break;
case MD5:
DPRINTF("[%d] Server: Request MD5-Challenge!\n", (EAP_ID)captured[19]);
SendResponseMD5(adhandle, captured, ethhdr, UserName, Password);
DPRINTF("[%d] Client: Response MD5-Challenge.\n", (EAP_ID)captured[19]);
break;
case NOTIFICATION:
DPRINTF("[%d] Server: Request Notification!\n", captured[19]);
SendResponseNotification(adhandle, captured, ethhdr);
DPRINTF(" Client: Response Notification.\n");
break;
default:
DPRINTF("[%d] Server: Request (type:%d)!\n", (EAP_ID)captured[19], (EAP_Type)captured[22]);
DPRINTF("Error! Unexpected request type\n");
exit(-1);
break;
}
}
else if ((EAP_Code)captured[18] == FAILURE)
{ // 处理认证失败信息
uint8_t errtype = captured[22];
uint8_t msgsize = captured[23];
const char *msg = (const char*) &captured[24];
DPRINTF("[%d] Server: Failure.\n", (EAP_ID)captured[19]);
if (errtype==0x09 && msgsize>0)
{ // 输出错误提示消息
fprintf(stderr, "%s\n", msg);
// 已知的几种错误如下
// E2531:用户名不存在
// E2535:Service is paused
// E2542:该用户帐号已经在别处登录
// E2547:接入时段限制
// E2553:密码错误
// E2602:认证会话不存在
// E3137:客户端版本号无效
exit(-1);
}
else if (errtype==0x08) // 可能网络无流量时服务器结束此次802.1X认证会话
{ // 遇此情况客户端立刻发起新的认证会话
goto START_AUTHENTICATION;
}
else
{
DPRINTF("errtype=0x%02x\n", errtype);
exit(-1);
}
}
else if ((EAP_Code)captured[18] == SUCCESS)
{
DPRINTF("[%d] Server: Success.\n", captured[19]);
// 刷新IP地址
//system("dhclient");
system("njit-RefreshIP");
}
else
{
DPRINTF("[%d] Server: %2x %2x %2x %2x %2x(H3C data)\n", captured[19],captured[22],captured[23],captured[24],captured[25],captured[26]);
if ((uint8_t)captured[22]==0x19 && (uint8_t)captured[23]==0x2b && (uint8_t)captured[24]==0x44 && (uint8_t)captured[25]==0x2b && (uint8_t)captured[26]==0x32)
{
response1620=HandleKeepOnline(captured);
have1620CODE=1;
DPRINTF("[0x32] Client: ProcessEapHW --> hb-pro is set.\n");
}
// TODO: 这里没有处理华为自定义数据包
}
}
}
return (0);
}
static
void GetMacFromDevice(uint8_t mac[6], const char *devicename)
{
int fd;
int err;
struct ifreq ifr;
fd = socket(PF_PACKET, SOCK_RAW, htons(0x0806));
assert(fd != -1);
assert(strlen(devicename) < IFNAMSIZ);
strncpy(ifr.ifr_name, devicename, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
err = ioctl(fd, SIOCGIFHWADDR, &ifr);
assert(err != -1);
memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
err = close(fd);
assert(err != -1);
return;
}
static
void SendStartPkt(pcap_t *handle, const uint8_t localmac[])
{
uint8_t packet[18];
// Ethernet Header (14 Bytes)
memcpy(packet, BroadcastAddr, 6);
memcpy(packet+6, localmac, 6);
packet[12] = 0x88;
packet[13] = 0x8e;
// EAPOL (4 Bytes)
packet[14] = 0x01; // Version=1
packet[15] = 0x01; // Type=Start
packet[16] = packet[17] =0x00;// Length=0x0000
// 为了兼容不同院校的网络配置,这里发送两遍Start包
// 1、广播发送Strat包
pcap_sendpacket(handle, packet, sizeof(packet));
// 2、多播发送Strat包
memcpy(packet, MultcastAddr, 6);
pcap_sendpacket(handle, packet, sizeof(packet));
}
static
void SendResponseAvailable(pcap_t *handle, const uint8_t request[], const uint8_t ethhdr[], const uint8_t ip[4], const char username[])
{
int i;
uint16_t eaplen;
int usernamelen;
uint8_t response[128];
assert((EAP_Code)request[18] == REQUEST);
assert((EAP_Type)request[22] == AVAILABLE);
// Fill Ethernet header
memcpy(response, ethhdr, 14);
// 802,1X Authentication
// {
response[14] = 0x1; // 802.1X Version 1
response[15] = 0x0; // Type=0 (EAP Packet)
//response[16~17]留空 // Length
// Extensible Authentication Protocol
// {
response[18] = (EAP_Code) RESPONSE; // Code
response[19] = request[19]; // ID
//response[20~21]留空 // Length
response[22] = (EAP_Type) AVAILABLE; // Type
// Type-Data
// {
i = 23;
response[i++] = 0x00;// 上报是否使用代理
response[i++] = 0x15; // 上传IP地址
response[i++] = 0x04; //
memcpy(response+i, ip, 4);//
i += 4; //
response[i++] = 0x06; // 携带版本号
response[i++] = 0x07; //
FillBase64Area((char*)response+i);//
i += 28; //
response[i++] = ' '; // 两个空格符
response[i++] = ' '; //
usernamelen = strlen(username);
memcpy(response+i, username, usernamelen);//
i += usernamelen; //
// }
// }
// }
// 补填前面留空的两处Length
eaplen = htons(i-18);
memcpy(response+16, &eaplen, sizeof(eaplen));
memcpy(response+20, &eaplen, sizeof(eaplen));
// 发送
pcap_sendpacket(handle, response, i);
}
static
void SendResponseIdentity(pcap_t *adhandle, const uint8_t request[], const uint8_t ethhdr[], const uint8_t ip[4], const char username[],const int connectFlag)
{
uint8_t response[128];
size_t i;
uint16_t eaplen;
int usernamelen;
assert((EAP_Code)request[18] == REQUEST);
assert((EAP_Type)request[22] == IDENTITY
||(EAP_Type)request[22] == AVAILABLE); // 兼容中南财经政法大学情况
// Fill Ethernet header
memcpy(response, ethhdr, 14);
// 802,1X Authentication
// {
response[14] = 0x1; // 802.1X Version 1
response[15] = 0x0; // Type=0 (EAP Packet)
//response[16~17]留空 // Length
// Extensible Authentication Protocol
// {
response[18] = (EAP_Code) RESPONSE; // Code
response[19] = request[19]; // ID
//response[20~21]留空 // Length
response[22] = (EAP_Type) IDENTITY; // Type
// Type-Data
// {
i = 23;
if (connectFlag != 1)//connect stage
{
if (have1620CODE)
{
response[i++] = 0x16;
response[i++] = 0x20;
memcpy(response+i,response1620,32);
i+=32;
}
response[i++] = 0x15; // 上传IP地址
response[i++] = 0x04; //
memcpy(response+i, ip, 4);//
i += 4; //
}
response[i++] = 0x06; // 携带版本号
response[i++] = 0x07; //
FillBase64Area((char*)response+i);//
i += 28; //
response[i++] = ' '; // 两个空格符
response[i++] = ' '; //
usernamelen = strlen(username); //末尾添加用户名
memcpy(response+i, username, usernamelen);
i += usernamelen;
assert(i <= sizeof(response));
// }
// }
// }
// 补填前面留空的两处Length
eaplen = htons(i-18);
memcpy(response+16, &eaplen, sizeof(eaplen));
memcpy(response+20, &eaplen, sizeof(eaplen));
// 发送
pcap_sendpacket(adhandle, response, i);
return;
}
static
void SendResponseMD5(pcap_t *handle, const uint8_t request[], const uint8_t ethhdr[], const char username[], const char passwd[])
{
uint16_t eaplen;
size_t usernamelen;
size_t packetlen;
uint8_t response[128];
int i;
assert((EAP_Code)request[18] == REQUEST);
assert((EAP_Type)request[22] == MD5);
usernamelen = strlen(username);
eaplen = htons(22+usernamelen);
packetlen = 14+4+22+usernamelen; // ethhdr+EAPOL+EAP+usernamelen
// Fill Ethernet header
memcpy(response, ethhdr, 14);
// 802,1X Authentication
// {
response[14] = 0x1; // 802.1X Version 1
response[15] = 0x0; // Type=0 (EAP Packet)
memcpy(response+16, &eaplen, sizeof(eaplen)); // Length
// Extensible Authentication Protocol
// {
response[18] = (EAP_Code) RESPONSE;// Code
response[19] = request[19]; // ID
response[20] = response[16]; // Length
response[21] = response[17]; // (NCEPU:32)
response[22] = (EAP_Type) MD5; // Type
response[23] = 16; // Value-Size: 16 Bytes
FillMD5Area(response+24, request[19], passwd, request+24);
memcpy(response+40, username, usernamelen);
// }
// }
for (i=0;i<10;i++)//Fill last 10 character.
response[packetlen++]=0x0;
pcap_sendpacket(handle, response, packetlen);
}
static
void SendLogoffPkt(pcap_t *handle, const uint8_t localmac[])
{
uint8_t packet[18];
// Ethernet Header (14 Bytes)
memcpy(packet, MultcastAddr, 6);
memcpy(packet+6, localmac, 6);
packet[12] = 0x88;
packet[13] = 0x8e;
// EAPOL (4 Bytes)
packet[14] = 0x01; // Version=1
packet[15] = 0x02; // Type=Logoff
packet[16] = packet[17] =0x00;// Length=0x0000
// 发包
pcap_sendpacket(handle, packet, sizeof(packet));
DPRINTF(">>Client: Logoff. Program Exit......\n");
}
// 函数: XOR(data[], datalen, key[], keylen)
//
// 使用密钥key[]对数据data[]进行异或加密
//(注:该函数也可反向用于解密)
static
void XOR(uint8_t data[], unsigned dlen, const char key[], unsigned klen)
{
unsigned int i,j;
// 先按正序处理一遍
for (i=0; i<dlen; i++)
data[i] ^= key[i%klen];
// 再按倒序处理第二遍
for (i=dlen-1,j=0; j<dlen; i--,j++)
data[i] ^= key[j%klen];
}
static
void FillClientVersionArea(uint8_t area[20])
{
uint32_t random=22222222;
char RandomKey[8+1];
random += (uint32_t) (time(NULL)+12121212); // 注:可以选任意32位整数
sprintf(RandomKey, "%08x", random);// 生成RandomKey[]字符串
// 第一轮异或运算,以RandomKey为密钥加密16字节
memcpy(area, H3C_VERSION, sizeof(H3C_VERSION));
XOR(area, 16, RandomKey, strlen(RandomKey));
// 此16字节加上4字节的random,组成总计20字节
random = htonl(random); // (需调整为网络字节序)
memcpy(area+16, &random, 4);
// 第二轮异或运算,以H3C_KEY为密钥加密前面生成的20字节
XOR(area, 20, H3C_KEY, strlen(H3C_KEY));
}
static
void FillWindowsVersionArea(uint8_t area[20])
{
/*
const uint8_t WinVersion[20] = "r70393861";
memcpy(area, WinVersion, 20);
XOR(area, 20, H3C_KEY, strlen(H3C_KEY));
*/
int i;
uint8_t vmac[20]={0x39, 0x68, 0x3C, 0x1C, 0x00, 0x38, 0x46, 0x0B,
0x17, 0x7C, 0x7C, 0x17, 0x0B, 0x46, 0x08, 0x32,
0x32, 0x08, 0x46, 0x0B};
//uint8_t vwin[20]={};
for (i=0;i<20;i++)
area[i]=vmac[i];
}
static
void SendResponseNotification(pcap_t *handle, const uint8_t request[], const uint8_t ethhdr[])
{
uint8_t response[67];
assert((EAP_Code)request[18] == REQUEST);
assert((EAP_Type)request[22] == NOTIFICATION);
// Fill Ethernet header
memcpy(response, ethhdr, 14);
// 802,1X Authentication
// {
response[14] = 0x1; // 802.1X Version 1
response[15] = 0x0; // Type=0 (EAP Packet)
response[16] = 0x00; // Length
response[17] = 0x31; //
// Extensible Authentication Protocol
// {
response[18] = (EAP_Code) RESPONSE; // Code
response[19] = (EAP_ID) request[19]; // ID
response[20] = response[16]; // Length
response[21] = response[17]; //
response[22] = (EAP_Type) NOTIFICATION; // Type
int i=23;
/* Notification Data (44 Bytes) */
// 其中前2+20字节为客户端版本
response[i++] = 0x01; // type 0x01
response[i++] = 22; // lenth
FillClientVersionArea(response+i);
i += 20;
// 最后2+20字节存储加密后的Windows操作系统版本号
response[i++] = 0x02; // type 0x02
response[i++] = 22; // length
FillWindowsVersionArea(response+i);
i += 20;
// }
// }
pcap_sendpacket(handle, response, sizeof(response));
}
static
void FillBase64Area(char area[])
{
uint8_t version[20];
const char Tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/"; // 标准的Base64字符映射表
uint8_t c1,c2,c3;
int i, j;
// 首先生成20字节加密过的H3C版本号信息
FillClientVersionArea(version);
// 然后按照Base64编码法将前面生成的20字节数据转换为28字节ASCII字符
i = 0;
j = 0;
while (j < 24)
{
c1 = version[i++];
c2 = version[i++];
c3 = version[i++];
area[j++] = Tbl[ (c1&0xfc)>>2 ];
area[j++] = Tbl[((c1&0x03)<<4)|((c2&0xf0)>>4) ];
area[j++] = Tbl[ ((c2&0x0f)<<2)|((c3&0xc0)>>6)];
area[j++] = Tbl[ c3&0x3f ];
}
c1 = version[i++];
c2 = version[i++];
area[24] = Tbl[ (c1&0xfc)>>2 ];
area[25] = Tbl[((c1&0x03)<<4)|((c2&0xf0)>>4)];
area[26] = Tbl[ ((c2&0x0f)<<2)];
area[27] = '=';
}