
1 实验原理
1.1 SSL协议
SSL(Secure Socket Layer,安全套接字层)在通信双方间建立了一个传输层安全通道,它使用对称加密来保证通信保密性,使用消息认证码(MAC)来保证数据完整性,并且在建立连接时主要使用PKI对通信双方进行身份认证。Netscape Navigator和Internet Explorer都支持SSL。
1.2 SSL协议的分层结构
SSL协议基于C/S(client/server)模式,位于TCP/IP协议与各种应用层协议之间,为数据通信提供安全支持。它可分为两层:
1. SSL记录协议(SSL Record Protocol)
它建立在可靠的传输控制协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
2. SSL握手协议(SSL Handshake Protocol)
它建立在SSL记录协议之上,用于在实际的数据传输开始前,通信双方进行身份认证、协商加密算法、交换加密密钥等。
作为分层的协议,在每一层,消息可以包含长度、描述和内容字段。SSL发出消息,先把数据分成可管理的块,然后压缩、加密并发出加密后的结果。接收消息后进行解密、验证、解压和重组,再把结果发往更高一层的客户。
1.3 SSL握手协议
SSL握手协议的作用是在正式的秘密通信之前,让服务器和客户之间互相鉴别对方的身份并协商一种会话的加密算法和加密密钥,主要可分为以下两个方面:
(1)客户端和服务器端之间互相验证身份
C/S主要是通过证书来验证,首先通过对方证书中权威发证机构签字的验证,来确定对方拥有的证书是否有效。如果证书有效,接着就从这个证书中提取出公钥,通过对方的签名验证用户是不是假冒的。如果二者都通过,则证明对方的身份是真实可信的。其中服务器对客户端的验证是可选的。
(2)客户端和服务器之间协商安全参数
协商的参数一般包括协议的版本号、密钥交换算法、数据加密算法和Hash算法,通过协商达成一致性。其中版本号一般要求一致。关于密钥交换算法和数据加密算法,是先由客户端向服务器端发送一个列表,其中详细列举了客户端所支持的算法,然后由服务器端从中选取自己支持且加密性能优良的算法,将其返回给客户端,至此完成了算法的协商;最后由客户端随机产生一个用于数据加密的对称密钥,用一种商议好的密钥交换协议将它传给服务器端。SSL支持的密钥交换算法有RSA密钥交换和Diffie-Hellman密钥交换两种。
SSL握手协议顺序图
2 实验内容
2.1实验环境
1、win7
2、VS2005
3、openSSL函数库
2.2实验代码
2.2.1 server端
// ssl_Server.cpp : 定义控制台应用程序的入口点。
//
//server
#include "stdafx.h"
#include #include #include #include "openssl/x509.h" #include "openssl/ssl.h" #include "openssl/err.h" #define MSGLENGTH 1024 #define PORT 8888 #define CACERT "./private/ca.crt" #define SVRCERTF "./certs/server.crt" #define SVRKEYF "./private/server.key" #pragma comment(lib, "wsock32.lib") #pragma comment(lib, "libeay32.lib") #pragma comment(lib, "ssleay32.lib") int _tmain(int argc, _TCHAR* argv[]) { WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); SOCKET sock; SSL_METHOD *meth; SSL_CTX* ctx; SSL* ssl; //SSL初始化 OpenSSL_add_ssl_algorithms(); //SSL错误信息初始化 SSL_load_error_strings(); //创建本次会话所使用的协议 meth = (SSL_METHOD *)TLSv1_server_method(); //申请SSL会话的环境 ctx = SSL_CTX_new(meth); if (NULL == ctx) exit(1); //设置会话的握手方式并加载CA证书 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); if (!SSL_CTX_load_verify_locations(ctx, CACERT, NULL)) { printf("%d \\n\\n下载CA 证书出错!\\n", SSL_CTX_load_verify_locations(ctx, CACERT, NULL)); exit(-1); } //加载服务器端的证书 if (!SSL_CTX_use_certificate_file(ctx, SVRCERTF, SSL_FILETYPE_PEM)) { ERR_print_errors_fp(stderr); exit(1); } //加载服务器端的私钥 if (!SSL_CTX_use_PrivateKey_file(ctx, SVRKEYF, SSL_FILETYPE_PEM)) { ERR_print_errors_fp(stderr); exit(1); } //检查服务器端的证书和私钥是否匹配 if (!SSL_CTX_check_private_key(ctx)) { printf("\\n\\n私钥与证书公钥不匹配!\\n"); exit(1); } //加密方式 SSL_CTX_set_cipher_list(ctx, "RC4-MD5"); //处理握手多次 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); /*以下是正常的TCP socket建立过程.............................. */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { printf("\\n\\nSOCKET有问题. \\n"); return 0; } sockaddr_in addr; memset(&addr, '\\0', sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); /* Server Port number */ addr.sin_addr.s_addr = INADDR_ANY; //绑定sock int nResult = bind(sock, (sockaddr *)&addr, sizeof(addr)); if (nResult == SOCKET_ERROR) { printf("\\n\\n绑定SOCKET有问题. \\n"); return 0; } printf("\\n**************************************\\n"); printf("\\n服务器启动成功,端口:%d\\n\\n等待连接.....\\n", PORT); /*接受TCP链接*/ sockaddr_in sa_cli; int err = listen(sock, 5); if (-1 == err) exit(1); int client_len = sizeof(sa_cli); int ss = accept(sock, (struct sockaddr *) &sa_cli, &client_len); if (ss == -1) { exit(1); } closesocket(sock); printf("\\n连接端口:%d, port %d\\n", sa_cli.sin_addr.s_addr, sa_cli.sin_port); /* TCP 链接已建立.开始SSL 握手过程.......................... */ //绑定套接字 ssl = SSL_new(ctx); if (NULL == ssl) exit(1); if (!SSL_set_fd(ssl, ss)) { printf("\\n连接失败!\\n"); exit(1); } //SSL握手 //SSL_accept(ssl); int k = SSL_accept(ssl); if ( k != 1) { printf("%d\\n", k); printf("\\n连接失败!\\n"); exit(1); } //进行信息验证 X509 *client_cert; client_cert = SSL_get_peer_certificate(ssl); printf("\\n发现客户端尝试连接\\n"); if (client_cert != NULL) { printf ("\\n客服端证书:\\n"); //读取证书subject名并显示 char *str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0); if (NULL == str) { printf("\\n认证出错!\\n"); exit(1); } printf("\\n主题:%s\\n", str); //读取证书的issuer名并显示 str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0); if (NULL == str) { printf("\\n证书名为空\\n"); exit(1); } printf("\\n颁发者:%s\\n", str); printf("\\n连接成功\\n"); X509_free (client_cert);/*如不再需要,需将证书释放*/ OPENSSL_free(str); } else { printf("\\n找不到客户端的认证证书\\n"); exit(1); } char buf[MSGLENGTH]; SSL_write(ssl, "\\n服务器连接成功!\\n", strlen("\\n服务器连接成功!\\n")); printf("\\n监听客服端:\\n\\n"); while (1) { err = SSL_read(ssl, buf, sizeof(buf)); buf[err] = '\\0'; if (!strcmp(buf, "退出")) { //关闭套接字 SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); WSACleanup(); getchar(); break; } else printf(">> %s\\n", buf); } return 0; } 2.2.2 client端 // ssl_Client.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" //client #include //#include #include #include #include "openssl/x509.h" #include "openssl/ssl.h" #include "openssl/err.h" #include "openssl/rand.h" #include #define PORT 8888 #define SERVER "127.0.0.1" //#define SERVER "172.31.32.176" #define CACERT "./private/ca.crt" #define MYCERTF "./certs/client.crt" #define MYKEYF "./private/client.key" #define MSGLENGTH 1024 #pragma comment(lib, "wsock32.lib") #pragma comment(lib, "libeay32.lib") #pragma comment(lib, "ssleay32.lib") int _tmain(int argc, _TCHAR* argv[]) { WSADATA wsadata; WSAStartup(MAKEWORD(2,2), &wsadata); sockaddr_in sin; int seed_int[100]; /*存放随机序列*/ SSL *ssl; const SSL_METHOD *meth; SSL_CTX *ctx; //SSL初始化 OpenSSL_add_ssl_algorithms(); //SSL错误信息初始化 SSL_load_error_strings(); //创建本次会话所使用的协议 meth = TLSv1_client_method(); //申请SSL会话的环境 ctx = SSL_CTX_new(meth); if (NULL == ctx) exit(1); //设置会话的握手方式并加载CA证书 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); if (!SSL_CTX_load_verify_locations(ctx, CACERT, NULL)) { printf("%d\\n 下载CA 证书出错!\\n", SSL_CTX_load_verify_locations(ctx, CACERT, NULL)); exit(-1); } //加载自己的证书 if (!SSL_CTX_use_certificate_file(ctx, MYCERTF, SSL_FILETYPE_PEM)) { ERR_print_errors_fp(stdout); printf("\\n错误1 %d\\n", SSL_CTX_use_certificate_file(ctx, MYCERTF, SSL_FILETYPE_PEM)); exit(1); } //加载自己的私钥 if (!SSL_CTX_use_PrivateKey_file(ctx, MYKEYF, SSL_FILETYPE_PEM)) { ERR_print_errors_fp(stderr); exit(1); } //检查自己的证书和私钥是否匹配 if (!SSL_CTX_check_private_key(ctx)) { printf("\\n私钥与证书公钥不匹配!\\n"); exit(1); } /*构建随机数生成机制,WIN32平台必需*/ srand((unsigned)time(NULL)); for (int i = 0; i < 100; i++) seed_int[i] = rand(); RAND_seed(seed_int, sizeof(seed_int)); //加密方式 SSL_CTX_set_cipher_list(ctx, "RC4-MD5"); //处理握手多次 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); /*以下是正常的TCP socket建立过程.............................. */ SOCKET sock; //printf("Begin tcp socket...\\n"); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { printf("\\nSOCKET有问题. \\n"); } memset(&sin, '\\0', sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(SERVER); /* Server IP */ sin.sin_port = htons(PORT); /* Server Port number */ int icnn = connect(sock, (sockaddr *)&sin, sizeof(sin)); if (icnn == SOCKET_ERROR) { printf("\\n连不上服务器\\n", GetLastError()); exit(1); } /* TCP 链接已建立.开始SSL 握手过程.......................... */ //绑定套接字 ssl = SSL_new(ctx); if (NULL == ssl) exit(1); if (0 >= SSL_set_fd(ssl, sock)) { printf("\\n连接失败!\\n"); exit(1); } //SSL握手 //SSL_connect(ssl); printf("\\n正在连接服务器......\\n"); int k = SSL_connect(ssl); if (k != 1) { printf("%d\\n", k); printf("\\nSSL connect fail!\\n"); exit(1); } printf("\\n连接服务器成功\\n"); char sendmsg[MSGLENGTH] = "\\0"; char revmsg[MSGLENGTH] = "\\0"; int err = SSL_read(ssl, revmsg, sizeof(revmsg)); revmsg[err] = '\\0'; printf("%s\\n", revmsg); while (1) { printf("\\n请输入所要发送的数据:\\n\\n>> "); gets(sendmsg); SSL_write(ssl, sendmsg, strlen(sendmsg)); printf("\\n发送消息“%s ”成功!\\n", sendmsg); if (!strcmp(sendmsg, "exit")) { //关闭套接字 //sleep(1); SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); closesocket(sock); WSACleanup(); getchar(); break; } } return 0; } 3 实验结果分析
