使用openssl API编写client和server

使用openssl api编写的client程序和server程序,其中第一个client采用了BIO的方式,第二个client采用了ssl接口,第一个server程序基本没有使用BIO方式,第二个server程序绝大部分使用了BIO的方式。(server和client程序只是为了展示openssl api的使用,没有涉及到多进程及多线程,没有考虑程序的性能问题。对于openssl api的学习了解是一个任重而道远的过程,需要慢慢积累,积少成多,看完文档要多去实践。)

程序清单:

    makefile

    client.c

    server.c  (两个server不能同时编译)

 

makefile代码如下:

#gcc -o client client.c -I/openssl_gx/include -L/openssl_gx/lib -lssl -lcrypto -ldl
#gcc -o server serv.c -I/openssl_gx/include -L/openssl_gx/lib -lssl -lcrypto -ldl

CC = gcc
INCLUDE = -I/openssl_gx/include
LIBPATH = -L/openssl_gx/lib
LIB = -lssl -lcrypto -ldl

client:
	$(CC) -o client client.c $(INCLUDE) $(LIBPATH) $(LIB)
	
server:
	$(CC) -o server server.c $(INCLUDE) $(LIBPATH) $(LIB)

all: client server
	
clean:
	rm -f client
	rm -f server

 

client.c代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/err.h>

int main() {
	int iResult = 0;
	BIO * bio = NULL;
	SSL * ssl = NULL;
	SSL_CTX * ctx = NULL;
	X509 * pX509 = NULL;
	const SSL_METHOD * sslMethod = NULL;

	char commonName[512] = { 0 };
	X509_NAME * pX509_NAME = NULL;
	char szClientMsg[] = "I am guoxu.\n";

	unsigned int uiRecBytes = 0;

	unsigned char baRecBuffer[1024];
	unsigned char * pbRecFinish = NULL;
	unsigned char * pbRecBak = NULL;
	unsigned int uiRecBakLen = 0;

	char * pMsgline = NULL;

	SSL_library_init();
	ERR_load_BIO_strings();
	SSL_load_error_strings();
	OpenSSL_add_all_algorithms();

	do {
		sslMethod = TLSv1_client_method(); /* Load ssl method. */
		if(NULL == sslMethod) {
			printf("TLSv1_client_method err: %s\n",ERR_error_string(ERR_get_error(),NULL));
			iResult = -1;
			break;
		}

		ctx = SSL_CTX_new(sslMethod); /* Create a new ssl context. */
		if( NULL == ctx) {
			printf("SSL_CTX_new err: %s\n",ERR_error_string(ERR_get_error(),NULL));
			iResult = -2;
			break;
		}

		if(0 == SSL_CTX_load_verify_locations(ctx,"/openssl_gx/cers/ca.crt",NULL)) { /* Load CA certification. */
			printf("SSL_CTX_load_verify_locations err:%s\n",
				ERR_error_string(ERR_get_error(),
				NULL));
			iResult = -3;
			break;
		}
	
		if(0 == SSL_CTX_use_certificate_file(ctx, "/openssl_gx/cers/client.crt", /* Load client certification file. */
						SSL_FILETYPE_PEM)) {
			printf("SSL_CTX_use_certificate_file err: %s\n",
				ERR_error_string(ERR_get_error(),NULL));
			iResult = -4;
			break;
		}

		if(0 == SSL_CTX_use_PrivateKey_file(ctx,"/openssl_gx/cers/client.key", /* Load client private key. */
						SSL_FILETYPE_PEM)) {
			iResult = -5;
			break;
		}

		if(0 == SSL_CTX_check_private_key(ctx)) { /* Check whether the private key match with the certification file. */
			printf("SSL_CTX_check_private_key err: %s\n",
				ERR_error_string(ERR_get_error(), NULL));
			iResult = -6;
			break;
		}
		
		bio = BIO_new_ssl_connect(ctx);
		if(NULL == bio) {
			printf("BIO_new_ssl_connect err: %s\n",
				ERR_error_string(ERR_get_error(), NULL));
			iResult = -7;
			break;
		}

		BIO_get_ssl(bio, &ssl);
		SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
		BIO_set_conn_hostname(bio, "127.0.0.1:443");
		
		if(0 >= BIO_do_connect(bio)){ /* Create a socket connection and then ssl handshake.*/
			printf("BIO_do_connect err: %s\n",
				ERR_error_string(ERR_get_error(), NULL));
			iResult = -8;
			break;
		}

		if(X509_V_OK != SSL_get_verify_result(ssl)) {  /* Check whether the ssl connection is created successfully. */
			printf("SSL_get_verify_result err: %s\n",
				ERR_error_string(ERR_get_error(), NULL));
			iResult = -9;
			break;
		}	

		pX509 = SSL_get_peer_certificate(ssl); /* Get the server certification. */
		if(NULL == pX509) {
			printf("SSL_get_peer_certificate err: %s\n",
				ERR_error_string(ERR_get_error(), NULL));
			iResult = -10;
			break;
		}

		pX509_NAME = X509_get_subject_name(pX509);
		if(NULL == pX509_NAME) {
			printf("X509_get_subject_name err: %s\n",
				ERR_error_string(ERR_get_error(), NULL));
			iResult = -10;
			break;
		}

		X509_NAME_get_text_by_NID(pX509_NAME, NID_commonName,commonName,512);
		if(0 != strcasecmp(commonName,"guoxu")) {
			printf("Certificate`s name guoxu != %s\n",commonName);
			iResult = -11;
			break;
		}

		if(0 >= BIO_write(bio,szClientMsg, strlen(szClientMsg))) { /* Send message to the server. */
			printf("Send a string to server failed.\n");
			iResult = -12;
			break;
		}

		
	} while(0);


	/* Close the ssl connection and clear the context. */	
	if(NULL != bio) {
		BIO_free_all(bio);
	}

	if(NULL != ctx) {
		SSL_CTX_free(ctx);
	}

	return iResult;
}

 

