基于SSL协议的安全网络通信程序
****************************************
目录结构:
1. SSL理解
1.1 SSL的优点
1.2 SSL密文会话的安全机制
1.3 SSL工作过程
(1) SSL分层结构
(2) SSL握手过程
(3) 利用Wireshark分析SSL工作过程
2. X.509证书相关文件的生成
3. 实现SSL服务器和客户端
****************************************
1. SSL理解
1.1 SSL的优点
(1)提供较高的安全性保证。SSL利用数据加密、身份验证和消息完整性验证机制,保证数据传输的安全性。
(2)支持各种应用层协议。虽然SSL设计的初衷是为了解决Web安全性问题,但是由于SSL位于应用层和传输层之间,它可以为任何基于TCP等可靠连接的应用层协议提供安全性保证。
(3)部署简单。目前SSL己经成为网络中用来鉴别网站和网页浏览者身份,在客户端浏览器和Web服务器之间进行密文数据通信的全球化标准。SSL协议已被集成到大部分的浏览器中,如IE、Netscape、Firefox等。这就意味着几乎任意一台装有浏览器的计算机都支持SSL连接,不需要安装额外的客户端软件。
1.2 SSL密文会话的安全机制
(1)数据加密传输:数据需要加密后再进行传输,加密解密算法为对称密钥算法。
(2)身份验证:建立链接时需要对各通信对端进行身份验证,身份验证利用证书和数字签名技术,其中客户端的验证是可选的。
(3)消息完整性验证:消息传输过程中使用MAC算法来检验消息的完整性。
l 数据加密传输
与非对称密钥体制相比,对称密钥体制具有计算速度快的优点,通常用于对大量数据进行加密(如对整个数据流加密);而非对称密钥体制,一般用于数字签名和对少量的信息进行加密。SSL加密管道上的数据加解密使用对称密钥体制,目前主要支持的算法有DES、 3DES、 AES等,这些算法都可以有效地防止传输的数据被窃听。
l 身份验证
电子商务和网上银行等Web应用中必须保证客户要登录的Web服务器是真实的,以免重要信息被非法窃取。SSL安全协议利用数字签名来进行通信对端的身份验证。非对称密钥体制可以用来实现数字签名。由于通过私钥加密后的数据只能利用对应的公钥进行解密,因此根据解密是否成功,就可以判断发送者的身份,如同发送者对数据进行了 "签名"。
SSL客户端必须验证SSL服务器的身份,SSL服务器是否验证SSL客户端的身份,则由SSL服务器决定。使用数字签名验证身份时,需要确保被验证端的公钥是真实的,否则,非法用户可能会冒充被验证端与验证端通信。
l 消息完整性验证
为了避免网络中传输的数据被非法篡改,SSL利用基于MD5或SHA的MAC(消息认证码)算法来保证消息的完整性。
MAC算法要求通信双方具有相同的密钥,否则MAC值附件验证将会失败。因此,利用MAC算法验证消息完整性之前,需要在数据通信的双方部署相同的密钥。
l 利用非对称密钥算法保证密钥本身的安全
SSL利用非对称密钥体制加密密钥的方法來实现密钥交换,保证非授权的第三方无法获取相应的密钥。
利用非对称密钥算法加密对称密钥之前,Alice需要获取Bob的公钥,并保证该公钥确实属于Bob,否则,密钥可能会被未授权的非法用户窃取。SSL利用PKI提供的机制保证公钥的真实性。
l PKI保证公钥的真实性
数字证书(简称证书)是一个包含用户的公钥及其身份信息的文件,证明了客户与公钥的关联。数字证书由权威机构(CA)签发,并由CA保证数字证书的真实性。
验证SSL服务器/SSL客户端的身份之前,SSL服务器和SSL客户端需要将从CA获取的证书发送给通信对端,对端通过PKI判断该证书的真实性。如果该证书确实属于SSL服务器和SSL客户端,则通信对端利用该证书中的公钥验证SSL服务器和SSL客户端的身份。
1.3 SSL工作过程
(1) SSL分层结构
(2) SSL握手过程
SSL握手协议是SSL协议中最复杂的协议。SSL通过握手过程在客户端和服务器之间协商会话参数,并建立会话。会话包含的主要参数有会话ID、对方的证书、加密套件(密钥交换算法、数据加密算法和MAC算法等)以及主密钥(mastersecret)。通过SSL会话传输的数据,都将采用此次会话的主密钥和加密套件进行加密、计算MAC值等。
SSL握手协议由一系列报文组成,根据功能基本上可以分为四个阶段:
1) 第一阶段是建立安全能力。
2) 第二阶段是服务器鉴别和密钥交换。
3) 第三个阶段是客户鉴别和密钥交换。
4) 第四个阶段是完成握手阶段。
(3)利用Wireshark分析SSL工作过程
1.handshake--client hello
注释:
ECC算法和DH结合使用,用于密钥磋商,这个密钥交换算法称为ECDH。
使用RC4加密体制算法对通信数据加密(密钥长度128位)
使用SHA哈希算法进行消息完整性验证
使用RSA公钥体制算法进行证书验证和对称密钥交换
【既然密钥交换算法有很多种(RSA 和DH)那SSL握手期间用哪种呢,这个就是之前由选择的ciphersuit决定的,比如选择的是SSL_RSA_WITH_RC4_128_MD5 = 0x0004,那就是RSA的密钥交换算法即用非对称加密对称将密钥传送到对方,若选择的是SSL_DHE_RSA_EXPORT….那就使用DH交换算法】
2.handshake--server hello
3.S—>C handshake—certificate 服务器端向客户端发送服务器证书
【Certificate(可选):服务器发一个证书或一个证书链到客户端,证书链开始于服务器公共钥匙并结束于证明权威的根证书。该证书用于向客户端确认服务器的身份,该消息是可选的。如果配置服务器的SSL需要验证服务器的身份,会发送该消息。多数电子商务应用都需要服务器端身份验证。】
4.S-->Chandshake—sever key exchange handshake—sever hello done
服务器发送公钥和签名信息
【如果服务器发送的公共密钥对加密密钥的交换不是很合适,则发送一个服务器密钥交换消息。即和客户端协商密钥。】
RSA方式密钥交换消息则把消息中的加密用公钥放入会话缓存中,作为客户端这边握手阶段的写密钥而不是用服务器证书中的公钥。
DH 方式的消息就把消息中的 p,g,Ys三个参数(ECDH密钥交换情况下是G和A)记录下来,有这些 client端就可以计算出 pre-master了,只要回头再把自己这边的Yc参数发过去, server端就也能计算出相同的 pre-maseter了。
为了防止消息被恶意篡改,Server Key exchange消息中还要包含一个对密钥参数(pubKey)的签名(Signature)。
(1)serverkey exchange Pubkey signature
(2)Server hello done
5.C->S handshake—clientkey exchange
客户端产生一个会话密钥与服务器共享。在SSL握手协议完成后,客户端与服务器端通信信息的加密就会使用该会话密钥。如果使用RSA加密算法,客户端将使用服务器的公钥将会话密钥之后再发送给服务器。服务器使用自己的私钥对接收的消息进行解密得到共享的会话密钥。
若是 RSA 方式密钥交换,则产生一个 48 位随机数作为 pre-master 并用服务器公钥加密后发出去若是 DH 方式的密钥交换,则根据 sever 的 g,p,Ys ,产生 Xa 和 Yc , Xa 和 Ys 能计算出 pre-master ,把产生的 Yc 放入消息中发给server ,这样 server 用它的 Xb 和 Yc 也能计算出 pre-master 了。计算出预主密码后就顺便把主密码 (master secret) 给算出来了。算出主密码就把对称密钥产生出来了(预主要密码à主密码à对称密钥)
clientkey exchange, change cipher spec ,encrypted handshakemessage
【Change cipher spec:客户端要求服务器在后续的通信中使用加密模式】
6.S->C handshake—new session ticket
New session ticket,change cipher spec,encrypted handshake message
【Change cipher spec:服务器要求客户端在后续的通信中使用加密模式】
“session Ticket”(RFC 5077)取代机制被引入,目标是消除服务器需要维护每个客户端的会话状态缓存的要求。相反,如果客户指示它支持Session Ticket,在TLS握手的最后一步中服务器将包含一个“NewSession Ticket”信息,包含了一个加密通信所需要的信息,这些数据采用一个只有服务器知道的密钥进行加密。
【引:http://cache.baiducontent.com/cm=9f65cb4a8c8507ed4fece763105392230e54f7257b8c8148228a8448e435061e5a35a3fd7c734e5392d8781f79fb3e18feb56b32610c7ce0dedf883b9cecd36974d57b23706dc01a438a4ee9&p=b47ac64ad4af5ae508e297790a4d82&newp=8b2a971a818c11a05bea936213528c231610db2151d7d5122083dc&user=baidu&fm=sc&query=New+session+ticket&qid=&p1=5
】
以上1-6步,握手完成。下面开始客户端和服务器端开始传输加密信息。
7.C-->S 传送正式数据
【淘宝网登陆页面SSL连接建立分析】
42.156.196.14 https://login.taobao.com/member/login.jhtml
(1)C-->S client hello
(2)S-->C server hello
Ciphersuite:TLS_RSA_WITH_RC4_128_SHA
(3)S-->C certificate 证书链
(4)C-->S Client Key Exchange
RSA Encrypted Secret
(5)C-->S New Session Ticket
2. X.509证书相关文件的生成
【参考:基于X.509证书和SSL协议的身份认证过程实现http://h2appy.blog.51cto.com/609721/1181234】
生成数字证书相关文件的步骤:
1.生成服务器端的私钥(key文件),执行命令
openssl genrsa -des3 -out server.key1024
Enter pass phrase for server.key:123456
2.生成服务器端的csr文件
openssl req -new -key server.key -outserver.csr -config openssl.cnf
===拷贝 openssl\apps 下的openssl.cnf文档到out32dll目录下,就可以使用 Openssl了。
Enter pass phrase for server.key:123456
A challenge password:147258
3.客户端生成key文件
openssl genrsa -des3 -out client.key1024
Enter pass phrase for client.key:123456
4.生成客户端的csr文件
openssl req -new -key client.key -outclient.csr -config openssl.cnf
Enter pass phrase for client.key:123456
A challenge password:147258
5.生成自己的CA
openssl req -new -x509 -keyout ca.key -outca.crt -config openssl.cnf
Enter PEM pass phrase:123456
生成ca.key和ca.crt证书文件
6.用生成的ca给服务器的csr文件签名,生成服务器端的证书
openssl ca -in server.csr -out server.crt-cert ca.crt -keyfile ca.key -config openssl.cnf
===将apps目录下的demoCA目录以及根目录下的crypto目录复制到out32dll目录下;并在demoCA目录下新建文件夹newcerts.
Enter pass phrase for ca.key:123456
7.用生成的ca给客户端的csr文件签名,生成客户端的证书
openssl ca -in client.csr -out client.crt-cert ca.crt -keyfile ca.key -config openssl.cnf
===清空index.txt文件内容后正常生成
Enter pass phrase for ca.key:123456
3. 实现SSL服务器和客户端
对于上面的证书生成过程中,SSL服务器和客户端中所需要使用的只有五个文件,分别是ca.crt,client.crt,client.key,server.crt和server.key。
客户端需要ca.crt,client.crt,client.key这三个文件,
服务器端需要ca.crt,server.crt,server.key这三个文件。
源代码:
SSL Server端:
//
#include "openssl/rsa.h"
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
#include
using namespace std;
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
/*所有需要的参数信息都在此处以#define的形式提供*/
#define CERTF "D:\\openssl-1.0.1e\\out32dll\\server.crt" /*客户端的证书(需经CA签名)*/
#define KEYF "D:\\openssl-1.0.1e\\out32dll\\server.key" /*客户端的私钥(建议加密存储)*/
#define CACERT "D:\\openssl-1.0.1e\\out32dll\\ca.crt" /*CA 的证书*/
#define PORT 7758 /*服务端的端口*/
#define CHK_NULL(x) if ((x)==NULL) { printf("null\n"); }
#define MAXLEN 4096
int main ()
{
int err;
int ListenSock;//监听套接字
int ConnectSock;//连接套接字
SSL_CTX* ctx;
SSL* ssl;
X509* client_cert;
// char* str;
char buf [MAXLEN] = {0};
// char szMsg[4096] = {0};
//SSL_METHOD *meth;
WSADATA wsaData;
system("title SSL_SERVER");
system("color 0a");
if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
printf("WSAStartup()fail:%d\n",GetLastError());
return -1;
}
OpenSSL_add_ssl_algorithms(); /*初始化*/
SSL_load_error_strings(); /*为打印调试信息作准备*/
//注意这里是server和和客户端不同
//meth=TLSv1_server_method();
ctx = SSL_CTX_new (TLSv1_server_method()); //采用什么协议(SSLv2/SSLv3/TLSv1)在此指定
CHK_NULL(ctx);
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /*验证与否*/
SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若验证,则放置CA证书*/
if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0)
{
cout<<"服务器端证书检查失败!"<
SSL Client 端:
//
#include "openssl/rsa.h"
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
#include
using namespace std;
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
/*所有需要的参数信息都在此处以#define的形式提供*/
#define CERTF "D:\\openssl-1.0.1e\\out32dll\\client.crt" /*客户端的证书(需经CA签名)*/
#define KEYF "D:\\openssl-1.0.1e\\out32dll\\client.key" /*客户端的私钥(建议加密存储)*/
#define CACERT "D:\\openssl-1.0.1e\\out32dll\\ca.crt" /*CA 的证书*/
#define PORT 7758 /*服务端的端口*/
#define CHK_NULL(x) if ((x)==NULL) { printf("null\n"); }
#define MAXLEN 4096
int main ()
{
int err;
int sd;
struct sockaddr_in sa;
SSL_CTX* ctx;
SSL* ssl;
X509* server_cert;
// char* str;
char buf [MAXLEN] = {0};
// char szMsg[MAXLEN] = {0};
//SSL_METHOD *meth;
//int seed_int[100]; /*存放随机序列*/
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
printf("WSAStartup()fail:%d\n",GetLastError());
return -1;
}
system("title SSL_CLIENT");
system("color 0a");
OpenSSL_add_ssl_algorithms(); /*1.SSL初始化*/
SSL_load_error_strings(); /*2.SSL错误信息初始化 为打印调试信息作准备*/
//注意这里是client和和服务器不同
ctx = SSL_CTX_new (TLSv1_client_method()); //3.创建本次会话所使用的协议(TLSv1); 4.申请SSL会话的环境
CHK_NULL(ctx);
//SSL_VERIFY_PEER:希望验证对方的证书
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /*验证与否*/ //5.设置会话的握手方式
SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若验证,则放置CA证书*/ //6.并加载CA证书
if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) //7.加载自己(客户端)的整数
{
cout << "客户端证书检查失败!" << endl;
exit(0);
}
if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) //8.加载客户端的私钥
{
cout << "客户端key检查失败!" << endl;
system("pause");
exit(0);
}
else
{
cout<<"客户端证key检查成功!"<h_addr_list[0],pHost->h_length);
else
exit(0);*/
sa.sin_addr.s_addr = inet_addr ("127.0.0.1");
// sa.sin_addr.s_addr = inet_addr ("192.168.146.1");
sa.sin_port = htons (PORT); /* Server Port number *///SERVER PORT
//TCP连接 等待服务器的响应
cout<<"等待连接中..."<5)
{
cout<<"TCP 连接超时..."<
运行结果示例: