关于c语言使用openssl的实现websocket底层协议功能

1. 获取安装包。
wget https://www.openssl.org/source/openssl-1.1.1c.tar.gz
2. 解压。
tar -xzvf openssl-1.1.1c.tar.gz
3. 配置。
./Configure --help
# 配置参数选项
--openssldir=OPENSSLDIR #配置文件目录,默认是/usr/local/ssl
--prefix=DIR #安装目录,默认为 OPENSSLDIR 目录
[no-]threads #是否编译支持多线程的库。默认支持
[no-]shared #是否生成动态连接库。默认生成

默认不选择,让他自动安装系统相关目录


4. 编译和安装。
make && make install

 

5 tcp.h

#pragma once
extern int passive_server(int port,int queue);

tcp.c

#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>

int passive_server(int port,int queue)
{
    ///定义sockfd
    int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);

    ///定义sockaddr_in
    struct sockaddr_in server_sockaddr;
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(port);
    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    ///bind,成功返回0,出错返回-1
    if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)
    {
        perror("bind");
        exit(1);
    }
    ///listen,成功返回0,出错返回-1
    if(listen(server_sockfd,queue) == -1)
    {
        perror("listen");
        exit(1);
    }
    printf("监听%d端口\n",port);
    return server_sockfd;
}

main.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include "tcp.h"

#define BUFFER_SIZE 1024
#define RESPONSE_HEADER_LEN_MAX 1024
#define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
/*-------------------------------------------------------------------
0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+
--------------------------------------------------------------------*/
typedef struct _frame_head {
    char fin;
    char opcode;
    char mask;
    unsigned long long payload_length;
    char masking_key[4];
}frame_head;

int base64_encode(char *in_str, int in_len, char *out_str)
{
    BIO *b64, *bio;
    BUF_MEM *bptr = NULL;
    size_t size = 0;

    if (in_str == NULL || out_str == NULL)
        return -1;

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);

    BIO_write(bio, in_str, in_len);
    BIO_flush(bio);

    BIO_get_mem_ptr(bio, &bptr);
    memcpy(out_str, bptr->data, bptr->length);
    out_str[bptr->length-1] = '\0';
    size = bptr->length;

    BIO_free(bio);
    return size;
}

/**
 * @brief _readline
 * read a line string from all buffer
 * @param allbuf
 * @param level
 * @param linebuf
 * @return
 */
int _readline(char* allbuf,int level,char* linebuf)
{
    int len = strlen(allbuf);
    for (;level<len;++level)
    {
        if(allbuf[level]=='\r' && allbuf[level+1]=='\n')
            return level+2;
        else
            *(linebuf++) = allbuf[level];
    }
    return -1;
}

int shakehands(int cli_fd)
{
    //next line's point num
    int level = 0;
    //all request data
    char buffer[BUFFER_SIZE];
    //a line data
    char linebuf[256];
    //Sec-WebSocket-Accept
    char sec_accept[32];
    //sha1 data
    unsigned char sha1_data[SHA_DIGEST_LENGTH+1]={0};
    //reponse head buffer
    char head[BUFFER_SIZE] = {0};

    if (read(cli_fd,buffer,sizeof(buffer))<=0)
        perror("read");
    printf("request\n");
    printf("%s\n",buffer);

    do {
        memset(linebuf,0,sizeof(linebuf));
        level = _readline(buffer,level,linebuf);
        //printf("line:%s\n",linebuf);

        if (strstr(linebuf,"Sec-WebSocket-Key")!=NULL)
        {
            strcat(linebuf,GUID);
//            printf("key:%s\nlen=%d\n",linebuf+19,strlen(linebuf+19));
            SHA1((unsigned char*)&linebuf+19,strlen(linebuf+19),(unsigned char*)&sha1_data);
//            printf("sha1:%s\n",sha1_data);
            base64_encode(sha1_data,strlen(sha1_data),sec_accept);
//            printf("base64:%s\n",sec_accept);
            /* write the response */
            sprintf(head, "HTTP/1.1 101 Switching Protocols\r\n" \
                          "Upgrade: websocket\r\n" \
                          "Connection: Upgrade\r\n" \
                          "Sec-WebSocket-Accept: %s\r\n" \
                          "\r\n",sec_accept);

            printf("response\n");
            printf("%s",head);
            if (write(cli_fd,head,strlen(head))<0)
                perror("write");

            break;
        }
    }while((buffer[level]!='\r' || buffer[level+1]!='\n') && level!=-1);
    return 0;
}

