服务端和客户端之文件加密传输

1.前言

1.1 why?

闲来无事,自己研究了一下服务端与客户端之间的文件传输,主要是服务端创建套接字开启监听并等待客户端的连接(TCP),在TCP协议上,为了安全我使用了TLS1.2进行加密通信(OpenSSL库),同时为了文件内容的安全,我在服务端对文件进行了加密(AES),客户端接收到文件后在对其解密。

1.2 硬件

我用的电脑系统是ubuntu 16.04,不是windows,下面的代码放到windows环境下跑不了的,有些库windows不兼容,但是windows开发的小伙伴也可以参考参考,把不兼容的库替换掉修改修改(比如linux的opendir,readdir,closedir可以替换成windows下的函数,具体百度),应该也可以跑过。无论是windows还是linux,下面的代码中都需要证书,将证书的路径正确的放到代码里面,程序才能编译通过,证书如何生成,请参考我以前的博客。

2 server端代码

2.1 头文件以及函数声明

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/ssl.h>
#include <fcntl.h>
#include <stdbool.h>
#include <dirent.h>
#include <sys/types.h>

#define CA_CRT         "./opensslkey/demoCA/cacert1.pem"
#define SERVER_CRT     "./opensslkey/server.crt"
#define SERVER_KEY     "./opensslkey/server.key"

#define MAXBUF 1024
char buffer[MAXBUF];
char buffer1[MAXBUF];
char *Path = NULL;
void Directory(char* path, SSL* ssl, struct dirent *ptr);
bool readfile(char* my_Path,SSL *ssl,struct dirent *ptr);
bool Document(char* Path, SSL *ssl,struct dirent *ptr);
void aes_box_encrypt(char* source_string, char* des_string, int size);
bool Shutdown(SSL* ssl);
void Getmessage(SSL* ssl, char* buf);
DIR* readFileList(char *basePath);
bool Sendlog(SSL* ssl, char* buf);
void ShowCerts(SSL * ssl);

2.2 函数实现

void aes_box_encrypt(char* source_string, char* des_string, int size)//负责数据加密
{
    int iLoop = 0;
    int iLen =0;
    char buf[1024];
    AES_KEY aes;
    unsigned char key[AES_BLOCK_SIZE];
    unsigned char iv[AES_BLOCK_SIZE];
    if(NULL == source_string || NULL == des_string)
    {
        return;
    }
 
    //Generate own AES Key
    for(iLoop = 0; iLoop < 16; iLoop++)
    {
        key[iLoop] = 32 + iLoop;
    }
 
    // Set encryption key
    for (iLoop=0; iLoop<AES_BLOCK_SIZE; iLoop++) 
    {
        iv[iLoop] = 0;
    }
 
    if (AES_set_encrypt_key(key, 128, &aes) < 0) 
    {
        return ;
    }
    iLen = size;
    AES_cbc_encrypt(source_string, des_string, iLen, &aes, iv, AES_ENCRYPT);
}

bool Shutdown(SSL* ssl)//log发送完毕,服务端发送消息断开与客户端的连接(服务器继续等待客户端的下一次连接)
{
    char d[1024];
    memset(d,'\0', sizeof(d));
    strcpy(d, "complate");
    int len = SSL_write(ssl, d, strlen(d));
    if (len <= 0) {
        printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno));
        return false;
    } 
    else
        printf("消息发送成功%s,共发送了%d个字节!\n",d, len);
    return true;
}

void Getmessage(SSL* ssl, char* buf)//获取客户端的消息
{
    int len = SSL_read(ssl, buf, MAXBUF);
    if (len > 0)
        printf("接收消息成功:'%s',共%d个字节的数据\n", buf, len);
    else
        printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno));
}

void Directory(char* path, SSL* ssl, struct dirent *ptr)//目录处理
{
    char *base = NULL;
    base = path;
    strcat(ptr->d_name,"/");
    char* e = (char*)malloc(strlen(base) + strlen(ptr->d_name) + 1);
    strcpy(e,base);
    strcat(e,ptr->d_name);
    bool ret = readfile(e, ssl, ptr);
}


