Reputation: 31
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) 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.
Upvotes: 3
Views: 6350
Reputation: 24905
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.
Upvotes: 1