babon
babon

Reputation: 3774

Client authentication using OpenSSL

I am trying to write a client and server that exchange greetings after the server authenticates the client. The server is listening on port 12345.

Here is the server side:

#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;
}

And here is the client which tries to establish a handshake with the server but fails:

#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;
}

This is how I generated the certificates:

# 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

Under the /certs directory on the server, I have the following: ca.crt ca.key ca.srl client.crt client.csr client.key server.crt server.csr server.key

Under the /certs directory on the client, I have the following: client.crt client.csr client.key

With the server code waiting for a request, I execute the client code and see the following: On the server:

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:

On the client:

SSL handshake successful
Received from server:

I am not sure why the handshake fails. Any help would be appreciated. Thanks in advance.

Upvotes: 1

Views: 31

Answers (1)

Alon Alush
Alon Alush

Reputation: 1010

Server is failing to verify your client certificate because it can’t load ca.crt from the /certs/ directory unless that directory is hashed. An easier solution is to load the CA from a single file:

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

Or rehash your /certs directory so that OpenSSL recognizes the CA file within it. Make sure each side has the correct CA that signed the other side’s certificate.

Upvotes: 0

Related Questions