使用ssl接口的client程序:

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.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 MAXDATASIZE 1024
#define SERVERIP "127.0.0.1"
#define SERVERPORT 443

/* Make these what you want for cert & key files */
#define CERTF "/openssl_gx/cers/client.crt"
#define KEYF "/openssl_gx/cers/client.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) {

	char buf[MAXDATASIZE];
	int sockfd, numbytes;
	struct sockaddr_in server_addr;
	const SSL_METHOD *meth;
	int err;
	SSL_CTX* ctx;
	SSL*     ssl;

	if((sockfd = socket(AF_INET, SOCK_STREAM,0)) == -1) {
		perror("Socket error.\n");
		return 1;
	}	
	memset(&server_addr, 0, sizeof(struct sockaddr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(SERVERPORT);
	server_addr.sin_addr.s_addr = inet_addr(SERVERIP);
	
	if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) == -1) {
		perror("connect error.\n");
		return 1;
	}
	printf("Connected %d\n", sockfd);

	SSL_library_init();
	SSL_load_error_strings();
	SSLeay_add_ssl_algorithms();
  	meth = TLSv1_client_method(); /* Add ssl method. */
        if(NULL == meth) {
             printf("TLSv1_client_method err: %s\n",ERR_error_string(ERR_get_error(),NULL));
             exit(-1);
        }

	ctx = SSL_CTX_new (meth);  /* Create a new ssl context. */
  	if (!ctx) {
    		ERR_print_errors_fp(stderr);
    		exit(2);
  	}

	printf("111\n");
  	if(0 == SSL_CTX_load_verify_locations(ctx,"/openssl_gx/cers/ca.crt",NULL))  { /* Load CA certification file. */
        	ERR_print_errors_fp(stderr);
        	exit(15);
  	}
	printf("222\n");
  	if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) { /* Load server certification file. */
    		ERR_print_errors_fp(stderr);
    		exit(3);
  	}
	printf("333\n");
  	if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) { /* Load server private key file. */
    		ERR_print_errors_fp(stderr);
    		exit(4);
  	}
	printf("444\n");
  	if (!SSL_CTX_check_private_key(ctx)) { /* Check whether the private key match with the certification file. */
    		fprintf(stderr,"Private key does not match the certificate public key\n");
    		exit(5);
  	}
	printf("555\n");
	 /* TCP connection is ready. Do server side SSL. */
	ssl = SSL_new (ctx);
  	CHK_NULL(ssl);
  	SSL_set_fd (ssl, sockfd);
	
	err = SSL_connect (ssl);
	CHK_SSL(err);
	printf("SSL connect OK.\n");

	err = SSL_write(ssl,"I am guoxu.\n",strlen("I am guoxu.\n"));
	CHK_SSL(err);
	SSL_free(ssl);
	close(sockfd);
	SSL_CTX_free(ctx);
	
	return 0;
}


 


