文章目录
- 前言
- 一、客户端实现
- 二、服务端实现
- 总结
前言
本文是使用openssl111w实现的简单客户端和服务端,主要用于观察openssl一个记录层数据包的大小。
一、客户端实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define PORT 8000
#define SERVER_IP "127.0.0.1"
#define SERVER_KEY "./rsa_cert/serverkey.pem"
#define SERVER_CERT "./rsa_cert/servercert.pem"
#define CA_CERT "./rsa_cert/cacert.pem"
// 初始化OpenSSL库
void init_openssl() {
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
SSL_library_init();
}
//创建原始套接字
int create_connect_fd()
{
struct sockaddr_in serv_addr;
int client_fd;
// 创建socket
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 使用inet_pton转换IP字符串为网络字节序
if(inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr)<=0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// 连接服务器
if (connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
return client_fd;
}
//创建ssl上下文
SSL_CTX* create_ssl_ctx() {
const SSL_METHOD *method;
SSL_CTX *ctx;
method = TLSv1_2_client_method();
ctx = SSL_CTX_new(method);
if (!ctx) {
printf("Unable to create SSL context");
ERR_print_errors_fp(stderr);
return NULL;
}
// 加载服务器证书和私钥
if (SSL_CTX_use_certificate_file(ctx, SERVER_CERT, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
printf("load server cert failed\n");
return NULL;
}
if (SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
printf("load server key failed\n");
return NULL;
}
// 检查私钥和证书是否匹配
if (!SSL_CTX_check_private_key(ctx)) {
printf("cert and key mismatch\n");
return NULL;
}
//加载CA证书
if (!SSL_CTX_load_verify_locations(ctx, CA_CERT, NULL)) {
printf("load ca cert failed\n");
return NULL;
}
//必须验证服务端证书,不设置则不验证
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
return ctx;
}
int main()
{
SSL_CTX *ctx;
SSL *ssl;
int client_fd,send_len;
char buf[102400] = {0};
/* 初始化SSL库 */
init_openssl();
/* 创建SSL_CTX结构体 */
ctx = create_ssl_ctx();
if(ctx == NULL)
{
printf("create ssl ctx failed\n");
return -1;
}
/* TCP协议连接到服务器 */
client_fd = create_connect_fd();
if(client_fd < 0)
{
printf("create client fd failed\n");
return -1;
}
//创建ssl对象
ssl = SSL_new(ctx);
SSL_set_fd(ssl, client_fd);
//ssl握手
if (SSL_connect(ssl) == -1) {
ERR_print_errors_fp(stderr);
return -1;
}
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
/* 发送数据 */
send_len = SSL_write(ssl, buf, sizeof(buf));
if(send_len < 0)
{
printf("SSL_write failed\n");
}
else
{
printf("write len:%d\n", send_len);
}
SSL_shutdown(ssl);
SSL_free(ssl);
close(client_fd);
SSL_CTX_free(ctx);
return 0;
}
二、服务端实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define PORT 8000
#define SERVER_KEY "./rsa_cert/serverkey.pem"
#define SERVER_CERT "./rsa_cert/servercert.pem"
#define CA_CERT "./rsa_cert/cacert.pem"
// 初始化OpenSSL库
void init_openssl() {
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
SSL_library_init();
}
// 创建ssl上下文
SSL_CTX* create_ssl_ctx() {
const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_ssl_algorithms();
method = TLSv1_2_server_method();
ctx = SSL_CTX_new(method);
if (!ctx) {
printf("Unable to create SSL context");
ERR_print_errors_fp(stderr);
return NULL;
}
// 加载服务器证书和私钥
if (SSL_CTX_use_certificate_file(ctx, SERVER_CERT, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
printf("load server cert failed\n");
return NULL;
}
if (SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
printf("load server key failed\n");
return NULL;
}
// 检查私钥和证书是否匹配
if (!SSL_CTX_check_private_key(ctx)) {
printf("cert and key mismatch\n");
return NULL;
}
if (!SSL_CTX_load_verify_locations(ctx, CA_CERT, NULL)) {
printf("load ca cert failed\n");
return NULL;
}
//必须验证服务端证书,不设置则不验证
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
return ctx;
}
//创建监听套接字
int create_listenfd()
{
int listen_fd;
struct sockaddr_in server_addr;
int opt = 1;
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
printf("socket failed\n");
return -1;
}
// 设置socket选项,允许重复使用地址
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,&opt, sizeof(opt)))
{
printf("setsockopt failed\n");
return -1;
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// 绑定socket到指定地址和端口
if (bind(listen_fd, (struct sockaddr *)&server_addr,
sizeof(server_addr))<0)
{
printf("bind failed\n");
return -1;
}
// 开始监听,最大等待队列长度为5
if (listen(listen_fd, 5) < 0)
{
printf("listen failed\n");
return -1;
}
return listen_fd;
}
int main() {
int listen_fd,con_fd,recv_len;
char buf[20480] = {0};
init_openssl();
//创建ssl上下文
SSL_CTX *ctx = create_ssl_ctx();
if(ctx == NULL)
{
printf("create ssl ctx failed\n");
return -1;
}
//创建监听套接字
listen_fd = create_listenfd();
if(listen_fd < 0)
{
printf("create listen fd failed\n");
return -1;
}
while(1) {
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
SSL *ssl;
//等待客户端发起tcp链接
con_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len);
if(con_fd < 0)
{
printf("create connetct fd failed\n");
return -1;
}
printf("create connect fd success\n");
ssl = SSL_new(ctx);
SSL_set_fd(ssl, con_fd);
//等待ssl握手
if (SSL_accept(ssl) == -1) {
ERR_print_errors_fp(stderr);
return -1;
}
printf("SSL Connection established\n");
for (;;) {
//读取ssl数据
recv_len = SSL_read(ssl, buf, sizeof(buf));
if (recv_len > 0)
printf("Received_len:%d \n", recv_len);
else {
ERR_print_errors_fp(stderr);
break;
}
}
SSL_shutdown(ssl);
SSL_free(ssl);
close(con_fd);
}
SSL_CTX_free(ctx);
close(listen_fd);
return 0;
}
总结
服务端ssl_read能够接受的最大数据包是16384(2^14)字节,也就是一个记录层的最大数据。