精选文章

Android下使用TCPDUMP抓包Wireshark分析数据 如果想分析Android下某个APP的网络数据交互,需要在Android手机上抓包,最常用的抓包工具非tcpdump莫属,用tcpdump生成Wireshark识别的pcap文件,然后将pcap文件下载到电脑上,用电脑上的Wireshark加载pcap文件,通过Wireshark分析tcpdump抓取的数据。...

继续阅读

Mac下部署Android开发环境附加NDK 作为开发者,我们深有体会,不管是进行什么开发,为了部署开发环境,我们往往需要折腾很长时间、查阅很多资料才能完成,而且这次折腾完了,下次到了另一台新电脑上又得重新来过,整个部署过程记得还好,要是不记得又得重新开始,而且遇到Android这种GFW阻隔了开发资源下载链接的环境部署,又尤其浪费时间。所以这也是我写下这篇教程的初衷跟动力源泉,希望大家参考了这篇教程以后可以轻轻松松在Mac系统下将Android环境部署好。...

继续阅读

稍顯嚴肅的台中 坦白說,留在腦海中的台中影像並不多,來台灣之前在Booking上只訂到了台中的一家青旅,第一次住青旅有些不習慣,幹什麼都放不開。 同屋的一個男生是台灣人,不過一年中四分之三的時間在上海跟北京,這麼說來跟我還是比較有共同話題的。得之我準備花15天的時間環島,覺得太倉促了,他們大學時期花一個半月的時間也不見得能將台灣島給逛完。我只能無奈地表示,兩岸允許的簽證時間有限,自己的空閒時間更有限,只能用打卡式的旅行了,我深知正真地旅行應該慢下來,融入當地的環境,感受他們的風土人情,但第一次只能這樣作罷,以後換成民進黨上台,形勢會變成怎樣還不得而知,能否再過來還是個未知數。而我一向信奉的人生格言是秉燭夜遊,活在當下,所以,理解自己吧。...

继续阅读

為之留戀的新竹 來新竹之前本沒有對她有過高的期待,慢慢對她加分要從桃園火車站出發前往新竹開始。 在桃園火車站的候車月台上,有醒目的旅遊資料發放處,這上面的擺放的全是新竹的旅遊宣傳資料,關鍵的是資料做得非常簡潔易懂,而接下來一天的新竹之行就全部是依據這份寶典的指引來完成的。...

继续阅读

從桃園開始台灣之行 初到台灣恰逢華夏銀行系統升級,特意準備的華夏銀聯卡在桃園機場沒能派上用場,只好用建行在機場5000塊,算下來是很不划算的,但是沒辦法,誰叫我出機場就得花錢呢。 從機場打車到桃園的酒店,花了將近六百塊新台幣,到酒店時五點多,天已經漸亮了,洗漱完等到七點吃過早餐就開始補覺囉,一覺醒來已是中午,帶著換下來的衣服外出找自助洗衣店,順便覓食。...

继续阅读

  • Prev
  • Next

简易版SSL加密聊天程序

文章分类 : C语言, Linux, 应用与编程, 网络

简易版SSL加密聊天程序分为服务端跟客服端,主要实现简易的加密聊天功能。服务端支持同时与多个客服端保持通信(连接),采用OpenSSL开源库实现加密功能,依赖客户端主动发起连接与对话方能回复客户端,能够检测到某个客户端断开连接。客户端指定IP与端口与服务端建立连接,需要主动发起对话方能等待服务端的回复。需要用到源文件有sslServer.c、sslClient、api.c和ssl.h,同时我们还需要利用openssl命令生成私有密钥文件privkey.pem以及证书(公有密钥)文件cacert.pem。    

一、下面列出四个需要用到的C语言源文件:

1、ssl.h

#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>
typedef struct _SockList {
    int fd;
    SSL *ssl;
    char ip[16];
    struct _SockList *next;
}SockList;
int ssl_init(SSL_CTX **ctx);
int ssl_load(SSL_CTX *ctx, char *certificate, char *privateKey);
int ssl_accept(SSL_CTX *ctx, int sockfd, SSL **ssl);
int ssl_connect(SSL_CTX *ctx, int *sockfd, SSL **ssl, char *port, char *addr);
int ssl_close(SSL_CTX *ctx, SSL *ssl, int sockfd, int new_fd);

