sudo sudo apt-get install libssl-dev 2.服务器端实现代码
#include #define MAXBUF 1024 int main(int argc, char **argv) { int sockfd, new_fd; socklen_t len; struct sockaddr_in my_addr, their_addr; unsigned int myport, lisnum; char buf[MAXBUF + 1]; SSL_CTX *ctx; if (argv[1]) myport = atoi(argv[1]); else myport = 7838; if (argv[2]) lisnum = atoi(argv[2]); else lisnum = 2; /* SSL 库初始化 */ SSL_library_init(); /* 载入所有 SSL 算法 */ OpenSSL_add_all_algorithms(); /* 载入所有 SSL 错误消息 */ SSL_load_error_strings(); /* 以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Content Text */ ctx = SSL_CTX_new(SSLv23_server_method()); /* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 单独表示 V2 或 V3标准 */ if (ctx == NULL) { ERR_print_errors_fp(stdout); exit(1); } /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */ if (SSL_CTX_use_certificate_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); exit(1); } /* 载入用户私钥 */ if (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); exit(1); } /* 检查用户私钥是否正确 */ if (!SSL_CTX_check_private_key(ctx)) { ERR_print_errors_fp(stdout); exit(1); } /* 开启一个 socket 监听 */ if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror(\"socket\"); exit(1); } else printf(\"socket created\\n\"); bzero(&my_addr, sizeof(my_addr)); my_addr.sin_family = PF_INET; my_addr.sin_port = htons(myport); my_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { perror(\"bind\"); exit(1); } else printf(\"binded\\n\"); if (listen(sockfd, lisnum) == -1) { perror(\"listen\"); exit(1); } else printf(\"begin listen\\n\"); while (1) { SSL *ssl; len = sizeof(struct sockaddr); /* 等待客户端连上来 */ if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len)) == -1) { perror(\"accept\"); exit(errno); } else printf(\"server: got connection from %s, port %d, socket %d\\n\ inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd); /* 基于 ctx 产生一个新的 SSL */ ssl = SSL_new(ctx); /* 将连接用户的 socket 加入到 SSL */ SSL_set_fd(ssl, new_fd); /* 建立 SSL 连接 */ if (SSL_accept(ssl) == -1) { perror(\"accept\"); close(new_fd); break; } /* 开始处理每个新连接上的数据收发 */ bzero(buf, MAXBUF + 1); strcpy(buf, \"server->client\"); /* 发消息给客户端 */ len = SSL_write(ssl, buf, strlen(buf)); if (len <= 0) { printf (\"消息'%s'发送失败!错误代码是%d,错误信息是'%s'\\n\ buf, errno, strerror(errno)); goto finish; } else printf(\"消息'%s'发送成功,共发送了%d个字节!\\n\ buf, len); bzero(buf, MAXBUF + 1); /* 接收客户端的消息 */ len = SSL_read(ssl, buf, MAXBUF); if (len > 0) printf(\"接收消息成功:'%s',共%d个字节的数据\\n\ buf, len); else printf (\"消息接收失败!错误代码是%d,错误信息是'%s'\\n\ errno, strerror(errno)); /* 处理每个新连接上的数据收发结束 */ finish: /* 关闭 SSL 连接 */ SSL_shutdown(ssl); /* 释放 SSL */ SSL_free(ssl); /* 关闭 socket */ close(new_fd); } /* 关闭监听的 socket */ close(sockfd); /* 释放 CTX */ SSL_CTX_free(ctx); return 0; } 2. 客户端实现代码 dfa #include #define MAXBUF 1024 void ShowCerts(SSL * ssl) { X509 *cert; char *line; cert = SSL_get_peer_certificate(ssl);// 该函数可以从SSL套接字中提取对方的证书信息,这些 信息已经被SSL验证过了。 X509_NAME *X509_get_subject_name(X509 *a); 该函数得到证书所用者的名字。返回值是X509 if (cert != NULL) { printf(\"数字证书信息:\\n\"); line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); printf(\"证书: %s\\n\ free(line); line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); printf(\"颁发者: %s\\n\ free(line); X509_free(cert); } else printf(\"无证书信息!\\n\"); } int main(int argc, char **argv) { int sockfd, len; struct sockaddr_in dest; char buffer[MAXBUF + 1]; SSL_CTX *ctx; SSL *ssl; if (argc != 3) { printf(\"参数格式错误!正确用法如下:\\n\\%s IP地址 端口\\n\比如:\%s 127.0.0.1 80\\n此程序用来从某个\" \"IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息\ argv[0], argv[0]); exit(0); } /* SSL 库初始化,参看 ssl-server.c 代码 */ SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ctx = SSL_CTX_new(SSLv23_client_method()); 当SSL会话环境申请成功后,还要根据实际的需要设置CTX的属性,通常的设置是指定SSL握手阶段证书的验证方式和加载自己的证书。 if (ctx == NULL) { ERR_print_errors_fp(stdout);?????? exit(1); } /* 创建一个 socket 用于 tcp 通信 */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror(\"Socket\"); exit(errno); } printf(\"socket created\\n\"); /* 初始化服务器端(对方)的地址和端口信息 */ bzero(&dest, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(atoi(argv[2]));//7838 if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { inet_aton是一个改进的方法来将一个字符串IP地址转换为一个32位的网络序列IP地址 perror(argv[1]); exit(errno); } printf(\"address created\\n\"); /* 连接服务器 */ if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) { perror(\"Connect \"); exit(errno); } printf(\"server connected\\n\"); /* 基于 ctx 产生一个新的 SSL */ ssl = SSL_new(ctx);// 申请一个SSL套接字 SSL_set_fd(ssl, sockfd);// //绑定读写套接字 /* 建立 SSL 连接 */ if (SSL_connect(ssl) == -1)// 而对服务器来讲,则应使用函数SSL_ accept ( )替代传统的函数accept ( )来完成握手过程: ERR_print_errors_fp(stderr);?? else { printf(\"Connected with %s encryption\\n\ ShowCerts(ssl);这个时候从服务器接受服务器提供的本身认证!!!! } /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */ bzero(buffer, MAXBUF + 1); /* 接收服务器来的消息 */ len = SSL_read(ssl, buffer, MAXBUF); if (len > 0) printf(\"接收消息成功:'%s',共%d个字节的数据\\n\ buffer, len); else { printf (\"消息接收失败!错误代码是%d,错误信息是'%s'\\n\ errno, strerror(errno)); goto finish; } bzero(buffer, MAXBUF + 1); strcpy(buffer, \"from client->server\"); /* 发消息给服务器 */ len = SSL_write(ssl, buffer, strlen(buffer)); if (len < 0) printf (\"消息'%s'发送失败!错误代码是%d,错误信息是'%s'\\n\ buffer, errno, strerror(errno)); else printf(\"消息'%s'发送成功,共发送了%d个字节!\\n\ buffer, len); finish: /* 关闭连接 */ SSL_shutdown(ssl); SSL_free(ssl); close(sockfd); SSL_CTX_free(ctx); return 0; } 3. 编译方式: gcc -o ssl_server ssl_server.c -Wall -g –lssl//gcc -Wall sever.c -o server -lssl -lcrypto -ldl gcc -o ssl_client ssl_client.c -Wall -g -lssl 4. 生产私钥和证书 openssl genrsa -out privkey.pem 1024 openssl req -new -x509 -key privkey.pem -out CAcert.pem -days 1095 5. 程序运行方式: ./ssl_server 7838 1 CAcert.pem privkey.pem ./ssl_client 127.0.0.1 7838 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-04/34523.htm 于OpenSSL的程序可以被分为两个部分:客户机和服务器,使用SSL协议使通信双方可以相互验证对方身份的真实性,并且能够保证数据的完整性和机密性。建立SSL通信的过程如图2所示。 图2 SSL通信过程 SSL通信模型采用标准的C/S结构,除了在TCP层上进行传输之外,与普通的网络通信协议没有太大的区别,基于OpenSSL的程序都要遵循以下几个步骤: (1 ) OpenSSL初始化 在使用OpenSSL之前,必须进行相应的协议初始化工作,这可以通过下面的函数实现: int SSL_library_int(void); (2 ) 选择会话协议 在利用OpenSSL开始SSL会话之前,需要为客户端和服务器制定本次会话采用的协议,目前能够使用的协议包括TLSv1.0、SSLv2、SSLv3、SSLv2/v3。 需要注意的是,客户端和服务器必须使用相互兼容的协议,否则SSL会话将无法正常进行。 (3 ) 创建会话环境 在OpenSSL中创建的SSL会话环境称为CTX,使用不同的协议会话,其环境也 不一样的。申请SSL会话环境的OpenSSL函数是: SSL_CTX *SSL_CTX_new(SSL_METHOD * method); 当SSL会话环境申请成功后,还要根据实际的需要设置CTX的属性,通常的设置是指定SSL握手阶段证书的验证方式和加载自己的证书。制定证书验证方式的函数是: int SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int(*verify_callback),int(X509_STORE_CTX *)); 为SSL会话环境加载CA证书的函数是: SSL_CTX_load_verify_location(SSL_CTX *ctx,const char *Cafile,const char *Capath); 为SSL会话加载用户证书的函数是: SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file,int type); 为SSL会话加载用户私钥的函数是: SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx,const char* file,int type); 在将证书和私钥加载到SSL会话环境之后,就可以调用下面的函数来验证私钥和证书是否相符: int SSL_CTX_check_private_key(SSL_CTX *ctx); (4) 建立SSL套接字 SSL套接字是建立在普通的TCP套接字基础之上,在建立SSL套接字时可以使用下面的一些函数: SSL *SSl_new(SSL_CTX *ctx); //申请一个SSL套接字 int SSL_set_fd(SSL *ssl,int fd);) //绑定读写套接字 int SSL_set_rfd(SSL *ssl,int fd); //绑定只读套接字 int SSL_set_wfd(SSL *ssl,int fd); //绑定只写套接字 (5) 完成SSL握手 在成功创建SSL套接字后,客户端应使用函数SSL_connect( )替代传统的函数connect( )来完成握手过程: int SSL_connect(SSL *ssl); 而对服务器来讲,则应使用函数SSL_ accept ( )替代传统的函数accept ( )来完成握手过程: int SSL_accept(SSL *ssl); 握手过程完成之后,通常需要询问通信双方的证书信息,以便进行相应的验证,这可以借助于下面的函数来实现: X509 *SSL_get_peer_certificate(SSL *ssl); 该函数可以从SSL套接字中提取对方的证书信息,这些信息已经被SSL验证过了。 X509_NAME *X509_get_subject_name(X509 *a); 该函数得到证书所用者的名字。 (6) 进行数据传输 当SSL握手完成之后,就可以进行安全的数据传输了,在数据传输阶段,需要使用SSL_read( )和SSL_write( )来替代传统的read( )和write( )函数,来完成对套接字的读写操作: int SSL_read(SSL *ssl,void *buf,int num); int SSL_write(SSL *ssl,const void *buf,int num); (7 ) 结束SSL通信 当客户端和服务器之间的数据通信完成之后,调用下面的函数来释放已经申请的SSL资源: int SSL_shutdown(SSL *ssl); //关闭SSL套接字 void SSl_free(SSL *ssl); //释放SSL套接字 void SSL_CTX_free(SSL_CTX *ctx); //释放SSL会话环境 4 结束语 SSL协议采用数字证书进行双端实体认证,用非对称加密算法进行密钥协商,用对称加密算法将数据加密后进行传输以保证数据的保密性,并且通过计算数字摘要来验证数据在传输过程中是否被篡改和伪造,从而为敏感数据在Internet上的传输提供了一种安全保障手段。 OpenSSL是一个开放源代码的SSL协议的产品实现,它采用C语言作为开发语言,具备了跨系统的性能。调用OpenSSL 的函数就可以实现一个SSL加密的安全数据传输通道,从而保护客户端和服务器之间数据的安全。 本示例用多线程实现了一个ssl服务端和一个客户端。 服务端代码如下: #include #include #include \"pthread.h\" #include #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(\"null\\n\"); } #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 err\\n\"); 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 accerr\\n\"); return ; } printf (\"SSL connection using %s\\n\ 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 (\"\ subject: %s\\n\ OPENSSL_free (str); str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0); CHK_NULL(str); printf (\"\ issuer: %s\\n\ 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 err\\n\"); closesocket(s); return; } printf(\"get : %s\\n\#if 0 buf[err] = '\\0'; err = SSL_write (ssl, \"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_t pid; 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(\"err\\n\"); 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 key\\n\"); 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(\"err\\n\"); 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 } } SSL_CTX_free (ctx); return 0; } 客户端代码如下: #include #include #include \"pthread.h\" #include 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_callback\\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 err\\n\"); 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 err\\n\"); 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 err\\n\"); return ; } SSL_set_fd(ssl,sock); err = SSL_connect (ssl); if(err<0) { printf(\"SSL_connect err\\n\"); return; } printf (\"SSL connection using %s\\n\ server_cert = SSL_get_peer_certificate (ssl); printf (\"Server certificate:\\n\"); str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0); printf (\"\ subject: %s\\n\ OPENSSL_free (str); str = X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0); printf (\"\ issuer: %s\\n\ OPENSSL_free (str); X509_free (server_cert); err = SSL_write (ssl, \"Hello World!\ if(err<0) { printf(\"ssl write err\\n\"); return ; } #if 0 memset(buf,0,ONE_BUF_SIZE); err = SSL_read (ssl, buf, sizeof(buf) - 1); if(err<0) { printf(\"ssl read err\\n\"); return ; } buf[err] = '\\0'; printf (\"Got %d chars:'%s'\\n\#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; SOCKET sock; 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_t pid[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 eer\\n\"); 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 for (i=0; i 因篇幅问题不能全部显示,请点此查看更多更全内容