Openssl 範例程式:建立SSL連線
說來很神奇,雖然我研究所的研究主題是「網路安全」,但我在研究所卻從來沒撰寫使用 openssl 函式庫的程式。第一次撰寫是在工作上有需求而去使用一些加密的演算法,這次則是工作尚有需求,要去建立 DTLS 的安全連線。DTLS?等一下,標題不是 SSL 嗎?說來話長,要使用 DTLS 的相關 API,按照 openssl 網站的說法,最好是使用 openssl 1.0 以後的版本,問題是客戶所使用的版本是 0.9.8d 的。理論上來說,0.9.8d 的版本也有支援 DTLS,所以我不能拒絕客戶的要求(我會努力繼續嘗試說服客戶的),但還是要做好在 0.9.8d 的開發。問題是兩個版本的寫法不一樣啊(在 1.0.0 以後的版本,有一個 dtlsv1_listen 的函式,簡單來說就是在 UDP 上面實作 listen 的行為。)~雖然已經請同事搞定 1.0 上面的寫法,但 0.9.8 上的寫法還要再研究。所以我就進去開始研究囉。下面就是研究到一半(SSL)的心得。下面的程式碼是從 HP 的網站上改過來的。
ssl_server.c
ssl_client.c
下面是 Makefile:
ssl_server.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <netdb.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <openssl/crypto.h> #include <openssl/ssl.h> #include <openssl/err.h> #define RSA_SERVER_CERT "serverca.crt" #define RSA_SERVER_KEY "serverca.key" #define RSA_SERVER_CA_CERT "rootca.crt" #define ON 1 #define OFF 0 #define RETURN_NULL(x) if ((x)==NULL) exit(1) #define RETURN_ERR(err,s) if ((err)==-1) { perror(s); exit(1); } #define RETURN_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(1); } int main() { int err; int verify_client = ON; /* To verify a client certificate, set ON */ int listen_sock; int sock; struct sockaddr_in sa_serv; struct sockaddr_in sa_cli; unsigned int client_len; char *str; char buf[4096]; char client_ip_str[INET_ADDRSTRLEN]; SSL_CTX *ctx; SSL *ssl; SSL_METHOD *meth; X509 *client_cert = NULL; short int s_port = 5555; /*----------------------------------------------------------------*/ /* Load encryption & hashing algorithms for the SSL program */ SSL_library_init(); /* Load the error strings for SSL & CRYPTO APIs */ SSL_load_error_strings(); /* Create a SSL_METHOD structure (choose a SSL/TLS protocol version) */ meth = SSLv3_method(); /* Create a SSL_CTX structure */ ctx = SSL_CTX_new(meth); if (!ctx) { ERR_print_errors_fp(stderr); exit(1); } /* Load the server certificate into the SSL_CTX structure */ if ( SSL_CTX_use_certificate_file(ctx, RSA_SERVER_CERT, SSL_FILETYPE_PEM) <= 0 ) { ERR_print_errors_fp(stderr); exit(1); } /* Load the private-key corresponding to the server certificate */ if ( SSL_CTX_use_PrivateKey_file(ctx, RSA_SERVER_KEY, SSL_FILETYPE_PEM) <= 0 ) { ERR_print_errors_fp(stderr); exit(1); } /* Check if the server certificate and private-key matches */ if ( !SSL_CTX_check_private_key(ctx) ) { fprintf(stderr,"Private key does not match the certificate public key\n"); exit(1); } if( verify_client == ON ) { /* Load the RSA CA certificate into the SSL_CTX structure */ if ( !SSL_CTX_load_verify_locations( ctx, RSA_SERVER_CA_CERT, NULL ) ) { ERR_print_errors_fp(stderr); exit(1); } /* Set to require peer (client) certificate verification */ SSL_CTX_set_verify( ctx, SSL_VERIFY_PEER, NULL ); /* Set the verification depth to 1 */ SSL_CTX_set_verify_depth(ctx,1); } /* ----------------------------------------------- */ /* Set up a TCP socket */ listen_sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); RETURN_ERR(listen_sock, "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 (s_port); /* Server Port number */ err = bind(listen_sock, (struct sockaddr*)&sa_serv,sizeof(sa_serv)); RETURN_ERR( err, "bind" ); /* Wait for an incoming TCP connection. */ err = listen( listen_sock, 5 ); RETURN_ERR(err, "listen"); client_len = sizeof( sa_cli ); /* Socket for a TCP/IP connection is created */ sock = accept( listen_sock, (struct sockaddr*)&sa_cli, &client_len ); RETURN_ERR(sock, "accept"); close (listen_sock); inet_ntop( AF_INET, &( sa_cli.sin_addr.s_addr ), client_ip_str, INET_ADDRSTRLEN ); printf ("Connection from %s, port %d\n", client_ip_str, sa_cli.sin_port); /* ----------------------------------------------- */ /* TCP connection is ready. */ /* A SSL structure is created */ ssl = SSL_new(ctx); RETURN_NULL(ssl); /* Assign the socket into the SSL structure (SSL and socket without BIO) */ SSL_set_fd(ssl, sock); /* Perform SSL Handshake on the SSL server */ err = SSL_accept(ssl); RETURN_SSL(err); /* Informational output (optional) */ printf("SSL connection using %s\n", SSL_get_cipher (ssl)); if (verify_client == ON) { /* Get the client's certificate (optional) */ 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); RETURN_NULL(str); printf ("\t subject: %s\n", str); free (str); str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0); RETURN_NULL(str); printf ("\t issuer: %s\n", str); free (str); X509_free(client_cert); } else { printf("The SSL client does not have certificate.\n"); } } /*------- DATA EXCHANGE - Receive message and send reply. -------*/ /* Receive data from the SSL client */ err = SSL_read(ssl, buf, sizeof(buf) - 1); RETURN_SSL(err); buf[err] = '\0'; printf ("Received %d chars:'%s'\n", err, buf); /* Send data to the SSL client */ err = SSL_write( ssl, "This message is from the SSL server", strlen("This message is from the SSL server")); RETURN_SSL(err); /*--------------- SSL closure ---------------*/ /* Shutdown this side (server) of the connection. */ err = SSL_shutdown(ssl); RETURN_SSL(err); /* Terminate communication on a socket */ err = close(sock); RETURN_ERR(err, "close"); /* Free the SSL structure */ SSL_free(ssl); /* Free the SSL_CTX structure */ SSL_CTX_free(ctx); return 0; }
ssl_client.c
#include <stdio.h> #include <string.h> #include <errno.h> #include <netdb.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <openssl/crypto.h> #include <openssl/ssl.h> #include <openssl/err.h> #define RETURN_NULL(x) if ((x)==NULL) exit (1) #define RETURN_ERR(err,s) if ((err)==-1) { perror(s); exit(1); } #define RETURN_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(1); } #define RSA_CLIENT_CERT "clientca.crt" #define RSA_CLIENT_KEY "clientca.key" #define RSA_CLIENT_CA_CERT "rootca.crt" #define ON 1 #define OFF 0 int main() { int err; int verify_client = ON; /* To verify a client certificate, set ON */ int sock; struct sockaddr_in server_addr; char *str; char buf [4096]; char hello[80]; SSL_CTX *ctx; SSL *ssl; SSL_METHOD *meth; X509 *server_cert; short int s_port = 5555; const char *s_ipaddr = "127.0.0.1"; /*----------------------------------------------------------*/ printf ("Message to be sent to the SSL server: "); fgets ( hello, 80, stdin ); /* Load encryption & hashing algorithms for the SSL program */ SSL_library_init(); /* Load the error strings for SSL & CRYPTO APIs */ SSL_load_error_strings(); /* Create an SSL_METHOD structure (choose an SSL/TLS protocol version) */ meth = SSLv3_method(); /* Create an SSL_CTX structure */ ctx = SSL_CTX_new(meth); RETURN_NULL(ctx); /*----------------------------------------------------------*/ if( verify_client == ON ) { /* Load the client certificate into the SSL_CTX structure */ if ( SSL_CTX_use_certificate_file(ctx, RSA_CLIENT_CERT, SSL_FILETYPE_PEM) <= 0 ) { ERR_print_errors_fp( stderr ); exit(1); } /* Load the private-key corresponding to the client certificate */ if ( SSL_CTX_use_PrivateKey_file( ctx, RSA_CLIENT_KEY, SSL_FILETYPE_PEM ) <= 0 ) { ERR_print_errors_fp(stderr); exit(1); } /* Check if the client certificate and private-key matches */ if ( !SSL_CTX_check_private_key(ctx)) { fprintf(stderr,"Private key does not match the certificate public key\n " ); exit(1); } } /* Load the RSA CA certificate into the SSL_CTX structure */ /* This will allow this client to verify the server's */ /* certificate. */ if ( !SSL_CTX_load_verify_locations( ctx, RSA_CLIENT_CA_CERT, NULL ) ) { ERR_print_errors_fp(stderr); exit(1); } /* Set flag in context to require peer (server) certificate verification */ SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); SSL_CTX_set_verify_depth(ctx,1); /* ------------------------------------------------------------- */ /* Set up a TCP socket */ sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); RETURN_ERR(sock, "socket"); memset (&server_addr, '\0', sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(s_port); /* Server Port number */ server_addr.sin_addr.s_addr = inet_addr(s_ipaddr); /* Server IP */ /* Establish a TCP/IP connection to the SSL client */ err = connect(sock, (struct sockaddr*) &server_addr, sizeof(server_addr)); RETURN_ERR(err, "connect"); /* ----------------------------------------------- */ /* An SSL structure is created */ ssl = SSL_new (ctx); RETURN_NULL(ssl); /* Assign the socket into the SSL structure (SSL and socket without BIO) */ SSL_set_fd(ssl, sock); /* Perform SSL Handshake on the SSL client */ err = SSL_connect(ssl); RETURN_SSL(err); /* Informational output (optional) */ printf ("SSL connection using %s\n", SSL_get_cipher (ssl)); /* Get the server's certificate (optional) */ server_cert = SSL_get_peer_certificate (ssl); if ( server_cert != NULL ) { printf ("Server certificate:\n"); str = X509_NAME_oneline(X509_get_subject_name(server_cert),0,0); RETURN_NULL(str); printf ("\t subject: %s\n", str); free (str); str = X509_NAME_oneline(X509_get_issuer_name(server_cert),0,0); RETURN_NULL(str); printf ("\t issuer: %s\n", str); free(str); X509_free (server_cert); } else { printf("The SSL server does not have certificate.\n"); } /*-------- DATA EXCHANGE - send message and receive reply. -------*/ /* Send data to the SSL server */ err = SSL_write(ssl, hello, strlen(hello)); RETURN_SSL(err); /* Receive data from the SSL server */ err = SSL_read(ssl, buf, sizeof(buf)-1); RETURN_SSL(err); buf[err] = '\0'; printf ("Received %d chars:'%s'\n", err, buf); /*--------------- SSL closure ---------------*/ /* Shutdown the client side of the SSL connection */ err = SSL_shutdown(ssl); RETURN_SSL(err); /* Terminate communication on a socket */ err = close(sock); RETURN_ERR(err, "close"); /* Free the SSL structure */ SSL_free(ssl); /* Free the SSL_CTX structure */ SSL_CTX_free(ctx); return 0; }
下面是 Makefile:
all: gcc -Wall ssl_client.c -o ssl_client -lssl -lcrypto gcc -Wall ssl_server.c -o ssl_server -lssl -lcrypto clean: rm ssl_client ssl_server
留言
張貼留言