2、api.c

#include "ssl.h"
int ssl_init(SSL_CTX **ctx)
{
    /* SSL 库初始化 */
    SSL_library_init();
    /* 载入所有 SSL 算法 */
    OpenSSL_add_all_algorithms();
    /* 载入所有 SSL 错误消息 */
    SSL_load_error_strings();
    /* 以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Content Text */
    *ctx = SSL_CTX_new(SSLv23_server_method());
    /* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 单独表示 V2 或 V3标准 */
    if (NULL == *ctx)
    {
        ERR_print_errors_fp(stderr);
        return -1;
    }
    return 0;
}
int ssl_load(SSL_CTX *ctx, char *certificate, char *privateKey)
{
    if (NULL == ctx || NULL == certificate || NULL == privateKey) return -1;
    /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
    if (SSL_CTX_use_certificate_file(ctx,certificate, SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stderr);
        return -1;
    }
    /* 载入用户私钥 */
    if (SSL_CTX_use_PrivateKey_file(ctx, privateKey, SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stderr);
        return -1;
    }
    /* 检查用户私钥是否正确 */
    if (!SSL_CTX_check_private_key(ctx))
    {
        ERR_print_errors_fp(stderr);
        return -1;
    }
    return 0;
}
int createListen(int *listen_fd, char *port, char *addr)
{
    int yes = 1;
    struct sockaddr_in my_addr;
    if (NULL == listen_fd || NULL == port) return -1;
    /* 开启一个 socket 监听 */
    if ((*listen_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return -1;
    }
    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(atoi(port));
    if (addr)
        my_addr.sin_addr.s_addr = inet_addr(addr);
    else
        my_addr.sin_addr.s_addr = INADDR_ANY;

    if (-1 == setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
    {
        perror("setsockopt");
        close(*listen_fd);
        return -1;
    }

    if (bind(*listen_fd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) < 0)
    {
        perror("bind");
        close(*listen_fd);
        return -1;
    }
    if (listen(*listen_fd, 3) < 0)
    {
        perror("listen");
        close(*listen_fd);
        return -1;
    }

    return 0;
}

int ssl_accept(SSL_CTX *ctx, int listen_fd, SSL **ssl)
{
    struct sockaddr_in their_addr;
    socklen_t len = sizeof(struct sockaddr);
    int new_fd;

    if (NULL == ctx || listen_fd < 0 || NULL == ssl) return -1;

    /* 等待客户端连上来 */
    bzero(&their_addr, sizeof(their_addr));
    if ((new_fd = accept(listen_fd, (struct sockaddr *)&their_addr, &len)) < 0)
    {
        perror("accept");
        return -1;
    } else
    printf("\n>>>>>>> got connection from %s, port %d, socket %d <<<<<<<\n",
    inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);

    /* 基于 ctx 产生一个新的 SSL */
    *ssl = SSL_new(ctx);
    /* 将连接用户的 socket 加入到 SSL */
    SSL_set_fd(*ssl, new_fd);
    /* 建立 SSL 连接 */
    if (SSL_accept(*ssl) < 0)
    {
        perror("accept");
        close(new_fd);
        return -1;
    }

    return new_fd;
}

int ssl_connect(SSL_CTX *ctx, int *sockfd, SSL **ssl, char *port, char *addr)
{
    struct sockaddr_in dest;

    if (NULL == ctx || NULL == sockfd ||
            NULL == ssl || NULL == port || NULL == addr)
        return -1;

    /* 创建一个 socket 用于 tcp 通信 */
    if ((*sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Socket");
        return -1;
    }

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(port));
    if (inet_aton(addr, (struct in_addr *) &dest.sin_addr.s_addr) == 0)
    {
        perror(addr);
        return -1;
    }

    /* 连接服务器 */
    if (connect(*sockfd, (struct sockaddr *) &dest, sizeof(dest)) < 0)
    {
        perror("Connect ");
        return -1;
    }

    /* 基于 ctx 产生一个新的 SSL */
    *ssl = SSL_new(ctx);
    SSL_set_fd(*ssl, *sockfd);
    /* 建立 SSL 连接 */
    if (SSL_connect(*ssl) < 0)
    {
        ERR_print_errors_fp(stderr);
        return -1;
    }

    return 0;
}

int ssl_close(SSL_CTX *ctx, SSL *ssl, int listen_fd, int new_fd)
{
    if (NULL == ctx || NULL == ssl || listen_fd < 0 || new_fd < 0) return -1;

    /* 关闭 SSL 连接 */
    SSL_shutdown(ssl);
    /* 释放 SSL */
    SSL_free(ssl);
    /* 关闭 socket(服务端专用) */
    if (new_fd)
        close(new_fd);
    /* 关闭监听的 socket */
    close(listen_fd);
    /* 释放 CTX */
    SSL_CTX_free(ctx);

    return 0;
}

int disconnect(SockList *head, int fd)
{
    if (NULL == head || fd < 0) return -1;

    SockList *preNode, *curNode;

    preNode = head;
    curNode = head->next;
    while (curNode)
    {
        if (curNode->fd == fd)
        {
            printf("\n>>>>>>> disconnect with the fd %d from %s\n", curNode->fd, curNode->ip);
            preNode->next = curNode->next;
            SSL_shutdown(curNode->ssl);
            SSL_free(curNode->ssl);
            close(fd);
            free(curNode);
            break;
        }
        preNode = preNode->next;
        curNode = curNode->next;
    }

    return 0;
}

3、sslServer.c

#include "ssl.h"

#define MAXLEN 2048

int main(int argc, char *argv[])
{
    int listen_fd, new_fd, max_fd;
    int ret, recv_len;
    SSL_CTX *ctx;
    SSL *ssl;
    fd_set readfds;
    time_t now;
    char *pos;
    char recvBuf[MAXLEN];
    SockList head, *tmpNode;
    socklen_t cli_len = sizeof(struct sockaddr);
    struct timeval timeout;
    struct sockaddr_in cli;

    if (argc != 4)
    {
        printf("Please set the arguments as follow:\n");
        printf("%s <port> <certificate> <privateKey>\n", argv[0]);
        return -1;
    }

    signal(SIGPIPE, SIG_IGN);

    if (ssl_init(&ctx)) return -1;
    if (ssl_load(ctx, argv[2], argv[3])) return -1;
    if (createListen(&listen_fd, argv[1], NULL)) return -1;
    bzero(&head, sizeof(head));

    while (1)
    {
        FD_ZERO(&readfds);
        max_fd = listen_fd;
        FD_SET(listen_fd, &readfds);
        tmpNode = head.next;
        while (tmpNode)
        {
            max_fd = max_fd > tmpNode->fd ? max_fd : tmpNode->fd;
            FD_SET(tmpNode->fd, &readfds);
            tmpNode = tmpNode->next;
        }
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
        ret = select(max_fd + 1, &readfds, NULL, NULL, &timeout);
        if(ret <= 0)
            continue;
        else
        {
            if (FD_ISSET(listen_fd, &readfds))
            {
                new_fd = ssl_accept(ctx, listen_fd, &ssl);
                if (new_fd < 0) continue;

                tmpNode = (SockList *)malloc(sizeof(SockList));
                memset(tmpNode, 0, sizeof(SockList));
                tmpNode->fd = new_fd;
                tmpNode->ssl = ssl;
                bzero(&cli, sizeof(cli));
                getpeername(new_fd, (struct sockaddr *)&cli, &cli_len);
                strncpy(tmpNode->ip, inet_ntoa(cli.sin_addr), sizeof(tmpNode->ip));

                tmpNode->next = head.next;
                head.next = tmpNode;
            }

            tmpNode = head.next;
            while (tmpNode)
            {
                if (FD_ISSET(tmpNode->fd, &readfds))
                {
                    memset(recvBuf, 0, sizeof(recvBuf));
                    recv_len = SSL_read(tmpNode->ssl, recvBuf, sizeof(recvBuf));
                    if (recv_len <= 0)
                    {
                        ERR_print_errors_fp(stderr);
                        disconnect(&head, tmpNode->fd);
                    }
                    else
                    {
                        now = time(NULL);
                        pos = ctime(&now);
                        pos[strlen(pos)-1] = '\0';
                        printf("\n[%s] Recv Msg from Client %s, fd %d >>>>>>> %s\n",
                        pos, tmpNode->ip, tmpNode->fd, recvBuf);
                        printf("[%s] Input the Msg you want to Response <<<<<<< ", pos);
                        memset(recvBuf, 0, sizeof(recvBuf));
                        scanf("%s", recvBuf);
                        SSL_write(tmpNode->ssl, recvBuf, strlen(recvBuf));
                    }
                    break;
                }
                tmpNode = tmpNode->next;
            }
        }
    }

    /* 释放 CTX */
    SSL_CTX_free(ctx);
    return 0;
}

4、sslClient.c

#include "ssl.h"

#define MAXLEN 2048

int main(int argc, char *argv[])
{
    int sockfd;
    int len;
    SSL_CTX *ctx = NULL;
    SSL *ssl = NULL;
    char buf[MAXLEN];
    time_t now;
    char *pos;

    if (argc != 3)
    {
        printf("Please set the arguments as follow:\n");
        printf("%s <serverIP> <port>\n", argv[0]);
        return -1;
    }

    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx == NULL)
    {
        ERR_print_errors_fp(stdout);
        return -1;
    }
    if (ssl_connect(ctx, &sockfd, &ssl, argv[2], argv[1])) return -1;

    now = time(NULL);
    pos = ctime(&now);
    pos[strlen(pos)-1] = '\0';

    while (1)
    {

        bzero(buf, sizeof(buf));
        printf("[%s] Input the Msg send to Server <<<<<<< ", pos);
        scanf("%s", buf);
        len = SSL_write(ssl, buf, strlen(buf));
        if (len <= 0)
        {
            ERR_print_errors_fp(stderr);
            printf("Disconnect with the Server!\n");
            return -1;
        }
        bzero(buf, sizeof(buf));
        len = SSL_read(ssl, buf, sizeof(buf));
        if (len <= 0)
        {
            ERR_print_errors_fp(stderr);
            printf("Disconnect with the Server!\n");
            return -1;
        }

        now = time(NULL);
        pos = ctime(&now);
        pos[strlen(pos)-1] = '\0';
        printf("\n[%s] Recv Msg from Server >>>>>>> %s\n", pos, buf);
    }

    return 0;
}

二、编译程序

gcc sslServer.c api.c -o server -lssl
gcc sslClient.c api.c -o client -lssl

三、生成私有密钥文件与证书文件

openssl genrsa -out privkey.pem 2048
openssl req -new -x509 -key privkey.pem -out cacert.pem -days 3650

第二步生成证书文件cacert.pem的时候,按提示依次输入国家(两位英文字母,如中国“CN”)、省份名称(如“Beijing”或“北京”)、城市名称(如“Beijing”或“北京”)、公司名称(如“Haier”或“海尔”)、部门名称(如“R&D”或“研发部”)、你的名字(如“Tom”或“汤姆”)以及你的邮箱地址。以上数据纯粹为了接收方方便辨别证书提供方用,所以不必太过真实。

四、运行程序

./server 8888 cacert.pem privkey.pem
./client 127.0.0.1 8888

五、以上就是简易版SSL加密聊天程序的全部内容,接下来的工作就是留待大家通过该程序慢慢研究SSL通信的实现机制了,如有问题,欢迎留言。

除非注明,文章均为CppLive 编程在线原创,转载请注明出处,谢谢。

本文地址:https://www.cpplive.com/html/1397.html

这里因为你的留言而存在!!!

You must be logged in to post a comment.