bool Document(char* Path, SSL *ssl,struct dirent *ptr)//真正发送log文件
{
    char a[1024];
    int fd = 0;
    memset(a,'\0', sizeof(a));
    memset(buffer,'\0', sizeof(buffer));
    memset(buffer1,'\0', sizeof(buffer1));
    strcpy(a, Path);
    strcat(a, ptr->d_name);
    fd = open(a, O_RDONLY);
    read(fd, buffer,sizeof(buffer));//发送两次数据,第一次是文件名,第二次是文件内容

    int len1 = SSL_write(ssl, ptr->d_name, strlen(ptr->d_name));
    printf("document name is:%s\n", ptr->d_name);
    if (len1 <= 0) {
        printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno));
        return false;
    } 
    else
        printf("消息发送成功,共发送了%d个字节!\n", len1);
    aes_box_encrypt(buffer, buffer1, sizeof(buffer));
    int len = SSL_write(ssl, buffer1, strlen(buffer1));
    if (len <= 0) {
        printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno));
        return false;
    } 
    else
        printf("消息发送成功,共发送了%d个字节!\n", len);
    
    close(fd);
    return true;
}

DIR* readFileList(char *basePath)//打开目录,opendir配合readdir使用
{
    DIR *dir;
    struct dirent *ptr;//readdir

    if ((dir=opendir(basePath)) == NULL)
    {
        printf("Open path error\n");
        exit(1);
    }
    return dir;
}

bool readfile(char* Path,SSL *ssl,struct dirent *ptr)//文件或者目录处理
{
    DIR *dir = readFileList(Path);
    while ((ptr=readdir(dir)) != NULL)
    {
        if(strcmp(ptr->d_name,".")==0 || strcmp(ptr->d_name,"..")==0) ///current dir OR parrent dir
            continue;
        else if(ptr->d_type == 8) //document
        {
            printf("path is:%s\n",Path);
            Document(Path, ssl, ptr);
        }
        else if(ptr->d_type == 4)//directory
        {
            Directory(Path, ssl, ptr);
        }
    }
    return true;
}

bool Sendlog(SSL* ssl, char* buf)//发送log文件
{
    if(strcmp(buf, "getlog") == 0)
    {
        struct dirent *ptr;
        char *basePath = NULL;
        basePath = "./log/";
        readfile(basePath, ssl, ptr);
    }
    Shutdown(ssl);
}

void ShowCerts(SSL * ssl)//验证显示证书
{
    X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl);
    // SSL_get_verify_result()是重点,SSL_CTX_set_verify()只是配置启不启用并没有执行认证,调用该函数才会真证进行证书认证
    // 如果验证不通过,那么程序抛出异常中止连接
    if(SSL_get_verify_result(ssl) == X509_V_OK){
        printf("证书验证通过\n");
    }
    if (cert != NULL) {
        printf("数字证书信息:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("证书: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("颁发者: %s\n", line);
        free(line);
        X509_free(cert);
    } 
    else
        printf("无证书信息!\n");
}

3 后序

3.1 参考博客

  1. AES加密算法:https://www.cnblogs.com/ygxsk/p/7693978.html
  2. AES加密算法:https://blog.csdn.net/qncj666/article/details/8244893
  3. 文件操作:https://blog.csdn.net/angle_birds/article/details/8502874
  4. 双向认证程序实现:https://www.cnblogs.com/lsdb/p/9391979.html

3.2写给读者的话

  1. 代码一部分是参考别人的,一部分是我自己写的
  2. 代码供大家参考,转载或者自己参考写,请标明原文档地址
  3. 代码我只贴了server端的,客户端代码较少,我没贴,大家可以参考上面的链接自行书写
  4. 代码可能会有我没测出来的bug,欢迎大家继续完善
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页