Files
TLS/tls_server.cpp
huanglinhuan 63273630b3 first commit
2025-10-09 10:05:40 +08:00

225 lines
6.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <iostream>
#include <string>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <vector>
#include <netinet/tcp.h>
const int PORT = 7271;
const char* SERVER_CERT_FILE = "server.crt";
const char* SERVER_KEY_FILE = "server.key";
const char* CA_CERT_FILE = "ca.crt";
volatile sig_atomic_t server_running = 1;
void signal_handler(int sig) {
std::cout << "\n收到信号 " << sig << ",正在关闭服务器..." << std::endl;
server_running = 0;
}
void print_openssl_errors() {
BIO* bio = BIO_new_fp(stderr, BIO_NOCLOSE);
ERR_print_errors(bio);
BIO_free(bio);
}
void init_openssl() {
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
void cleanup_openssl() {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_cleanup();
#endif
}
SSL_CTX* create_context() {
const SSL_METHOD* method = TLS_server_method();
SSL_CTX* ctx = SSL_CTX_new(method);
if (!ctx) {
std::cerr << "无法创建SSL上下文" << std::endl;
print_openssl_errors();
exit(EXIT_FAILURE);
}
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
return ctx;
}
int verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
if (!preverify_ok) {
int err = X509_STORE_CTX_get_error(ctx);
if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
return 1;
}
}
return preverify_ok;
}
void configure_context(SSL_CTX* ctx) {
if (SSL_CTX_use_certificate_file(ctx, SERVER_CERT_FILE, SSL_FILETYPE_PEM) <= 0) {
std::cerr << "无法加载服务器证书" << std::endl;
print_openssl_errors();
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY_FILE, SSL_FILETYPE_PEM) <= 0) {
std::cerr << "无法加载服务器私钥" << std::endl;
print_openssl_errors();
exit(EXIT_FAILURE);
}
if (!SSL_CTX_check_private_key(ctx)) {
std::cerr << "证书和私钥不匹配" << std::endl;
exit(EXIT_FAILURE);
}
if (SSL_CTX_load_verify_locations(ctx, CA_CERT_FILE, nullptr) <= 0) {
std::cerr << "无法加载CA证书" << std::endl;
print_openssl_errors();
exit(EXIT_FAILURE);
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
SSL_CTX_set_verify_depth(ctx, 4);
// 关键优化简化SSL选项
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES128-GCM-SHA256");
}
void graceful_ssl_close(SSL* ssl, int fd) {
if (!ssl || fd < 0) return;
// 简化的关闭逻辑
SSL_shutdown(ssl);
SSL_free(ssl);
close(fd);
}
void handle_connection(SSL* ssl, int client_fd, const char* client_ip, uint16_t client_port) {
std::cout << "\n处理连接 " << client_ip << ":" << client_port << std::endl;
std::cout << "协议: " << SSL_get_version(ssl) << std::endl;
// 验证客户端证书
X509* client_cert = SSL_get_peer_certificate(ssl);
if (!client_cert) {
std::cerr << "客户端未提供证书" << std::endl;
graceful_ssl_close(ssl, client_fd);
return;
}
X509_free(client_cert);
// 设置TCP_NODELAY
int optval = 1;
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
// 设置60秒读取超时
struct timeval timeout = {60, 0};
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
std::cout << "等待客户端数据..." << std::endl;
// 读取客户端数据
char buffer[4096];
int bytes = SSL_read(ssl, buffer, sizeof(buffer));
if (bytes > 0) {
std::cout << "收到数据: " << bytes << "字节" << std::endl;
// 发送响应
const char* response =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n"
"\r\n"
"Hello from TLS server!";
SSL_write(ssl, response, strlen(response));
std::cout << "已发送响应" << std::endl;
} else if (bytes == 0) {
std::cout << "客户端关闭连接" << std::endl;
} else {
std::cout << "客户端未发送数据(超时)" << std::endl;
}
graceful_ssl_close(ssl, client_fd);
}
int main() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
init_openssl();
SSL_CTX* ctx = create_context();
configure_context(ctx);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
int optval = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(sockfd, 10) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
std::cout << "服务器监听端口 " << PORT << std::endl;
while (server_running) {
sockaddr_in client_addr{};
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(sockfd, (sockaddr*)&client_addr, &client_len);
if (client_fd < 0) {
if (server_running) perror("accept");
continue;
}
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
uint16_t client_port = ntohs(client_addr.sin_port);
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, client_fd);
if (SSL_accept(ssl) <= 0) {
std::cerr << "SSL握手失败: ";
print_openssl_errors();
SSL_free(ssl);
close(client_fd);
continue;
}
handle_connection(ssl, client_fd, client_ip, client_port);
}
close(sockfd);
SSL_CTX_free(ctx);
cleanup_openssl();
return 0;
}