没有全部使用BIO的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>       /* SSLeay stuff */
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

/* Make these what you want for cert & key files */
#define CERTF "/openssl_gx/cers/server.crt"
#define KEYF "/openssl_gx/cers/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); }

static int s_server_verify = SSL_VERIFY_NONE;

int verify_callback_server(int ok, X509_STORE_CTX * ctx) {
        printf("verify_callback_server.\n");
        return ok;
}

void main ()
{
  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];
  const SSL_METHOD *meth;

  SSL_load_error_strings();
  SSLeay_add_ssl_algorithms();
  meth = TLSv1_server_method(); /* Add ssl method. */
  ctx = SSL_CTX_new (meth);  /* Create a new ssl context. */
  if (!ctx) {
    ERR_print_errors_fp(stderr);
    exit(2);
  }
 
  if(0 == SSL_CTX_load_verify_locations(ctx,"/openssl_gx/cers/ca.crt",NULL))  { /* Load CA certification file. */
	ERR_print_errors_fp(stderr);
	exit(15);	
  }
  if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) { /* Load server certification file. */
    ERR_print_errors_fp(stderr);
    exit(3);
  }
  if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) { /* Load server private key file. */
    ERR_print_errors_fp(stderr);
    exit(4);
  }

  if (!SSL_CTX_check_private_key(ctx)) { /* Check whether the private key match with the certification file. */
    fprintf(stderr,"Private key does not match the certificate public key\n");
    exit(5);
  }

  /* Indicate that the server need verify the client certification file. */
  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);
  
  /* Prepare TCP socket for receiving connections */
  listen_sd = socket (AF_INET, SOCK_STREAM, 0);
  CHK_ERR(listen_sd, "socket");
  
  memset (&sa_serv, '\0', sizeof(sa_serv));
  sa_serv.sin_family      = AF_INET;
  sa_serv.sin_addr.s_addr = INADDR_ANY;
  sa_serv.sin_port        = htons (443);          /* Server Port number */
  												  /* Server listening port should set to 443, if not the wireshark can not show the
												     ssl communication. */
  
  err = bind(listen_sd, (struct sockaddr*) &sa_serv, sizeof (sa_serv));
  CHK_ERR(err, "bind");
	     
  /* Receive a TCP connection. */
  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);

  printf ("Connection from %lx, port %x\n",
	  sa_cli.sin_addr.s_addr, sa_cli.sin_port);
  
  /* TCP connection is ready. Do server side SSL. */
  ssl = SSL_new (ctx);
  CHK_NULL(ssl);
  SSL_set_fd (ssl, sd);
  err = SSL_accept (ssl);
  CHK_SSL(err);
  
  /* Get the cipher - opt */
  printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
  
  /* Get client's certificate (note: beware of dynamic allocation) - opt */
  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);
    
    /* We could do all sorts of certificate verification stuff here before
       deallocating the certificate. */
    
    X509_free (client_cert);
  } else {
    printf ("Client does not have certificate.\n");
  }
  
  /* DATA EXCHANGE - Receive message and send reply. */

  err = SSL_read (ssl, buf, sizeof(buf) - 1);                   CHK_SSL(err);
  buf[err] = '\0';
  printf ("Got %d chars:'%s'\n", err, buf);
  
  //err = SSL_write (ssl, "I hear you.", strlen("I hear you."));  CHK_SSL(err);

  /* Clean up. */
  close (sd);
  SSL_free (ssl);
  SSL_CTX_free (ctx);
}


尽力使用BIO的server.c代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>

#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/crypto.h>

#define ERR_OK	0
#define ERR_HAPPENED 1

#define TRACE_ENABLED 1
#define TRACE0(x) printf(x)

SSL_CTX *ctx;
BIO * sbio, * bbio, * acpt, * out;
SSL * ssl;

