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.
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
#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/emailAddress=aaa#aaa.com
issuer: /C=IN/ST=WB/L=Kolkata/O=TEST-INFO-CLIENTA/OU=IT/CN=clienta.com/emailAddress=aaa#aaa.com
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.
ssl = SSL_new(ctx);
...
// to request client's certificate
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
You generate the SSL object from the context and after that you change the context. This has no effect on the created SSL object, which means that the server will not request the client certificate in the first place and thus the client will not provide a certificate.
Once you've put SSL_CTX_set_verify in the correct place you will notice that the server will not be able to verify the client certificate. This is because the certificate is not signed by a CA trusted by the server. See SSL_CTX_load_verify_locations for how to set the trusted root CA.
Related
openssl_1.1.1_perl_dump.txt
We are trying to upgrade on Openssl version 1.1.1, with security layer from TLS 1.2 to TLS 1.3.
Our code base performs SSL handshake, and later use send & read/recv function to transfer data. *Note - it is working fine for TLS 1.2.
However when we switch to TLS 1.3, and data packets are not received for send & read/recv, but working only when we use SSL_write & SSL_read.
Below we have tried to make sample server & client program, where we use send & read/recv functions after successful SSL connection, here also it works fine for TLS 1.2, but in case of TLS 1.3 there is lag in packets received, the first response from server is blank, please refer outputs below. For out put of 'perl configdata.pm --dump' refer attachment.
Used steps mentioned as per this reference article https://help.ubuntu.com/community/OpenSSL - to create SSL certificates.
server.c
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "/usr/include/openssl/ssl.h"
#include "/usr/include/openssl/crypto.h"
#include "/usr/include/openssl/err.h"
#define FAIL -1
int OpenListener(int port)
{ int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
perror("can't bind port");
abort();
}
if ( listen(sd, 10) != 0 )
{
perror("Can't configure listening port");
abort();
}
return sd;
}
int isRoot()
{
if (getuid() != 0)
{
return 0;
}
else
{
return 1;
}
}
SSL_CTX* InitServerCTX(void)
{ SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
char *imethod=getenv("TLS_METHOD");
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0){
printf("\nSetting TLS1.3 method\n");
fflush(stdout);
method = TLS_server_method(); /* create new server-method instance */
}
else{
printf("\nSetting TLS1.2 method\n");
fflush(stdout);
method = TLSv1_2_server_method(); /* create new server-method instance */
}
ctx = SSL_CTX_new(method); /* create new context from method */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0)
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
SSL_CTX_load_verify_locations(ctx, "01.pem", "/home/qarun/aparopka/myCA/signedcerts");
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
//New lines - Force the client-side have a certificate
/*SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
SSL_CTX_set_verify_depth(ctx, 2);*/
//End new lines
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("No certificates.\n");
}
void Servlet(SSL* ssl) /* Serve the connection -- threadable */
{ char buf[1024];
char reply[1024];
int sd, bytes;
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
sd = SSL_get_fd(ssl); /* get socket connection */
printf("\nsd: <%d>", sd);
if ( SSL_accept(ssl) == FAIL ) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
else
{
/*if(SSL_do_handshake(ssl) <= 0){
printf("\n SSL_do_handshake failed....\n");
}*/
ShowCerts(ssl); /* get any certificates */
do{
//bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
//bytes = read(sd, buf, sizeof(buf));
bytes = recv(sd, buf, sizeof(buf), 0);
if ( bytes > 0 )
{
buf[bytes] = 0;
printf("Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); /* construct reply */
//SSL_write(ssl, reply, strlen(reply)); /* send reply */
send(sd, reply, strlen(reply), 0); // MSG_NOSIGNAL | MSG_DONTWAIT); //MSG_CONFIRM);
}
else
ERR_print_errors_fp(stderr);
}while(bytes>0);
}
SSL_free(ssl); /* release SSL state */
close(sd); /* close connection */
}
int main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
char *portnum;
if(!isRoot())
{
printf("This program must be run as root/sudo user!!");
//exit(0);
}
if ( count != 2 )
{
printf("Usage: %s <portnum>\n", strings[0]);
exit(0);
}
SSL_library_init();
portnum = strings[1];
ctx = InitServerCTX(); /* initialize SSL */
LoadCertificates(ctx, "/home/qarun/aparopka/myCA/server_crt.pem", "/home/qarun/aparopka/myCA/server_key.pem"); /* load certs */
server = OpenListener(atoi(portnum)); /* create server socket */
while (1)
{ struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL *ssl;
int client = accept(server, (struct sockaddr*)&addr, &len); /* accept connection as usual */
printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, client); /* set connection socket to SSL state */
Servlet(ssl); /* service connection */
}
close(server); /* close server socket */
SSL_CTX_free(ctx); /* release context */
}
client.c
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define FAIL -1
//Added the LoadCertificates how in the server-side makes.
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_chain_file(ctx, CertFile ) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
int OpenConnection(const char *hostname, int port)
{ int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL )
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
SSL_CTX* InitCTX(void)
{ SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
char *imethod=getenv("TLS_METHOD");
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0){
printf("\nSetting TLS1.3 method\n");
fflush(stdout);
method = TLS_client_method(); /* create new server-method instance */
}
else{
printf("\nSetting TLS1.2 method\n");
fflush(stdout);
method = TLSv1_2_client_method(); /* create new server-method instance */
}
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0)
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
return ctx;
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
}
else
printf("Info: No client certificates configured.\n");
}
int main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
SSL *ssl;
char buf[1024];
int bytes;
char *hostname, *portnum;
if ( count != 3 )
{
printf("usage: %s <hostname> <portnum>\n", strings[0]);
exit(0);
}
SSL_library_init();
hostname=strings[1];
portnum=strings[2];
ctx = InitCTX();
LoadCertificates(ctx, "/home/qarun/aparopka/myCA/server_crt.pem", "/home/qarun/aparopka/myCA/server_key.pem");
server = OpenConnection(hostname, atoi(portnum));
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
int sd = SSL_get_fd(ssl);
int flip=1;
printf("\nsd: <%d>", sd);
if ( SSL_connect(ssl) == FAIL ) /* perform the connection */
ERR_print_errors_fp(stderr);
else
{ //char *msg = "Hello???";
if(SSL_do_handshake(ssl) <= 0){
printf("\n client SSL_do_handshake failed....\n");
}
char msg[1024];
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl); /* get any certs */
while(1){
bzero(msg, 1024);
bzero(buf, 1024);
printf("\nEnter message: ");
fgets(msg, 1024, stdin);
//SSL_write(ssl, msg, strlen(msg)); /* encrypt & send message */
send(sd, msg, strlen(msg), 0); //MSG_NOSIGNAL | MSG_DONTWAIT); //MSG_CONFIRM);
//bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
//bytes = read(sd, buf, sizeof(buf));
bytes = recv(sd, buf, sizeof(buf), 0);
/* read again for TLS 1.3 only for first time */
/*char *imethod=getenv("TLS_METHOD");
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0
&& flip){
bytes = read(sd, buf, sizeof(buf));
flip=0;
}*/
buf[bytes] = 0;
printf("Received: \"%s\"\n", buf);
}
SSL_free(ssl); /* release connection state */
}
close(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
Server & client Output with TLS 1.2
**$ ./ssl_server_working 2023**
Setting TLS1.2 method
Connection: 10.250.14.23:41152
sd: <4>No certificates.
Client msg: "first
"
Client msg: "second
"
Client msg: "third
"
**./ssl_client_working 10.250.14.23 2023**
Setting TLS1.2 method
sd: <3>Connected with ECDHE-RSA-AES256-GCM-SHA384 encryption
Server certificates:
Subject: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari#quest.com/O=SharePlex/OU=IT Department
Issuer: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari#quest.com/O=SharePlex/OU=IT Department
Enter message: first
Received: "<html><body><pre>first
</pre></body></html>
"
Enter message: second
Received: "<html><body><pre>second
</pre></body></html>
"
Enter message: third
Received: "<html><body><pre>third
</pre></body></html>
"
Server & client output for TLS 1.3 : Note the response for first client request is blank, which is received in next response for second request.
export TLS_METHOD=1.3
**./ssl_server_working 2024**
Setting TLS1.3 method
Connection: 10.250.14.23:34012
sd: <4>No certificates.
Client msg: "first
"
Client msg: "second
"
Client msg: "third
"
**./ssl_client_working 10.250.14.23 2024**
Setting TLS1.3 method
sd: <3>Connected with TLS_AES_256_GCM_SHA384 encryption
Server certificates:
Subject: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari#quest.com/O=SharePlex/OU=IT Department
Issuer: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari#quest.com/O=SharePlex/OU=IT Department
Enter message: first
Received: ""
Enter message: second
Received: "<html><body><pre>first
</pre></body></html>
"
Enter message: third
Received: "<html><body><pre>second
</pre></body></html>
"
We got the answer for this. Turns out TLS 1.3 has added one more handshake step, where after server Finish message Client again sends Server finish message, server verifies it, & sends post-handshake protocol level message, called as 'NewSessionTicket message'. We can disable it with SSL_CTX_set_num_tickets method, before initiating SSL server.
SSL_set_num_tickets(ssl, 0); /* Disable post-handshake token */
sd = SSL_get_fd(ssl); /* get socket connection */
if ( SSL_accept(ssl) == FAIL ) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
As for people concerned about this not being secure, in our case of requirement we only need initial secure authentication, and then we do session tunneling. After that if we use SSL_write SSL_read for every transaction (which are in huge volume & time constraint) , the encryption decryption step would degrade the performance. So we are using send & recv for sending data.
I am trying to use the openssl api to implement the JA3 method (TLS fingerprinting method) on a personnal project. To do that I need to get some informations on the Client Hello packet and I am trying to do that with the openssl api https://www.openssl.org/docs/man1.1.1/man3/ . Firstly I downloaded a code which set up a TLS connection between my computer and a website and retrieved the certificat information. This program is running well, the tls connection is using TLS 1.3 and the certificat information are ok. Then I tried to retrieved the Client Hello packet to start implement the JA3 method. After few researches I found many function that may allow me to get the information I need : https://www.openssl.org/docs/man1.1.1/man3/SSL_client_hello_cb_fn.html . All of those function can only be call on the client hello callback function. This function can be called thanks to the SSL_CTX_set_client_hello_cb function. But in my case my function is never called. I am starting to be hopeless it's why I need your help. I really searched a lot to debug that but I didn't find a flag that can allow the callback function or something like that. Here is the program :
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
int create_socket(char[], BIO *);
int callback(SSL *s, int *al, void *arg)
{
int * number = arg;
*number = (*number) + 1;
printf("\nWe are in the callback function !\n");
return SSL_CLIENT_HELLO_SUCCESS;
}
int main() {
char dest_url[] = "https://www.hp.com";
BIO *certbio = NULL;
BIO *outbio = NULL;
X509 *cert = NULL;
X509_NAME *certname = NULL;
const SSL_METHOD *method;
SSL_CTX *ctx;
struct ssl_st *ssl;
int server = 0;
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
certbio = BIO_new(BIO_s_file());
outbio = BIO_new_fp(stdout, BIO_NOCLOSE);
if(SSL_library_init() < 0)
BIO_printf(outbio, "Could not initialize the OpenSSL library !\n");
method = TLS_client_method();
if ( (ctx = SSL_CTX_new(method)) == NULL)
BIO_printf(outbio, "Unable to create a new SSL context structure.\n");
int hope = 12;
SSL_CTX_set_client_hello_cb(ctx, &callback, (void *)&hope);
ssl = SSL_new(ctx);
server = create_socket(dest_url, outbio);
if(server != 0)
BIO_printf(outbio, "Successfully made the TCP connection to: %s.\n", dest_url);
SSL_set_fd(ssl, server);
if ( SSL_connect(ssl) != 1 )
BIO_printf(outbio, "Error: Could not build a SSL session to: %s.\n", dest_url);
else
BIO_printf(outbio, "Successfully enabled SSL/TLS session to: %s.\n", dest_url);
cert = SSL_get_peer_certificate(ssl);
if (cert == NULL)
BIO_printf(outbio, "Error: Could not get a certificate from: %s.\n", dest_url);
else
BIO_printf(outbio, "Retrieved the server's certificate from: %s.\n", dest_url);
certname = X509_NAME_new();
certname = X509_get_subject_name(cert);
BIO_printf(outbio, "Displaying the certificate subject data:\n");
X509_NAME_print_ex(outbio, certname, 0, 0);
BIO_printf(outbio, "\n");
SSL_free(ssl);
close(server);
X509_free(cert);
SSL_CTX_free(ctx);
BIO_printf(outbio, "Finished SSL/TLS connection with server: %s.\n", dest_url);
printf("Hope value : %d \n", hope);
return(0);
}
int create_socket(char url_str[], BIO *out) {
int sockfd;
char hostname[256] = "";
char portnum[6] = "443";
char proto[6] = "";
char *tmp_ptr = NULL;
int port;
struct hostent *host;
struct sockaddr_in dest_addr;
if(url_str[strlen(url_str)] == '/')
url_str[strlen(url_str)] = '\0';
strncpy(proto, url_str, (strchr(url_str, ':')-url_str));
strncpy(hostname, strstr(url_str, "://")+3, sizeof(hostname));
if(strchr(hostname, ':')) {
tmp_ptr = strchr(hostname, ':');
/* the last : starts the port number, if avail, i.e. 8443 */
strncpy(portnum, tmp_ptr+1, sizeof(portnum));
*tmp_ptr = '\0';
}
port = atoi(portnum);
if ( (host = gethostbyname(hostname)) == NULL ) {
BIO_printf(out, "Error: Cannot resolve hostname %s.\n", hostname);
abort();
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
dest_addr.sin_family=AF_INET;
dest_addr.sin_port=htons(port);
dest_addr.sin_addr.s_addr = *(long*)(host->h_addr);
memset(&(dest_addr.sin_zero), '\0', 8);
tmp_ptr = inet_ntoa(dest_addr.sin_addr);
if ( connect(sockfd, (struct sockaddr *) &dest_addr,
sizeof(struct sockaddr)) == -1 ) {
BIO_printf(out, "Error: Cannot connect to host %s [%s] on port %d.\n",
hostname, tmp_ptr, port);
}
return sockfd;
}
I am waiting for your answer thanks guys.
The hint for what is going wrong is from the first line of the description in the doc that you linked to:
SSL_CTX_set_client_hello_cb() sets the callback function, which is automatically called during the early stages of ClientHello processing on the server.
This is called by a server when processing the received ClientHello. You have written a client and therefore it is never called.
As an alternative I suggest you look at SSL_CTX_set_msg_callback():
https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_msg_callback.html
From the docs:
SSL_CTX_set_msg_callback() or SSL_set_msg_callback() can be used to define a message callback function cb for observing all SSL/TLS protocol messages (such as handshake messages) that are received or sent, as well as other events that occur during processing.
I want to connect to the gcm service without a gcm class (only native socket with openssl).
The ssl handshake works fine. But when I send the post request to https
server i got the http 404 error.
If I uncomment the line
"//strcat(FormBuffer1, "Host: gcm-http.googleapis.com\r\n");", the server returns nothing and the connection seems to hang.
I tried gcm-http.googleapis.com and android.googleapis.com.
Is there anywhere an https post example for GCM? I found only code sample which use gcm classes.
Thanks for your answers!
My C source:
//gcc -lssl -lcrypto sock.c -o sock.o
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <stdlib.h>
#include <memory.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <stdarg.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
int main(int argc, char *argv[]){
int sd;
struct hostent *host;
struct sockaddr_in addr;
BIO *outbio = NULL;
SSL_METHOD *method;
SSL_CTX *ctx;
SSL *ssl;
char *req;
int req_len;
char hostname[] = "gcm-http.googleapis.com"; // https://gcm-http.googleapis.com/gcm/send
//char hostname[] = "www.gmx.de"; // https://gcm-http.googleapis.com/gcm/send
char certs[] = "/etc/ssl/certs/ca-certificates.crt";
int port = 443;
int bytes;
char buf[1];
char FormBuffer1[1024];
char FormBuffer2[1024];
int ContentLength;
char ContentLengthBuffer[10];
// added this to test
char dest_url[] = "https://gcm-http.googleapis.com/gcm/send";
BIO *certbio = NULL;
X509 *cert = NULL;
X509_NAME *certname = NULL;
BIO *outbio2 = NULL;
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
outbio = BIO_new(BIO_s_file());
outbio = BIO_new_fp(stdout, BIO_NOCLOSE);
if(SSL_library_init() < 0){
BIO_printf(outbio, "Could not initialize the OpenSSL library !\n");
}
method = SSLv23_client_method();
ctx = SSL_CTX_new(method);
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
host = gethostbyname(hostname);
sd = socket(AF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) == -1 ) {
BIO_printf(outbio, "%s: Cannot connect to host %s [%s] on port %d.\n", argv[0], hostname, inet_ntoa(addr.sin_addr), port);
}
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sd);
SSL_connect(ssl);
// try something here
/* Get the remote certificate into the X509 structure */
printf("SSL_get_peer_certificate(ssl) \n");
cert = SSL_get_peer_certificate(ssl);
if (cert == NULL)
printf("Error: Could not get a certificate from: %s.\n", dest_url);
else
printf("Retrieved the server's certificate from: %s.\n", dest_url);
printf("\n");
/* extract various certificate information */
certname = X509_NAME_new();
certname = X509_get_subject_name(cert);
/* display the cert subject here */
BIO_printf(outbio, "Displaying the certificate subject data:\n");
X509_NAME_print_ex(outbio, certname, 0, 0);
BIO_printf(outbio, "\n\n");
/*
Format:
Content-Type:application/json
Authorization:key=....
{
"to" : ".......",
"data" : {
...
},
}*/
//req = "GET / HTTP/1.1\r\n Host: gcm-http.googleapis.com/gcm/send\r\n\r\n";
// header
strcpy(FormBuffer2, "{\"to\" : \"/topics/global\", \n \"data\": { \"message\" : \"Test123\", },\n }\n\r\n");
ContentLength = strlen(FormBuffer2);
sprintf(ContentLengthBuffer, "%d", ContentLength);
strcpy(FormBuffer1, "POST ");
strcat(FormBuffer1, "/gcm/send");
strcat(FormBuffer1, " HTTP/1.1\r\n");
//strcat(FormBuffer1, "Host: gcm-http.googleapis.com\r\n");
//strcat(FormBuffer1, "Host: android.googleapis.com\r\n ");
strcat(FormBuffer1, "Content-length: ");
strcat(FormBuffer1, ContentLengthBuffer);
strcat(FormBuffer1, "\r\nContent-Type:application/json\r\n");
strcat(FormBuffer1, "Authorization:key=AIzaSyA.....\r\n");
//strcat(FormBuffer, ContentLengthBuffer); // size of actual content
//strcat_s(FormBuffer, "\n\n");
req_len = strlen(FormBuffer1);
SSL_write(ssl, FormBuffer1, req_len);
req_len = strlen(FormBuffer2);
SSL_write(ssl, FormBuffer2, req_len);
memset(buf, '\0', sizeof(buf));
bytes = SSL_read(ssl, buf, sizeof(buf));
while(bytes > 0){
write(STDOUT_FILENO, buf, bytes);
memset(buf, '\0', sizeof(buf));
bytes = SSL_read(ssl, buf, sizeof(buf));
}
SSL_free(ssl);
close(sd);
SSL_CTX_free(ctx);
}
HTTP Answer:
SSL_get_peer_certificate(ssl)
Retrieved the server's certificate from: https://gcm-http.googleapis.com/gcm/send.
Displaying the certificate subject data:
C=US, ST=California, L=Mountain View, O=Google Inc, CN=*.googleapis.com
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=UTF-8
Content-Length: 1569
Date: Sat, 21 Nov 2015 20:26:47 GMT
Server: GFE/2.0
Connection: close
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 404 (Not Found)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}#media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}#media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}#media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>404.</b> <ins>That’s an error.</ins>
<p>The requested URL <code>/gcm/send</code> was not found on this server. <ins>That’s all we know.</ins>
I'm trying to create server busy by adding delay in the SSL Server code(given below) before accept(), then from the SSL client trying to connect to server. After approx 2 minutes i'm getting SSL error and client returns as expected.
When i try to analyse wireshark log i can seen "Client Hello TCP retramission" message 10 times. I'm trying to understand the reason and source of this retransmission. Is it in TCP Layer or SSL Layer?
I'm doing this on linux ubuntu 14.04 x86. Below are the ssl-server and ssl-client downloaded.
I tried changing /proc/sys/net/ipv4/tcp_retries2 but it doesn't effect the number of retries. From Wireshark log i can see the number of retries for client hello is 10.
Thanks in advance.
//SSL-Server.c
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "openssl/ssl.h"
#include "openssl/err.h"
#define FAIL -1
int OpenListener(int port)
{ int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
perror("can't bind port");
abort();
}
if ( listen(sd, 1) != 0 )
{
perror("Can't configure listening port");
abort();
}
return sd;
}
int isRoot()
{
if (getuid() != 0)
{
return 0;
}
else
{
return 1;
}
}
SSL_CTX* InitServerCTX(void)
{ SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
method = SSLv3_server_method(); /* create new server-method instance */
ctx = SSL_CTX_new(method); /* create new context from method */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("No certificates.\n");
}
void Servlet(SSL* ssl) /* Serve the connection -- threadable */
{ char buf[1024];
char reply[1024];
int sd, bytes;
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
if ( SSL_accept(ssl) == FAIL ) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
else
{
ShowCerts(ssl); /* get any certificates */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
if ( bytes > 0 )
{
buf[bytes] = 0;
printf("Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); /* construct reply */
SSL_write(ssl, reply, strlen(reply)); /* send reply */
}
else
ERR_print_errors_fp(stderr);
}
sd = SSL_get_fd(ssl); /* get socket connection */
SSL_free(ssl); /* release SSL state */
close(sd); /* close connection */
}
int main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
char *portnum;
if(!isRoot())
{
printf("This program must be run as root/sudo user!!");
exit(0);
}
if ( count != 2 )
{
printf("Usage: %s <portnum>\n", strings[0]);
exit(0);
}
SSL_library_init();
portnum = strings[1];
ctx = InitServerCTX(); /* initialize SSL */
LoadCertificates(ctx, "mycert.pem", "mycert.pem"); /* load certs */
server = OpenListener(atoi(portnum)); /* create server socket */
while (1)
{ struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL *ssl;
sleep(1200);
// sleep(12);
int client = accept(server, (struct sockaddr*)&addr, &len); /* accept connection as usual */
printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
//Wait before close connection to check max connections error
ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, client); /* set connection socket to SSL state */
Servlet(ssl); /* service connection */
}
close(server); /* close server socket */
SSL_CTX_free(ctx); /* release context */
}
SSL Client.c
//SSL-Client.c
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define FAIL -1
int OpenConnection(const char *hostname, int port)
{ int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL )
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
SSL_CTX* InitCTX(void)
{ SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
method = SSLv3_client_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
}
else
printf("No certificates.\n");
}
int main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
SSL *ssl;
char buf[1024];
int bytes;
char *hostname, *portnum;
if ( count != 3 )
{
printf("usage: %s <hostname> <portnum>\n", strings[0]);
exit(0);
}
SSL_library_init();
hostname=strings[1];
portnum=strings[2];
ctx = InitCTX();
server = OpenConnection(hostname, atoi(portnum));
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
if ( SSL_connect(ssl) == FAIL ) /* perform the connection */
ERR_print_errors_fp(stderr);
else
{ char *msg = "Hello???";
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl); /* get any certs */
SSL_write(ssl, msg, strlen(msg)); /* encrypt & send message */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
buf[bytes] = 0;
printf("Received: \"%s\"\n", buf);
SSL_free(ssl); /* release connection state */
}
close(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
EDIT: Below is the Retransmission Message from the Wireshark log, SSL-Server is running on X.X.X.241, Client is on X.X.X.242
5 0.206404000 X.X.X.242 X.X.X.241 SSL 363 [TCP Retransmission] Client Hello
Your delay is added before accept not before SSL_accept. At this stage the tcp 3-way handshake has not completed, and therefore the SSL/TLS layer has not even started. What will be happening is the client will be sending a SYN packet, and the server responding only after a delay with an ACK packet. My recollection is the default is 5 SYN packet retries over 20 seconds; I can't immediately explain why you are seeing more than that, but it may be because you are attempting to acknowledge a SYN which itself has been replaced. The knob to adjust this is net.ipv4.tcp_syn_retries not tcp_retries2, and it needs to be adjusted on the client not the server.
There is no such thing (to my knowledge) as a 'TCP Hello Transmission'; that would seem to be a poor message client side. Perhaps the same routing opens the TCP connection and sends the SSL handshake.
I found a pretty good SSL/TLS server-client example in C and I wanted to adapt it for use with the BIO library. And I pretty much succeeded except one error I get when running the server:
$ ./ssl-server
68671:error:140950D3:SSL routines:SSL3_READ_N:read bio not set:/SourceCache/OpenSSL098/OpenSSL098-35.1/src/ssl/s3_pkt.c:203:
I'm using gcc -o ssl-server SSL-Server.c -lssl -lcrypto -Wall to compile the server:
//SSL-Server.c
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "openssl/ssl.h"
#include "openssl/err.h"
#define FAIL -1
#define PORT "2013"
SSL_CTX* InitServerCTX(void)
{
SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv3_server_method();
ctx = SSL_CTX_new(method);
if( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
if( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
if( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
if( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
void ShowCerts(SSL* ssl)
{
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl);
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
{
printf("No certificates.\n");
}
}
void Servlet(SSL* ssl)
{
char buf[1024];
int sd, bytes;
if( SSL_accept(ssl) == FAIL )
{
ERR_print_errors_fp(stderr);
}
else
{
ShowCerts(ssl);
bytes = SSL_read(ssl, buf, sizeof(buf));
if( bytes > 0 )
{
buf[bytes] = 0;
printf("Client msg: \"%s\"\n", buf);
SSL_write(ssl, "back message", strlen("back message"));
}
else
{
ERR_print_errors_fp(stderr);
}
}
sd = SSL_get_fd(ssl);
SSL_free(ssl);
close(sd);
}
int main(int count, char *strings[])
{
SSL_CTX *ctx;
BIO *acc, *client;
SSL_library_init();
ctx = InitServerCTX();
LoadCertificates(ctx, "mycert.pem", "mycert.pem");
acc = BIO_new_accept(PORT);
if(!acc)
{
printf("Error creating server socket");
}
while(1)
{
if(BIO_do_accept(acc) <= 0)
{
printf("Error binding server socket");
}
SSL *ssl;
client = BIO_pop(acc);
if(!(ssl = SSL_new(ctx)))
{
printf("Error creating SSL context");
}
SSL_set_bio(ssl, client, client);
// Here should be created threads
Servlet(ssl);
}
SSL_CTX_free(ctx);
}
I'm using gcc -o ssl-client SSL-Client.c -lssl -lcrypto -Wall to compile the client:
//SSL-Client.c
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#define FAIL -1
#define SERVER "localhost"
#define PORT "2013"
SSL_CTX* InitCTX(void)
{
SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv3_client_method();
ctx = SSL_CTX_new(method);
if( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void ShowCerts(SSL* ssl)
{
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl);
if( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
{
printf("No certificates.\n");
}
}
int main(int count, char *strings[])
{
SSL_CTX *ctx;
SSL *ssl;
BIO *conn;
char buf[1024];
int bytes;
SSL_library_init();
ctx = InitCTX();
conn = BIO_new_connect(SERVER ":" PORT);
if(!conn)
{
printf("Error creating connection BIO");
}
if(BIO_do_connect(conn) <= 0)
{
printf("Error connecting to remote machine");
}
ssl = SSL_new(ctx);
SSL_set_bio(ssl, conn, conn);
if( SSL_connect(ssl) <= 0 )
{
printf("Error connecting SSL object");
}
else
{
printf("Connected!");
ShowCerts(ssl);
SSL_write(ssl, "ana are mere", strlen("ana are mere") );
bytes = SSL_read(ssl, buf, sizeof(buf));
printf("%s\n", buf);
SSL_free(ssl);
}
SSL_CTX_free(ctx);
return 0;
}
I'm generating the certificate with the openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem command.
Everything works well and the messages are sending OK. Please ignore the lack of error checking and excuse the total mess in the code, it's just for learning purposes.
Can anyone point me what's wrong? Also can you tell if I'm doing major mistakes, I'm trying to learn?(e.g. routine call order, major security problems, etc.)
Thanks!
According to the manual page (the example code at the bottom is very illustrative), the first call to BIO_do_accept() will set up the accept BIO and does nothing else. Only the second and all subsequent calls will be actual calls to accept connections. This is quite illustrative of why OpenSSL will never win an award for «most intuitive API design».
So what happens in your code? You only call BIO_do_accept() within the loop. First time through the loop, it will set up the BIO and return immediately. Your code calls Servlet() on the inexisting connection, and SSL_accept() fails, returning the error you are seeing. After Servlet() returns, your code loops happily into the second invocation of BIO_do_accept() which this time blocks, waiting for the first connection, and everything works as intended from here.
To fix this, you need to call BIO_do_accept() once before the loop, like this (using your broken style of error handling for consistency -- you really need to fix your error handling!):
[...]
acc = BIO_new_accept(PORT);
if(!acc)
{
printf("Error creating server socket");
}
/* first call is to set up accept BIO */
if(BIO_do_accept(acc) <= 0)
{
printf("Error calling BIO_do_accept() the first time");
}
while(1)
{
if(BIO_do_accept(acc) <= 0)
{
printf("Error binding server socket");
}
[...]