Implement openssl multithreaded SSL server and client using pthread library
- 2020-04-02 02:08:29
- OfStack
The server code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <winsock2.h>
#include <windows.h>
#endif
#include "pthread.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 CERTF "certs/sslservercert.pem"
#define KEYF "certs/sslserverkey.pem"
#define CAFILE "certs/cacert.pem"
pthread_mutex_t mlock=PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t *lock_cs;
static long *lock_count;
#define CHK_NULL(x) if ((x)==NULL) { printf("nulln"); }
#define CHK_ERR(err,s) if ((err)==-1) { printf(" -1 n"); }
#define CHK_SSL(err) if ((err)==-1) { printf(" -1 n");}
#define CAFILE "certs/cacert.pem"
int verify_callback_server(int ok, X509_STORE_CTX *ctx)
{
printf("verify_callback_server n");
return ok;
}
int SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx,char *filename,char *pass)
{
EVP_PKEY *pkey=NULL;
BIO *key=NULL;
key=BIO_new(BIO_s_file());
BIO_read_filename(key,filename);
pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,pass);
if(pkey==NULL)
{
printf("PEM_read_bio_PrivateKey err");
return -1;
}
if (SSL_CTX_use_PrivateKey(ctx,pkey) <= 0)
{
printf("SSL_CTX_use_PrivateKey errn");
return -1;
}
BIO_free(key);
return 1;
}
static int s_server_verify=SSL_VERIFY_NONE;
void * thread_main(void *arg)
{
SOCKET s,AcceptSocket;
WORD wVersionRequested;
WSADATA wsaData;
struct sockaddr_in service;
int err;
size_t client_len; SSL_CTX *ctx;
SSL *ssl;
X509 *client_cert;
char *str;
char buf[1024];
SSL_METHOD *meth;
ssl=(SSL *)arg;
s=SSL_get_fd(ssl);
err = SSL_accept (ssl);
if(err<0)
{
printf("ssl accerrn");
return ;
}
printf ("SSL connection using %sn", 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: %sn", str);
OPENSSL_free (str);
str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);
CHK_NULL(str);
printf ("t issuer: %sn", str);
OPENSSL_free (str);
X509_free (client_cert);
}
else
printf ("Client does not have certificate.n");
memset(buf,0,1024);
err = SSL_read (ssl, buf, sizeof(buf) - 1);
if(err<0)
{
printf("ssl read errn");
closesocket(s);
return;
}
printf("get : %sn",buf);
#if 0
buf[err] = '0';
err = SSL_write (ssl, "I hear you.", strlen("I hear you.")); CHK_SSL(err);
#endif
SSL_free (ssl);
closesocket(s);
}
pthread_t pthreads_thread_id(void)
{
pthread_t ret;
ret=pthread_self();
return(ret);
}
void pthreads_locking_callback(int mode, int type, char *file,
int line)
{
if (mode & CRYPTO_LOCK)
{
pthread_mutex_lock(&(lock_cs[type]));
lock_count[type]++;
}
else
{
pthread_mutex_unlock(&(lock_cs[type]));
}
}
int main ()
{
int err;
int i;
SOCKET s,AcceptSocket;
WORD wVersionRequested;
WSADATA wsaData;
struct sockaddr_in service;
pthread_tpid;
size_t client_len;
SSL_CTX *ctx;
SSL *ssl;
X509 *client_cert;
char *str;
char buf[1024];
SSL_METHOD *meth;
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = SSLv3_server_method();
ctx = SSL_CTX_new (meth);
if (!ctx)
{
ERR_print_errors_fp(stderr);
exit(2);
}
if ((!SSL_CTX_load_verify_locations(ctx,CAFILE,NULL)) ||
(!SSL_CTX_set_default_verify_paths(ctx)))
{
printf("errn");
exit(1);
}
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_pass(ctx, KEYF, "123456") <= 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);
}
s_server_verify=SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
SSL_VERIFY_CLIENT_ONCE;
SSL_CTX_set_verify(ctx,s_server_verify,verify_callback_server);
SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAFILE));
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
printf("errn");
return -1;
}
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s<0) return -1;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(1111);
if (bind( s, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR)
{
printf("bind() failed.n");
closesocket(s);
return -1;
}
if (listen( s, 1 ) == SOCKET_ERROR)
printf("Error listening on socket.n");
printf("recv .....n");
lock_cs=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
lock_count=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
for (i=0; i<CRYPTO_num_locks(); i++)
{
lock_count[i]=0;
pthread_mutex_init(&(lock_cs[i]),NULL);
}
CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);
while(1)
{
struct timeval tv;
fd_set fdset;
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&fdset);
FD_SET(s, &fdset);
select(s+1, &fdset, NULL, NULL, (struct timeval *)&tv);
if(FD_ISSET(s, &fdset))
{
AcceptSocket=accept(s, NULL,NULL);
ssl = SSL_new (ctx);
CHK_NULL(ssl);
err=SSL_set_fd (ssl, AcceptSocket);
if(err>0)
{
err=pthread_create(&pid,NULL,&thread_main,(void *)ssl);
pthread_detach(pid);
}
else
continue;
}
}
SSL_CTX_free (ctx);
return 0;
}
The client code is as follows:
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
#include "pthread.h"
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define MAX_T 1000
#define CLIENTCERT "certs/sslclientcert.pem"
#define CLIENTKEY "certs/sslclientkey.pem"
#define CAFILE "certs/cacert.pem"
static pthread_mutex_t *lock_cs;
static long *lock_count;
pthread_t pthreads_thread_id(void)
{
pthread_t ret;
ret=pthread_self();
return(ret);
}
void pthreads_locking_callback(int mode, int type, char *file,
int line)
{
if (mode & CRYPTO_LOCK)
{
pthread_mutex_lock(&(lock_cs[type]));
lock_count[type]++;
}
else
{
pthread_mutex_unlock(&(lock_cs[type]));
}
}
int verify_callback(int ok, X509_STORE_CTX *ctx)
{
printf("verify_callbackn");
return ok;
}
int SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx,char *filename,char *pass)
{
EVP_PKEY *pkey=NULL;
BIO *key=NULL;
key=BIO_new(BIO_s_file());
BIO_read_filename(key,filename);
pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,pass);
if(pkey==NULL)
{
printf("PEM_read_bio_PrivateKey err");
return -1;
}
if (SSL_CTX_use_PrivateKey(ctx,pkey) <= 0)
{
printf("SSL_CTX_use_PrivateKey errn");
return -1;
}
BIO_free(key);
return 1;
}
void*thread_main(void *arg)
{
int err,buflen,read;
int sd;
SSL_CTX *ctx=(SSL_CTX *)arg;
struct sockaddr_in dest_sin;
SOCKET sock;
PHOSTENT phe;
WORD wVersionRequested;
WSADATA wsaData;
SSL *ssl;
X509 *server_cert;
char *str;
char buf [1024];
SSL_METHOD *meth;
FILE *fp;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
printf("WSAStartup errn");
return -1;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
dest_sin.sin_family = AF_INET;
dest_sin.sin_addr.s_addr = inet_addr( "127.0.0.1" );
dest_sin.sin_port = htons( 1111 );
again:
err=connect( sock,(PSOCKADDR) &dest_sin, sizeof( dest_sin));
if(err<0)
{
Sleep(1);
goto again;
}
ssl = SSL_new (ctx);
if(ssl==NULL)
{
printf("ss new errn");
return ;
}
SSL_set_fd(ssl,sock);
err = SSL_connect (ssl);
if(err<0)
{
printf("SSL_connect errn");
return;
}
printf ("SSL connection using %sn", SSL_get_cipher (ssl));
server_cert = SSL_get_peer_certificate (ssl);
printf ("Server certificate:n");
str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
printf ("t subject: %sn", str);
OPENSSL_free (str);
str = X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0);
printf ("t issuer: %sn", str);
OPENSSL_free (str);
X509_free (server_cert);
err = SSL_write (ssl, "Hello World!", strlen("Hello World!"));
if(err<0)
{
printf("ssl write errn");
return ;
}
#if 0
memset(buf,0,ONE_BUF_SIZE);
err = SSL_read (ssl, buf, sizeof(buf) - 1);
if(err<0)
{
printf("ssl read errn");
return ;
}
buf[err] = '0';
printf ("Got %d chars:'%s'n", err, buf);
#endif
SSL_shutdown (ssl); /* send SSL/TLS close_notify */
SSL_free (ssl);
closesocket(sock);
}
int main ()
{
int err,buflen,read;
int sd;
struct sockaddr_in dest_sin;
SOCKETsock;
PHOSTENT phe;
WORD wVersionRequested;
WSADATA wsaData;
SSL_CTX *ctx;
SSL *ssl;
X509 *server_cert;
char *str;
char buf [1024];
SSL_METHOD *meth;
int i;
pthread_tpid[MAX_T];
SSLeay_add_ssl_algorithms();
meth = SSLv3_client_method();
SSL_load_error_strings();
ctx = SSL_CTX_new (meth);
if(ctx==NULL)
{
printf("ssl ctx new eern");
return -1;
}
if (SSL_CTX_use_certificate_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
exit(3);
}
if (SSL_CTX_use_PrivateKey_file_pass(ctx, CLIENTKEY, "123456") <= 0)
{
ERR_print_errors_fp(stderr);
exit(4);
}
lock_cs=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
lock_count=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
for (i=0; i<CRYPTO_num_locks(); i++)
{
lock_count[i]=0;
pthread_mutex_init(&(lock_cs[i]),NULL);
}
CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);
for(i=0;i<MAX_T;i++)
{
err=pthread_create(&(pid[i]),NULL,&thread_main,(void *)ctx);
if(err!=0)
{
printf("pthread_create errn");
continue;
}
}
for (i=0; i<MAX_T; i++)
{
pthread_join(pid[i],NULL);
}
SSL_CTX_free (ctx);
printf("test okn");
return 0;
}
The above program runs successfully under Windows, using the open source pthread library under Windows.
Note that if multiple threads use openssl, you need to set up two callback functions
CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);