2
votes

I'm going to try TLS with mutual authentication using openssl.

However, as shown in the output results below, the client can receive a server certificate and output it, but the server has not received the client certificate.

The details of my work are as follows.

  1. Server and client certificate generation (without certificate signing through CA, just self-signing)

(1) Generating the server key and certificate.

$ openssl genrsa -des3 -out server.key 2048

$ openssl req -new -key server.key -out server.csr

$ cp server.key server.key.origin

$ openssl rsa -in server.key.origin -out server.key

$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

(2) Generating the client key and certificate. $ openssl genrsa -des3 -out client.key 2048

$ openssl req -new -key client.key -out client.csr

$ cp client.key client.key.origin

$ openssl rsa -in client.key.origin -out client.key

$ openssl x509 -req -days 365 -in client.csr -signkey client.key -out client.crt

// Server.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define HOME "./"
#define CERTF HOME "server.crt"
#define KEYF HOME "server.key"

#define CHK_NULL(x) if((x) == NULL) exit(1);
#define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
#define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }

int main(void) {
int err;
    int listen_sd;
    int sd;
    struct sockaddr_in sa_serv;
    struct sockaddr_in sa_cli;
    size_t client_len;

    SSL_CTX  *ctx;
    SSL    *ssl;
    X509                *client_cert;
    char                *str;
    char                buf[4096];
    SSL_METHOD  *meth;

    SSL_load_error_strings();
    SSLeay_add_ssl_algorithms();
    meth = TLSv1_2_server_method();
    ctx = SSL_CTX_new(meth);

    if(!ctx) {
        ERR_print_errors_fp(stderr);
        exit(2);
    }

    if(SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(3);
    }

    if(SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(4);
    }

    if(!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr, "Private key does not match the certificate public keyn");
        exit(5);
    }

    listen_sd = socket(AF_INET, SOCK_STREAM, 0);
    CHK_ERR(listen_sd, "socket");

    memset(&sa_serv, 0x00, sizeof(sa_serv));
    sa_serv.sin_family = AF_INET;
    sa_serv.sin_addr.s_addr = INADDR_ANY;
    sa_serv.sin_port = htons(1111);

    err = bind(listen_sd, (struct sockaddr*)&sa_serv, sizeof(sa_serv));
    CHK_ERR(err, "bind");

    err = listen(listen_sd, 5);
    CHK_ERR(err, "listen");

    client_len = sizeof(sa_cli);
    sd = accept(listen_sd, (struct sockaddr*)&sa_cli, &client_len);
    CHK_ERR(sd, "accept");
    close(listen_sd);

    ssl = SSL_new(ctx);
    CHK_NULL(ssl);
    SSL_set_fd(ssl, sd);

// to request client's certificate
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    err = SSL_accept(ssl);
    CHK_SSL(err);

    printf("SSL connection using %s \n", SSL_get_cipher(ssl));

    client_cert = SSL_get_peer_certificate(ssl);

    if(client_cert != NULL) {
        printf("Client certificate: \n");

        str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
        CHK_NULL(str);
        printf("\t subject: %s\n", str);
        OPENSSL_free(str);

        str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
        CHK_NULL(str);
        printf("\t issuer: %s\n", str);
        OPENSSL_free(str);

        X509_free(client_cert);
    } else {
        printf("Client does not have certificate. \n");
    }

    err = SSL_read(ssl, buf, sizeof(buf)-1);
    CHK_SSL(err);
    buf[err] = 0x00;
    printf("Got %d chars: %s \n", err, buf);

    err = SSL_write(ssl, "I hear you/", strlen("I hear you."));
    CHK_SSL(err);

    close(sd);
    SSL_free(ssl);
    SSL_CTX_free(ctx);

    return(0);
}

// client.c

#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define CHK_NULL(x) if((x) == NULL) exit(1);
#define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
#define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }

