Connecting a client to a Java-based server using OpenSSL socket, C code

By | January 27, 2011

I decided to switch to Python while developing the embedded client in C language. There was not much work done anyway, and faster coding with Python should allow me to catch up. Luckily the socket part is in working condition. It was tested in Ubuntu Linux and OpenWrt router firmware v8.09 based on 2.4 kernel. Here is a short how-to source code that should connect to the server, perform server and client verification and then echo the received data back to the server.


Source code:

main.c

#include "openssl/bio.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
#include "openssl/engine.h"

#define CA_CERT_FILE 		"server_cert.pem"	// public key of the server we are connecting to
#define CLIENT_KEY_FILE		"client_key.pem"	// private key
#define CLIENT_KEY_PASS		"client key password"	// password for the private key

#define SERVER		"localhost"
#define PORT		3999

static SSL *ssl = NULL;
static BIO *bio = NULL;
static SSL_CTX *ctx = NULL;

int sslcl_init_ca(const char *const ca_cert_filename)
{
	/* Initializing OpenSSL */
	ENGINE_load_builtin_engines();
	SSL_load_error_strings();
	ERR_load_BIO_strings();
	OpenSSL_add_all_algorithms();
	SSLeay_add_ssl_algorithms();

	RAND_load_file("/dev/urandom", 1024);

	ctx = SSL_CTX_new(SSLv23_method());	// create SSL context

	if (NULL == ctx) {
		return 2;
	}

	if (!SSL_CTX_load_verify_locations(ctx, ca_cert_filename, NULL)) {
		ERR_print_errors_fp(stderr);
		return 3;
	}

	return 0;
}

// Install Client certificate to SSL context.
int sslcl_set_client_cert(const char *const cert_file, const char *const password)
{
	if (NULL == ctx) {
		return 1;
	}
	if (NULL == cert_file) {
		return 2;
	}

	if (NULL != password) {
		SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)password);
	}

	if (SSL_CTX_use_certificate_chain_file(ctx, cert_file) != 1) {
		ERR_print_errors_fp(stderr);
		return 3;
	}

	return 0;
}

// Set the client's private key from file to SSL context
int sslcl_set_client_priv_key(const char *const key_file,
			  const char *const password)
{
	if (NULL == ctx) {
		return 1;
	}
	if (NULL == key_file) {
		return 2;
	}

	if (NULL != password) {
		SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)password);
	}

	if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) != 1) {
		ERR_print_errors_fp(stderr);
		return 3;
	}

	return 0;
}

int sslcl_init(void)
{
	if (sslcl_init_ca(CA_CERT_FILE) != 0)	// init SSL library, give Certification Authority (CA) certificate
	{
		error("ssl\tinit failed");
		return 1;
	}

	if (sslcl_set_client_cert(CLIENT_KEY_FILE, CLIENT_KEY_PASS) != 0)	// setup Client's certificate
	{
		error("ssl\tclient certificate setup failed");
		return 2;
	}

	if (sslcl_set_client_priv_key(CLIENT_KEY_FILE, CLIENT_KEY_PASS) != 0)	// setup client's private key
	{
		error("ssl\tinit failed: client key");
		return 3;
	}

	SSL_CTX_set_timeout(ctx, 120);
	return 0;
}

int sslcl_connect(const char *const server, const unsigned int port)
{
	char conn_string[256];
	long result;

	fprintf(stdout, "Connecting to port %i at %s\n", port, server);

	snprintf(conn_string, sizeof(conn_string), "%s:%i", server, port);

	bio = BIO_new_ssl_connect(ctx);
	if (NULL == bio) {
		ERR_print_errors_fp(stderr);
		return 1;
	}
	BIO_get_ssl(bio, &ssl);

	if (NULL == ssl) {
		ERR_print_errors_fp(stderr);
		return 2;
	}

	SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

	/* Attempt to connect */
	BIO_set_conn_hostname(bio, conn_string);

	/* Verify the connection opened and perform the handshake */
	if (BIO_do_connect(bio) <= 0) {
		ERR_print_errors_fp(stderr);
		return 3;
	}

	if (BIO_do_handshake(bio) <= 0) {
		ERR_print_errors_fp(stderr);
		return 4;
	}

	result = SSL_get_verify_result(ssl);

	if (result != X509_V_OK) {
		fprintf(stderr, "ssl\tcertificate verification error: %i\n",
			  (int)SSL_get_verify_result(ssl));

		ERR_print_errors_fp(stderr);

		return 5;
	}

	return 0;
}

int main(void)
{
	int ret_value;
	ret_value = sslcl_init();
	if (ret_value){
		return ret_value;
	}

	//connect to server
	if (sslcl_connect(SERVER, PORT) != 0)	// connect to server
	{
		fprintf(stdout, "connect failed\n");
	}
	else
	{
		int sock;
		fd_set fds;
		struct timeval tmo;

		fprintf(stdout, "ssl\tconnected");

		sock = SSL_get_fd(ssl);

		while (1) {
			int readsocks;

			FD_ZERO(&fds);	/* initialize selection vector */
			FD_SET(sock, &fds);	/* set bit in selection vector */
			tmo.tv_sec = 1;
			tmo.tv_usec = 0;
			readsocks = select(sock + 1, &fds, (fd_set *) 0, (fd_set *) 0, &tmo);
			if (readsocks < 0) {
				break;	//error
			}
			else if (readsocks != 0)
			{	// ready to read
				const int max_size = 1024;
				char buffer[max_size];
				int n;

				n = SSL_read(ssl, buffer, max_size);
				if (n < 0) {
					break;
				} else if (n > 0) {
					//received something
					//send back as echo

					SSL_write(ssl, buffer, n);
				}
			}
		}
	}
	return 0;
}

Makefile:

all: demo

LIBS= -lssl  -lcrypto

%.o: %.c
	$(CC) $(CFLAGS) -c -I. -o $@ $^

demo: main.o
	$(CC) -o $@ $^ $(LIBS)

clean:
	rm -f *.o demo

Leave a Reply

Your email address will not be published.