int main ()
{

	char tmpbuf[1024];
	ERR_load_crypto_strings();  //registers the error strings for all libcrypto functions. 
	ERR_load_SSL_strings(); 	//registers the error strings for libssl functions. 
	OpenSSL_add_ssl_algorithms(); //same as SSLeay_add_ssl_algorithms();
	
	ctx = SSL_CTX_new(TLSv1_server_method()); //Create a ssl context.
	if(ctx == NULL) {  //Must check NULL here.
		ERR_print_errors_fp(stderr);
		return ERR_HAPPENED;
	}
	
	if(!SSL_CTX_use_certificate_file(ctx,"/openssl_gx/cers/server.crt",SSL_FILETYPE_PEM)) {
		//ERR_print_errors_fp(stderr);
		return ERR_HAPPENED;
	} else {
		TRACE0("====> Step 1 : Load server certificate file done.\n");
	}
	
	if(!SSL_CTX_use_PrivateKey_file(ctx,"/openssl_gx/cers/server.key",SSL_FILETYPE_PEM)) {
		ERR_print_errors_fp(stderr);
		return ERR_HAPPENED;
	} else {
		TRACE0("====> Step 2: Load server private key done.\n");
	}
	
	if(!SSL_CTX_check_private_key(ctx)) {
		ERR_print_errors_fp(stderr);
		return ERR_HAPPENED;
	} else {
		TRACE0("====> Step 3: Check server private key done.\n");
	}
	
	sbio = BIO_new_ssl(ctx,0); //0: Run as server; non-0: Run as client.
	BIO_get_ssl(sbio, &ssl); //Retrieves the SSL pointer of BIO sbio, it can then be manipulated using the standard SSL library functions.
	if(!ssl) {
		printf("SSL pointer is NULL.\n");
		return ERR_HAPPENED;
	}

	SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); //SSL_MODE_AUTO_RETRY will cause read/write operations to only return after the handshake and successful completion.
	bbio = BIO_new(BIO_f_buffer()); //BIO_f_buffer() returns the buffering BIO method.
									//The BIO_new() function returns a new BIO using method type.
	// Add to the chain.
	sbio = BIO_push(bbio,sbio); //Data writing to bbio will be dealed with sbio.
	acpt = BIO_new_accept("443"); //Create a new accept bio.
	
	/*BIO_set_accept_bios() can be used to set a chain of BIOs which will be duplicated and 
	  prepended to the chain when an incoming connection is received. 
	  This is useful if, for example, a buffering or SSL BIO is required for each connection. 
	  The chain of BIOs must not be freed after this call, 
	  they will be automatically freed when the accept BIO is freed.*/
	
	BIO_set_accept_bios(acpt,sbio); //append sbio to acpt.
	out = BIO_new_fp(stdout,BIO_NOCLOSE);

	/*BIO_do_accept() serves two functions. 
	  When it is first called, after the accept BIO has been setup, 
	  it will attempt to create the accept socket and bind an address to it. 
	  Second and subsequent calls to BIO_do_accept() will await an incoming connection, 
	  or request a retry in non blocking mode. */
	  
	/* The first BIO_do_accept() setup the accept BIO. */
	if(BIO_do_accept(acpt) <= 0) {
		ERR_print_errors_fp(stderr);
		return ERR_HAPPENED;
	}
	TRACE0("====>Step 4: Waiting for the incoming connection...\n");
	
	/* The second BIO_do_accept() wait for the incoming connection. */
	if(BIO_do_accept(acpt) <= 0) {
		ERR_print_errors_fp(stderr);
		return ERR_HAPPENED;
	}
	
	TRACE0("====>Step 5: An connection comes.\n");

	sbio = BIO_pop(acpt);
	BIO_free_all(acpt);

	if(BIO_do_handshake(sbio) <= 0) {
		ERR_print_errors_fp(stderr);
		return ERR_HAPPENED;
	}
	TRACE0("====>Step 6: Handshake done.\n");
	memset(tmpbuf,0,1024 * sizeof(char));
	
	BIO_read(sbio,tmpbuf,1024);
	printf("\t\tRead message from client is:%s\n",tmpbuf);
	BIO_free_all(sbio);
	return 0;
}


 

====完。
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值