int main(void) {
    int err;
    int sd;
    struct sockaddr_in sa;

    SSL_CTX   *ctx;
    SSL     *ssl;
    X509                    *server_cert;
    char                    *str;
    char                    buf[4096];
    SSL_METHOD    *meth;

    SSL_load_error_strings();
    SSLeay_add_ssl_algorithms();
    meth = TLSv1_2_client_method();
    ctx = SSL_CTX_new(meth);
    CHK_NULL(ctx);

    if(SSL_CTX_use_certificate_file(ctx, "./client.crt", SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(3);
    }

    if(SSL_CTX_use_PrivateKey_file(ctx, "./client.key", SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    exit(4);
    }

    if(!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr, "Private key does not match the certificate public keyn");
        exit(5);
    }

    CHK_SSL(err);

    sd = socket(AF_INET, SOCK_STREAM, 0);
    CHK_ERR(sd, "socket");

    memset(&sa, 0x00, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    sa.sin_port = htons(1111);

    err = connect(sd, (struct sockaddr*)&sa, sizeof(sa));
    CHK_ERR(err, "connect");

    ssl = SSL_new(ctx);
    CHK_NULL(ssl);

    SSL_set_fd(ssl, sd);
    err = SSL_connect(ssl);
    CHK_NULL(err);

    printf("SSL connection using %s \n", SSL_get_cipher(ssl));

    server_cert = SSL_get_peer_certificate(ssl);
    CHK_NULL(server_cert);
    printf("Server certificate: \n");

    str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
    CHK_NULL(str);
    printf("\t subject: %s \n", str);
    OPENSSL_free(str);

    str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
    CHK_NULL(str);
    printf("\t issuer: %s \n", str);
    OPENSSL_free(str);

    X509_free(server_cert);

    err = SSL_write(ssl, "Hello World!", strlen("Hello World!"));
    CHK_SSL(err);

    err = SSL_read(ssl, buf, sizeof(buf)-1);
    CHK_SSL(err);
    buf[err] = 0x0;
    printf("Got %d chars: %s \n", err, buf);
    SSL_shutdown(ssl);

    close(sd);
    SSL_free(ssl);
    SSL_CTX_free(ctx);

    return 0;
}

below is output results.

(1) server

$ ./server

SSL connection using ECDHE-RSA-AES256-GCM-SHA384

Client does not have certificate.

Got 12 chars: Hello World!

(2) client

$ ./client

SSL connection using ECDHE-RSA-AES256-GCM-SHA384

Server certificate:

subject: /C=IN/ST=WB/L=Kolkata/O=TEST-INFO-CLIENTA/OU=IT/CN=clienta.com/[email protected]

issuer: /C=IN/ST=WB/L=Kolkata/O=TEST-INFO-CLIENTA/OU=IT/CN=clienta.com/[email protected]

Got 11 chars: I hear you/

=======================================================

I don't know why the server's output says, "Client does not have certificate."

Although I added "SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL)" in server.c, the server does not receive the client's certificate.

If anyone knows about this, please reply.

1
off topic, but ` -des3` seems like a poor choice. triple DES is broken. en.wikipedia.org/wiki/Triple_DES#Security . Can't think of a good reason to use it for anything, unless you're specifically targeting it. Actually looking at it your process of creating self signed certs seems a little bazare. openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem is the pattern I preferDaniel Farrell
You set SSL_VERIFY_PEER on the context after creating the SSL object with SSL_new() so this change in the context is ignored. Either move it before the SSL_new or make it instead SSL_set_verify(...) (NOT SSL_CTX_). @DanFarrell: 3key-3DES still has 112 bits strength, which is enough and far better than the horribly lame KDF used for OpenSSL 'legacy' files, and one privatekey file will never ever come anywhere near the gigabytes where 64-bit block is a concern. Although OP actually removes the encryption immediately after setting it so it doesn't matter at all.dave_thompson_085
@dave_thompson_085: I have additional questions, could you answer me?sangsub lim
sangsub: as Jay said, you should put your question, including additions to your question, in the question; that's how the question-and-answer format is designed to work. And Jay's answer is correct, although I have added some minor points.dave_thompson_085

1 Answers

1
votes

How to avoid the certificate validation failure error?

If you are creating a 'rootCA' and then issuing the Server Certificate and Client Certificate using the same 'rootCA', you need to ensure that the 'rootCA' certificate file is added as CA to OpenSSL through the API SSL_CTX_load_verify_locations. Even if only self signed certificates are used, then we need to still add them using the API SSL_CTX_load_verify_locations. This operation must be done before SSL_new.

Is there a way to separate the validation of the certificate from the client? Now the code is SSL_accept (ssl); It seems that the certificate validation is done in the function. In other words, what I want is that after the server receives the client certificate, it first prints out the certificate information and then validates it.

To have some control over the validation of the certificate received from client, you can set the certificate verification callback. You will get the certificate received from client in this callback, which you print for debugging purposes and then continue with the validation. Please refer to the API SSL_CTX_set_verify. Though you seem to be setting it, you are passing for NULL for callback. If you pass a valid callback function as argument, you will receive the certificate in your callback.