使用 OpenSSL 进行客户端身份验证

问题描述 投票:0回答:1

我正在尝试编写一个客户端和服务器,在服务器对客户端进行身份验证后交换问候。服务器正在侦听端口 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:

我不知道为什么握手失败。任何帮助将不胜感激。预先感谢。

c authentication sockets openssl ssl-certificate
1个回答
0
投票

服务器无法验证您的客户端证书,因为它无法从

ca.crt
目录加载
/certs/
,除非该目录经过哈希处理。 一个更简单的解决方案是从单个文件加载 CA:

SSL_CTX_load_verify_locations(ctx, "/certs/ca.crt", NULL);

或者重新散列您的

/certs
目录,以便 OpenSSL 识别其中的 CA 文件。 确保每一方都有正确的 CA 签署另一方的证书。

© www.soinside.com 2019 - 2024. All rights reserved.