我正在尝试编写一个客户端和服务器,在服务器对客户端进行身份验证后交换问候。服务器正在侦听端口 12345。
这是服务器端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/rand.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define PORT 12345
void initialize_openssl() {
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
void cleanup_openssl() {
EVP_cleanup();
}
SSL_CTX* create_server_context() {
const SSL_METHOD *method = TLS_server_method();
SSL_CTX *ctx = SSL_CTX_new(method);
if (!ctx) {
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
void configure_server_context(SSL_CTX *ctx) {
/* Load server certificate and private key */
if (SSL_CTX_use_certificate_file(ctx, "/certs/server.crt", SSL_FILETYPE_PEM) <= 0) {
perror("Unable to load server certificate");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx, "/certs/server.key", SSL_FILETYPE_PEM) <= 0) {
perror("Unable to load server private key");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
/* Verify private key matches the certificate */
if (!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr, "Private key does not match the certificate public key\n");
exit(EXIT_FAILURE);
}
/* Load the directory containing client certificates */
if (SSL_CTX_load_verify_locations(ctx, NULL, "/certs/") <= 0) {
perror("SSL_CTX_load_verify_locations failed for /certs directory");
exit(1);
}
/* Set the verification mode to allow client certificates */
/*SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify_depth(ctx, 4);
}
void handle_client(SSL *ssl) {
char buf[1024];
int bytes;
/* Receive data from the client */
bytes = SSL_read(ssl, buf, sizeof(buf));
if (bytes < 0) {
perror("SSL_read failed");
ERR_print_errors_fp(stderr);
} else {
buf[bytes] = 0;
printf("Received message: %s\n", buf);
}
/* Send a response to the client */
const char *response = "Hello from server!";
SSL_write(ssl, response, strlen(response));
}
int main() {
int sockfd, client_sock;
struct sockaddr_in addr;
SSL_CTX *ctx;
SSL *ssl;
initialize_openssl();
ctx = create_server_context();
configure_server_context(ctx);
/* Create server socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("Unable to create socket");
exit(EXIT_FAILURE);
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("Unable to bind");
exit(EXIT_FAILURE);
}
if (listen(sockfd, 1) == -1) {
perror("Unable to listen");
exit(EXIT_FAILURE);
}
printf("Waiting for connections on port %d...\n", PORT);
/* Accept client connection */
client_sock = accept(sockfd, NULL, NULL);
if (client_sock == -1) {
perror("Unable to accept connection");
exit(EXIT_FAILURE);
}
/* Create SSL object and associate it with the client socket */
ssl = SSL_new(ctx);
SSL_set_fd(ssl, client_sock);
if (SSL_accept(ssl) == -1) {
perror("SSL_accept failed");
ERR_print_errors_fp(stderr);
} else {
printf("SSL handshake successful\n");
/* Extract the client certificate */
X509 *client_cert = SSL_get_peer_certificate(ssl);
if (client_cert == NULL) {
fprintf(stderr, "Client did not provide a certificate\n");
SSL_shutdown(ssl);
SSL_free(ssl);
close(client_sock);
return 0;
}
handle_client(ssl);
X509_free(client_cert); /* Free the client certificate after use */
}
/* Close the server socket */
close(client_sock);
close(sockfd);
SSL_CTX_free(ctx);
cleanup_openssl();
return 0;
}
这是客户端尝试与服务器建立握手但失败了:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/rand.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define SERVER "10.32.151.173"
#define PORT 12345
void initialize_openssl() {
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
void cleanup_openssl() {
EVP_cleanup();
}
SSL_CTX* create_client_context() {
const SSL_METHOD *method = TLS_client_method();
SSL_CTX *ctx = SSL_CTX_new(method);
if (!ctx) {
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
void configure_client_context(SSL_CTX *ctx) {
/* Load the client's certificate and private key */
if (SSL_CTX_use_certificate_file(ctx, "/certs/client.crt", SSL_FILETYPE_PEM) <= 0) {
perror("Unable to load client certificate");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx, "/certs/client.key", SSL_FILETYPE_PEM) <= 0) {
perror("Unable to load client private key");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
/* Verify the client's private key matches the certificate */
if (!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr, "Private key does not match the certificate public key\n");
exit(EXIT_FAILURE);
}
/* Load the server's certificate to verify the server's identity */
if (SSL_CTX_load_verify_locations(ctx, NULL, "/certs/") <= 0) {
perror("SSL_CTX_load_verify_locations failed for client certs directory");
exit(1);
}
/* Set the verification mode to verify server certificates */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
SSL_CTX_set_verify_depth(ctx, 4);
}
void communicate_with_server(SSL *ssl) {
char *msg = "Hello from client!";
SSL_write(ssl, msg, strlen(msg));
char buf[1024];
int bytes = SSL_read(ssl, buf, sizeof(buf));
if (bytes < 0) {
perror("SSL_read failed");
ERR_print_errors_fp(stderr);
} else {
buf[bytes] = 0;
printf("Received from server: %s\n", buf);
}
}
int main() {
int sockfd;
struct sockaddr_in addr;
SSL_CTX *ctx;
SSL *ssl;
initialize_openssl();
ctx = create_client_context();
configure_client_context(ctx);
/* Create socket and connect to the server */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("Unable to create socket");
exit(EXIT_FAILURE);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr(SERVER);
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("Unable to connect");
exit(EXIT_FAILURE);
}
/* Create SSL object and associate it with the socket */
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
if (SSL_connect(ssl) == -1) {
perror("SSL_connect failed");
ERR_print_errors_fp(stderr);
} else {
printf("SSL handshake successful\n");
/* Perform communication with the server */
communicate_with_server(ssl);
}
/* Close the SSL connection and the socket */
SSL_shutdown(ssl);
SSL_free(ssl);
close(sockfd);
SSL_CTX_free(ctx);
cleanup_openssl();
return 0;
}
这就是我生成证书的方式:
# Generate the CA certificate
openssl genpkey -algorithm RSA -out ca.key
openssl req -new -x509 -key ca.key -out ca.crt -days 365
# Generate the server certificate and key
openssl genpkey -algorithm RSA -out server.key
openssl req -new -key server.key -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
# Generate the client certificate and key
openssl genpkey -algorithm RSA -out client.key
openssl req -new -key client.key -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365
在服务器上的
/certs
目录下,我有以下内容:
ca.crt
ca.key
ca.srl
客户端.crt
客户端.csr
客户端密钥
服务器.crt
服务器.csr
服务器.key
在客户端的
/certs
目录下,我有以下内容:
客户端.crt
客户端.csr
客户端.key
当服务器代码等待请求时,我执行客户端代码并看到以下内容: 在服务器上:
Waiting for connections on port 12345...
SSL_accept failed: Error 0
00000001:error:0A000086:SSL routines:(unknown function):certificate verify failed:ssl/statem/statem_srvr.c:3523:
在客户端:
SSL handshake successful
Received from server:
我不知道为什么握手失败。任何帮助将不胜感激。预先感谢。
服务器无法验证您的客户端证书,因为它无法从
ca.crt
目录加载 /certs/
,除非该目录经过哈希处理。
一个更简单的解决方案是从单个文件加载 CA:
SSL_CTX_load_verify_locations(ctx, "/certs/ca.crt", NULL);
或者重新散列您的
/certs
目录,以便 OpenSSL 识别其中的 CA 文件。
确保每一方都有正确的 CA 签署另一方的证书。