int recv_frame_head(int fd,frame_head* head)
{
    char one_char;
    /*read fin and op code*/
    if (read(fd,&one_char,1)<=0)
    {
        perror("read fin");
        return -1;
    }
    head->fin = (one_char & 0x80) == 0x80;
    head->opcode = one_char & 0x0F;
    if (read(fd,&one_char,1)<=0)
    {
        perror("read mask");
        return -1;
    }
    head->mask = (one_char & 0x80) == 0X80;

    /*get payload length*/
    head->payload_length = one_char & 0x7F;

    if (head->payload_length == 126)
    {
        char extern_len[2];
        if (read(fd,extern_len,2)<=0)
        {
            perror("read extern_len");
            return -1;
        }
        head->payload_length = (extern_len[0]&0xFF) << 8 | (extern_len[1]&0xFF);
    }
    else if (head->payload_length == 127)
    {
        char extern_len[8],temp;
        int i;
        if (read(fd,extern_len,8)<=0)
        {
            perror("read extern_len");
            return -1;
        }
        for(i=0;i<4;i++)
        {
            temp = extern_len[i];
            extern_len[i] = extern_len[7-i];
            extern_len[7-i] = temp;
        }
        memcpy(&(head->payload_length),extern_len,8);
    }

    /*read masking-key*/
    if (read(fd,head->masking_key,4)<=0)
    {
        perror("read masking-key");
        return -1;
    }

    return 0;
}

/**
 * @brief umask
 * xor decode
 * @param data
 * @param len
 * @param mask
 */
void umask(char *data,int len,char *mask)
{
    int i;
    for (i=0;i<len;++i)
        *(data+i) ^= *(mask+(i%4));
}

int send_frame_head(int fd,frame_head* head)
{
    char *response_head;
    int head_length = 0;
    if(head->payload_length<126)
    {
        response_head = (char*)malloc(2);
        response_head[0] = 0x81;
        response_head[1] = head->payload_length;
        head_length = 2;
    }
    else if (head->payload_length<0xFFFF)
    {
        response_head = (char*)malloc(4);
        response_head[0] = 0x81;
        response_head[1] = 126;
        response_head[2] = (head->payload_length >> 8 & 0xFF);
        response_head[3] = (head->payload_length & 0xFF);
        head_length = 4;
    }
    else
    {
        //no code
        response_head = (char*)malloc(12);
//        response_head[0] = 0x81;
//        response_head[1] = 127;
//        response_head[2] = (head->payload_length >> 8 & 0xFF);
//        response_head[3] = (head->payload_length & 0xFF);
        head_length = 12;
    }

    if(write(fd,response_head,head_length)<=0)
    {
        perror("write head");
        return -1;
    }

    free(response_head);
    return 0;
}

int main()
{
    int ser_fd = passive_server(6200,20);


    struct sockaddr_in client_addr;
    socklen_t addr_length = sizeof(client_addr);
    int conn = accept(ser_fd,(struct sockaddr*)&client_addr, &addr_length);

    shakehands(conn);

    int count = 10;
    while (count--)
    {
        frame_head head;
        int rul = recv_frame_head(conn,&head);
        if (rul < 0)
            break;
        printf("fin=%d\nopcode=0x%X\nmask=%d\npayload_len=%llu\n",head.fin,head.opcode,head.mask,head.payload_length);

        //echo head
        send_frame_head(conn,&head);
        //read payload data
        char payload_data[1024] = {0};
        int size = 0;
        do {
            int rul;
            rul = read(conn,payload_data,1024);
            if (rul<=0)
                break;
            size+=rul;

            umask(payload_data,size,head.masking_key);
            printf("recive:%s",payload_data);

            //echo data
            if (write(conn,payload_data,rul)<=0)
                break;
        }while(size<head.payload_length);
        printf("\n-----------\n");

    }

    close(conn);
    close(ser_fd);
}

6 编写make文件,此文件执行当前目录下所有进行编译,只能所有文件只能由一个main函数

