对传输的数据包进行加密,在tcp层之上,应用层之下进行。在利用网络传输之前,对传输的数据进行混淆。防止中间人拦截,摧毁外挂。
有三种方案:
1. 自己实现对称加密算法,将数据包的内容进行加密。
- 通常做法是按字节或者按位进行运算,本质上是对字符串进行运算,将字符串作为参数,进行运算后产生新的内容然后发送。
- 加密和解密的效率,以及破解的难度,与字符串运算的算法有关。但实现起来相对比较容易,bug排查也易于进行。
2. 利用第三方的对称加密算法,客户端和服务器端利用相同的密钥进行加密。
- 此方法的所有数据包的加密和解密密钥相同,相同数据包,经过相同密钥加密后,产生的内容也相同。
- 算法对称,密钥固定,易于排查bug。
- 多次对数据包进行运行将容易推导出相应的内容,密钥固定,客户端被反编译出密钥,将面临破解。
3. 动态密钥加密。
- 算法逻辑是,在建立连接时,服务器端利用rsa私钥加密一个随机的密钥key,客户端利用rsa公钥解出从服务器端发出的密钥key。至此,在建立连接之后,双方同步了随机的key。
- 在通信阶段,客户端和服务器端利用握手过程中同步的key,进行des对称加密传输。这个时间段利用des主要考虑效率因素,测试阶段发现rsa的加密解密per都消耗了1k微妙,des则在10微妙左右。
- 连接终止,双方销毁密钥key。在下一个连接建立时,重新利用rsa同步新的随机密钥key。
该算法比较特殊的地方,在于建立连接的时候,双方利用rsa算法同步了一个随机的密钥,这使得每次通信的加密密钥都是新的。对于外挂的防范:
- c端和s端连接已经建立的情况下,若中间人拦截数据包,由于此前并没有获得des的密钥key,所以需进行破解,但是破解的时间被限定在了连接终止之前。在短时间内破解出des算法的密钥,几率很小。
- 如果攻击者反编译出客户端的rsa公钥,想破解出服务端的rsa私钥,用以建立私服,那在技术上是不可行的。
- 如果已获取了客户端的rsa公钥,想要利用和服务端建立连接的时机,获取通讯时的des密钥key,那是没有意义的,因为每次建立连接,des密钥key都被重新生成。
动态密钥的方法安全性较高,技术实现比其他前两种繁琐,而且涉及到动态的密钥,对bug的追踪构成一定困难。
效率问题存在两个阶段
- 建立连接的过程中,由rsa同步c和s双方的des密钥,rsa算法相对耗时,对建立连接时的握手有一定影响。
- 在通信的过程中,由于双方的加解密是利用des对称算法,时间消耗与des算法相关,而des算法的ecb方式每次只能对一组数据(8位加密),cbc方式不限定加密数据长度。经测算,des的cbc方式单个执行都在10微秒左右。高数据量通信有待进一步测试。
client:
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <openssl/des.h>
int main(int argc, char **argv)
{
if (argc < 3) {
printf("client <IP> <PORT>\n");
return 0;
}
char str[1024] = {0};
char buf[1024] = {0};
int sd;
struct sockaddr_in sdaddr;
sd = socket(AF_INET, SOCK_STREAM, 0);
sdaddr.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &sdaddr.sin_addr);
sdaddr.sin_port = atoi(argv[2]);
int res = connect(sd, (struct sockaddr *)&sdaddr, sizeof(sdaddr));
printf("res: %d, errno: %d, %s\n", res, errno, strerror(errno));
////
unsigned char rbuf[256] = {0};
res = recv(sd, rbuf, sizeof(rbuf), 0);
int len = strlen(rbuf);
for (int i = 0; i < len; ++i)
printf("%02X", rbuf[i]);
printf("\nlen:%d\n", len);
////
DES_cblock key;
memcpy(&key, rbuf, sizeof(key));
int ylen = sizeof(key);
for (int i = 0; i < ylen; ++i)
printf("%02X", key[i]);
printf("\nylen:%d\n", ylen);
DES_key_schedule key_schedule;
if (DES_set_key_checked(&key, &key_schedule) != 0) {
printf("convert to key_schedule failed.\n");
return -1;
}
//IV
DES_cblock ivec;
while(1)
{
//IV设置为0x0000000000000000
memset((char*)&ivec, 0, sizeof(ivec));
bzero(str, sizeof(str));
printf("input:");
scanf("%s\n", str);
size_t len = (strlen(str)+7)/8 * 8;
unsigned char *output = malloc(len+1);
DES_ncbc_encrypt((unsigned char*)str, output, strlen(str), &key_schedule, &ivec, DES_ENCRYPT);
int outlen = strlen(output);
printf("output:");
for (int i = 0; i < outlen; ++i)
printf("%02X", output[i]);
printf("\noutlen:%d\n", outlen);
send(sd, output, strlen(output), 0);
free(output);
////////////
bzero(buf, sizeof(buf));
res = recv(sd, buf, sizeof(buf), 0);
//printf("res: %d\n", res);
printf("recv: %s\n", buf);
}
return 0;
}
server
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/time.h>
#include <openssl/des.h>
#define EVENTS_NOMBER 1024
struct recv
{
char header[256];
char data[1024];
char recvbuf[1024];
char sendbuf[1024];
int sockedfd;
int retfd;
};
struct sock_operations
{
int (*get_sockfd)(const char *domain, int port);
int (*setNoneBlocking)(int fd);
void (*add_eventfd)(int epollfd, int fd);
void (*pth_work)(pthread_t *pid,void *fd);
DES_cblock key;
};
struct Recv_sock
{
struct recv recvdata;
struct sock_operations operations;
};
int setNoneBlocking(int fd)
{
int old = fcntl(fd, F_GETFL);
int new = old|O_NONBLOCK;
fcntl(fd, F_SETFL, new);
return old;
}
void addfd(int epollfd, int fd)
{
struct epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
setNoneBlocking(fd);
}
void *work(void *recv_sock_addr)
{
struct Recv_sock *recv_sock = (struct Recv_sock *) recv_sock_addr;
int retfd = recv_sock->recvdata.retfd;
bzero(recv_sock->recvdata.recvbuf, sizeof(recv_sock->recvdata.recvbuf));
bzero(recv_sock->recvdata.sendbuf, sizeof(recv_sock->recvdata.sendbuf));
DES_cblock key;
memcpy(&key, &(recv_sock->operations.key), sizeof(key));
int ylen = sizeof(key);
/*
printf("key:");
for (int i = 0; i < ylen; ++i)
printf("%02X", key[i]);
printf("\nylen:%d\n", ylen);
*/
DES_key_schedule key_schedule;
if (DES_set_key_checked(&key, &key_schedule) != 0) {
printf("convert to key_schedule failed.\n");
return;
}
DES_cblock ivec;
memset((char*)&ivec, 0, sizeof(ivec));
int len = recv(retfd, recv_sock->recvdata.recvbuf, sizeof(recv_sock->recvdata.recvbuf), 0);
int rcblen = strlen(recv_sock->recvdata.recvbuf);
unsigned char* recvbuf = (unsigned char*) recv_sock->recvdata.recvbuf;
printf("encrypto:");
for(int i=0; i<rcblen; ++i) {
printf("%02X ", recvbuf[i]);
}
printf("\nrcblen:%d\n", rcblen);
char output[1024] = {0};
DES_ncbc_encrypt((unsigned char*)recv_sock->recvdata.recvbuf, (unsigned char*)output, rcblen, &key_schedule, &ivec, 0);
printf("recvbuf: %s\n\n", output);
sprintf(recv_sock->recvdata.sendbuf, "len: %d, data:%s\n", len, output);
send(retfd, recv_sock->recvdata.sendbuf, strlen(recv_sock->recvdata.sendbuf), 0);
}
void pth_work(pthread_t *pid,void *recv_sock)
{
pthread_create(pid, NULL, work, recv_sock);
}
int get_sockfd(const char *domain, int port)
{
int sd;
struct sockaddr_in addr;
sd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
const char *ip = domain;
inet_pton(AF_INET, ip, &addr.sin_addr);
addr.sin_port = port;
int res = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
sd = res>=0?sd:-1;
return sd;
}
struct sock_operations operations =
{
.get_sockfd = get_sockfd,
.setNoneBlocking = setNoneBlocking,
.add_eventfd = addfd,
.pth_work = pth_work
};
struct Recv_sock * init(void)
{
struct Recv_sock *recv_sock = (struct Recv_sock *) malloc (sizeof(struct Recv_sock));
recv_sock->operations = operations;
return recv_sock=recv_sock==NULL?NULL:recv_sock;
}
int main(int argc, char **argv)
{
if (argc < 3) {
printf("server <IP> <PORT>\n");
return 0;
}
struct Recv_sock *recv_sock = init();
recv_sock->recvdata.sockedfd = recv_sock->operations.get_sockfd(argv[1], atoi(argv[2]));
printf("sd: %d\n", recv_sock->recvdata.sockedfd);
listen(recv_sock->recvdata.sockedfd, 5);
struct epoll_event events[EVENTS_NOMBER];
int epollfd = epoll_create(5);
recv_sock->operations.add_eventfd(epollfd, recv_sock->recvdata.sockedfd);
pthread_t pid;
while(1)
{
int ret = epoll_wait(epollfd, events, EVENTS_NOMBER, -1);
int i;
for(i=0;i<ret; i++)
{
recv_sock->recvdata.retfd = events[i].data.fd;
if(recv_sock->recvdata.retfd==recv_sock->recvdata.sockedfd)
{
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(recv_sock->recvdata.sockedfd, (struct sockaddr*)&client_address, &client_addrlength);
recv_sock->operations.add_eventfd(epollfd, connfd);
///////
DES_cblock key;
DES_random_key(&key);
int len = strlen(key);
for (int i = 0; i < len; ++i)
printf("%02X", key[i]);
printf("\nlen:%d\n", len);
send(connfd, (char*)key, sizeof(key), 0);
///////
memcpy(&(recv_sock->operations.key), &key, sizeof(key));
}
else if(events[i].events == EPOLLIN)
{
recv_sock->operations.pth_work(&pid,recv_sock);
pthread_join(pid, NULL);
}
else
{
//printf("something happen...\n");
}
}
}
return 0;
}
对于移动端的openssl来说: