90行代码实现C语言版https服务器,基于openssl
为了实现C语言版的https服务器(ubuntu linux),我查了很多资料,最后发现是非常简单好写的,在此向大家做个分享。
首先大致了解一下https的工作原理,下面这张图解释得非常好。
而openssl又对上面这张图里的一些功能进行了合并封装,让我们用起来更加方便简洁。
首先我们需要安装openssl开发包:
apt install openssl libssl-dev
然后就可以编译下面的mini版https server了 (https.c)
/* gcc https.c -lssl -lcrypto -o https */ #include <stdio.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <sys/socket.h> #include <arpa/inet.h> #include <openssl/ssl.h> #include <openssl/err.h> #define BufSize 30 * 1024 int create_socket_listen(int port) { int s; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("Unable to create socket"); exit(EXIT_FAILURE); } if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("Unable to bind"); exit(EXIT_FAILURE); } if (listen(s, 1) < 0) { perror("Unable to listen"); exit(EXIT_FAILURE); } return s; } SSL_CTX* initSSL(const char* cert, const char* key) { //todo 可能存在泄露 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, 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 单独表示 V2 或 V3标准 */ ctx = SSL_CTX_new(SSLv23_server_method()); if (ctx == NULL) { ERR_print_errors_fp(stdout); return NULL; } /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */ if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); return NULL; } /* 载入用户私钥 */ if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); return NULL; } /* 检查用户私钥是否正确 */ if (!SSL_CTX_check_private_key(ctx)) { ERR_print_errors_fp(stdout); return NULL; } return ctx; } int main(int argc, char** argv) { int port = 5555; int sock; SSL_CTX* ctx; char buf[BufSize]; /* Ignore broken pipe signals */ signal(SIGPIPE, SIG_IGN); ctx = initSSL("./fullchain.pem", "./privkey.pem"); if (ctx == NULL) { return 0; } sock = create_socket_listen(port); /* Handle connections */ while (1) { struct sockaddr_in addr; unsigned int len = sizeof(addr); SSL* ssl; const char reply[] = "HTTP/1.1 200 OK Date: Fri, 22 May 2009 06:07:21 GMT Content-Type: text/html; charset=UTF-8 <html><head></head><body>hello world</body></html>"; int client = accept(sock, (struct sockaddr*)&addr, &len); if (client < 0) { perror("Unable to accept"); exit(EXIT_FAILURE); } ssl = SSL_new(ctx); SSL_set_fd(ssl, client); if (SSL_accept(ssl) <= 0) { ERR_print_errors_fp(stderr); goto end; } int size = SSL_read(ssl, buf, BufSize); printf("ssl_read(%d): %s ", size, buf); SSL_write(ssl, reply, strlen(reply)); end: SSL_shutdown(ssl); SSL_free(ssl); close(client); } close(sock); SSL_CTX_free(ctx); }
编译命令:
gcc https.c -lssl -lcrypto -o https
运行前需要准备好证书fullchain.pem和私钥privkey.pem两个文件,运行:
./https