#####################################################################
## file        : test makefile for build current dir .c            ##
## author      : liujifeng                                         ##
## date-time   : 12/14/2019                                        ##
#####################################################################

#此功能是编译当前所有文件的c或cpp的文件。
CC      = gcc
CPP     = g++
RM      = rm -rf

#PROJECT_ROOT_PATH := /home/swoole/test/project
#mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))  #获取当前正在执行的makefile的绝对路径
#cur_makefile_path := patsubst$(%/, %, dir $(mkfile_patch))  #获取当前正在执行的makefile的绝对目录

##	@echo mkfile_path=$(mkfile_path)
##	@echo cur_makefile_path=$(cur_makefile_path)
## debug flag
DBG_ENABLE   = 1

## source file path
        SRC_PATH   := .

## target exec file name
TARGET     := main

## get all source files
SRCS         += $(wildcard $(SRC_PATH)/*.c)

## all .o based on all .c
OBJS        := $(SRCS:.c=.o)

## need libs, add at here
#LIBS := pthread mysqlclient hiredis websockets ssl crypto
LIBS := pthread ssl crypto

## used headers  file path
#INCLUDE_PATH := . /usr/local/include/  /usr/include/ /usr/local/include/mysql/ /usr/local/include/libwebsockets
INCLUDE_PATH := . /usr/local/include/  /usr/include/
## used include librarys file path
#LIBRARY_PATH := . /usr/local/lib  /usr/local/mysql/lib
LIBRARY_PATH := . /usr/local/lib
## debug for debug info, when use gdb to debug
ifeq (1, ${DBG_ENABLE})
	CFLAGS += -D_DEBUG -O0 -g -DDEBUG=1
endif

## get all include path
CFLAGS  += $(foreach dir, $(INCLUDE_PATH), -I$(dir))

## get all library path
LDFLAGS += $(foreach lib, $(LIBRARY_PATH), -L$(lib))

## get all librarys
LDFLAGS += $(foreach lib, $(LIBS), -l$(lib))

all: clean build
build:
	$(CC) -c $(CFLAGS) $(SRCS)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS)
	$(RM) $(OBJS)

clean:
	$(RM) $(OBJS) $(TARGET)

7 测试验证

 使用webscket的工具,进行向6200端口发送消息就好了。

 

 

C语言使用OpenSSL库可以实现对加密、解密、数字签名、证书生成等功能的支持。以下是C语言使用OpenSSL的基本流程: 1. 引入头文件:在代码中引入OpenSSL库的头文件,如<openssl/evp.h>、<openssl/rsa.h>等。 2. 初始化:在程序开始时调用OpenSSL库的初始化函数,如OPENSSL_init_crypto()。 3. 创建上下文:根据需要创建相应的上下文,如EVP_CIPHER_CTX* ctx,RSA* rsa等。 4. 设置参数:设置加密算法、密钥、初始向量等参数。 5. 执行操作:调用相应的OpenSSL函数执行加密、解密、数字签名等操作。 6. 清理:在程序结束时调用OpenSSL库的清理函数,如EVP_CIPHER_CTX_cleanup()。 下面是一个使用OpenSSL库进行对称加密的示例代码: ```c #include <stdio.h> #include <string.h> #include <openssl/evp.h> #define BUF_SIZE 1024 int main() { // 初始化OpenSSLOPENSSL_init_crypto(0, NULL); // 创建上下文 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); // 设置加密算法和密钥 unsigned char key[] = "0123456789abcdef"; unsigned char iv[] = "0123456789abcdef"; EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv); // 执行加密操作 unsigned char plaintext[BUF_SIZE] = "Hello, world!"; unsigned char ciphertext[BUF_SIZE + EVP_MAX_BLOCK_LENGTH]; int len; EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, strlen(plaintext)); int ciphertext_len = len; EVP_EncryptFinal_ex(ctx, ciphertext + len, &len); ciphertext_len += len; // 输出加密结果 printf("Plaintext: %s\n", plaintext); printf("Ciphertext: "); for (int i = 0; i < ciphertext_len; i++) { printf("%02x", ciphertext[i]); } printf("\n"); // 清理 EVP_CIPHER_CTX_free(ctx); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值