密码学总结
来源:互联网 发布:淘宝上没有点击量 编辑:程序博客网 时间:2024/05/22 12:01
根据网上资料总结而成!~
密码学总结
作业要求
- 根据本学期学习过的密码学知识,利用openssl,自己设计一套加密通讯的方案,实现客户端和服务端之间的安全的可靠的加密通讯,形式不限,提供实现代码和实验结果,最终形成实验报告
设计思路
首先,任何一套安全的通讯系统在数据传输过程中至少具备以下三个特性:
- 验证来源的合法性
- 保证数据的完整性
- 保证数据的私密性
根据所有加密传输的方法,总共有这几种加密方式
对数据的私密性要求,我们可以通过对数据进行加解密来完成。
如下图所示:
根据加解密Key的不同将加密体系分为两种:
1,对称加密 特点:使用同一个密钥进行加解密 优点: 加密速度快 缺点:秘钥传递问题 常见的对称加密算法有:DES,3DES,AES等2,非对称加密 特点:数据传输双方事先都必须产生一对密钥 一个称为公钥 一个称为私钥 公钥加密的数据,只有对应的私钥才能解密 私钥加密的数据,只有对应的公钥才能解密 优点:解决了秘钥传递问题 缺点:加密速度慢 常见的非对称加密算法有:RSA,DH等
对称加密:
发送方利用自己的密钥对明文数据进行加密;接收方必须得到发送方的密钥;接收方利用发送方的密钥对密文进行解密;如果密钥在传输过程中被第三方获悉,那此次加密的私密性得不得保障。
非对称加密:
首先通讯双方都产生一对密钥;数据发送方得到数据接收方的公钥;数据发送方利用数据接收方的公钥对数据进行加密;数据接收方利用自己的私钥对加密数据进行解密;数据接收方最终得到明文的数据。任何一方得到接收方的公钥,也解密不了传输中的数据。
总结:虽然非对称加密体系实现了密钥交换的问题,但其本身算法实现复杂导致其加密速度慢。
于是在使用中,人们一般将对称加密和非对称加密二者结合起来使用。
对称与非对称结合:
混合使用之加密图解:
首先发送方利用一个随机数产生了一个对称密钥;发送方利用这个密钥对数据进行加密;发送方拿着接收方的公钥对这个密钥进行加密;发送方将加密好的数据和加密好的密钥发送给接收方;这种方法既保证了加密速度,又保证了加密密钥的安全传送。
混合使用之解密图解:
接收方接收到加密的数据和加密的密钥;接收方利用自己的私钥解密加密的密钥;接收方再利用已解密的密钥来解密加密的数据文件;接收方最终得到了明文的数据。
总结:虽然混合使用对称加密和非对称加密之后,既保证了加密速度,又保证了加密密钥的安全传送。
但是始终没办法保证来源的合法性。这时候就需要用到数字签名。
数字签名:
发送方使用自己的私钥加密数据文件(数字签名);接收方接收到这个数字签名文件;接收方使用发送方的公钥来解密这个数字签名文件;如果能够解开,则表明这个文件是发送方发送过来的;否则为伪造的第三方发送过来的。对于发送方来讲这种签名有不可否认性。
总结:非对称加密中,公钥加密,私钥解密。主要用来进行数据加密。私钥加密,公钥解密。主要用来进行数字签名和认证。
解决了数据传输过程中数据私密性和来源合法性的问题。但对于数据的完整性就需要利用散列函数来实现。
散列函数:
散列函数的特点:
- 输入可以是任意长度 但输出是定长的。MD5 128bits SHA1 160bits
- 加密过程不可逆,无法根据特征码还原原来的数据
- 雪崩效应 输入的一点点改变 会导致结果发生很大改变
- 输入一致,输出必然相同
- 注:不同的输入,可能会有同样的输出。但是概率很小 并不代表没有这种可能
常见的散列函数(Hash函数):
1,MD5
2,SHA1
总结:散列函数可以根据任意长度的输入,生成定长的摘要信息。这样可以用来校验文件的完整性。防止数据在传输过程中被第三方篡改。
安全数字签名:
发送方利用散列函数对明文数据进行Hash得到摘要信息;发送方使用自己的私钥对摘要信息进行签名;接收方利用发送方的公钥来验证来源的合法性;接收方利用摘要信息来验证数据的完整性;当源文件很大,如果对源文件进行签名,就会比较耗费时间。如果只对摘要信息进行签名,速度比较快,因为它是定长的且位数不长。
中间人攻击:
利用散列函数我们可以用来保证数据的完整性。使用对称加密和非对称加密体系来完成数据的机密性和验证来源的合法性。但验证来源的合法性其实存在一个缺陷,就是怎么保证公钥的合法性?
假设Alice想向Bob发送一个文件。首先Alice得得到Bob的公钥;但是很不幸,Alice得到的是Hacker使用自己的公钥伪装成Bob的公钥;于是Alice拿着这个伪装的公钥对文件进行加密,并发送给Bob;但不幸的事情又发生了,这个加密文件被Hacker截获;于是,Hacker得到了文件的内容;Hacker对原文件可能进行篡改/也可能不会篡改;于是Hacker利用Bob真正的公钥对文件进行加密;然后发送给Bob;整个过程对Bob和Alice来说全然无知。
通过上图,我们看出要想实现对公钥来源合法性进行验证。需要数据通讯双方都得信任这个角色并且这个角色可以帮助通讯双方安全完成公钥交换。我们通常把这样一个角色或机构称为CA。
CA数字签名:
- 首先必须保证CA为数据通讯双方都认可的机构;
- 数据通讯双方向CA提交认证申请。里面包含各自的公钥;
- CA分别对通讯双方的合法性进行验证;
- 如果验证通过,CA则利用自己的私钥分别对两份申请文件进行加密(数字签名)。最终产生一个由CA完成数字签名的文件,称为数字证书。
- 通讯双方各自下载由CA签发的数字证书;
- 假设Alice想要发送一个文件给Bob;
- 首先Alice得向Bob请求得到Bob的数字证书;
- Alice利用CA的公钥完成对Bob数字证书合法性认证,并且从证书中得到Bob的公钥。
- Alice利用散列函数对数据进行Hash产生摘要信息;
- 并通过程序随机生成一个session key,利用这个session key对数据进行加密;
- Alice再利用Bob的公钥对这个session key进行加密;
- Alice将自己的证书和加密后的文件(包含session key)一并发送给Bob;
- Bob接收到Alice发来的文件后,先用CA的公钥来验证文件来源的合法性。
- 如果合法,则利用自己的私钥对session key进行解密,再利用session key对数据进行解密,得到明文数据。
- 然后,利用散列函数对数据进行Hash得到摘要信息。
- 将自己得到的摘要信息和Alice发过来的摘要信息进行比对,如果一致,则表明数据没有被篡改。安全通讯完成。
最终想法
目前在互联网上很多Web应用,尤其是那些电子商务应用,如网上银行,网上超市,股票和债券的在线交易以及软件的付费下载等,都需要在Web服务器和客户端浏览器之间传输机密敏感数据,这些数据包括了信用卡号,密码,银行帐号等高度私隐数据,如果这些数据给别人截获或篡改就会对客户和网站造成不可估量的损失。
为了保护这些敏感数据在传送过程中的安全,全球许多知名企业采用SSL的加密机制。 SSL是Netscape公司所提出來的安全保密协议,在浏览器和WWW服务器之间构造的安全通道来进行数据传输,SSL运行在TCP/IP层之上、应用层之下,为应用程序提供加密数据通道,它采用了RC4、MD5,以及RSA等加密演算法,使用40位的密钥,合适与对于商业信息的加密。
根据这一系列分析流程,我准备尝试进行利用openssl中的SSL系列函数进行加密通信。
SSL协议库
OpenSSL的SSL协议库实现了SSL 2.0、SSL 3.0和TLS 1.0。该库提供了丰富的API函数,这些API函数将SSL协议的处理细节进行了完美的封装。
实验过程
OpenSSL初始化
OpenSSL在使用之前,必须进行相应的初始化工作。
void SSL_load_error_strings(void); // 错误信息的初始化 int SSL_library_int(void); // 初始化SSL算法库函数( 加载要用到的算法 ),调用SSL函数之前必须调用此函数
在建立SSL连接之前,要为Client和Server分别指定本次连接采用的协议及其版本,目前能够使用的协议版本包括SSLv2、SSLv3、SSLv2/v3和TLSv1.0。SSL连接若要正常建立,则要求Client和Server必须使用相互兼容的协议。
创建CTX
在OpenSSL中,CTX是指SSL会话环境。建立连接时使用不同的协议,其CTX也不一样。
//客户端、服务端都需要调用的 SSL_CTX_new() //申请SSL会话环境 //若有验证对方证书的需求,则需调用 SSL_CTX_set_verify() //指定证书验证方式 SSL_CTX_load_verify_location() //为SSL会话环境加载本应用所信任的CA证书列表 //若有加载证书的需求,则需调用 SSL_CTX_use_certificate_file() //为SSL会话加载本应用的证书 SSL_CTX_use_certificate_chain_file() //为SSL会话加载本应用的证书所属的证书链 SSL_CTX_use_PrivateKey_file() //为SSL会话加载本应用的私钥 SSL_CTX_check_private_key() //验证所加载的私钥和证书是否相匹配
创建SSL套接字
在此之前要先创建普通的流套接字,完成TCP三次握手,建立普通的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); //以只写模式绑定流套接字
完成SSL握手
在普通TCP连接的基础上,建立SSL连接。与普通流套接字建立连接的过程类似:Client使用函数SSL_connect()【类似于流套接字中用的connect()】发起握手,而Server使用函数SSL_ accept()【类似于流套接字中用的accept()】对握手进行响应,从而完成握手过程。
int SSL_connect(SSL *ssl); int SSL_accept(SSL *ssl);
握手过程完成之后,Client通常会要求Server发送证书信息,以便对Server进行鉴别。
X509 *SSL_get_peer_certificate(SSL *ssl); //从SSL套接字中获取对方的证书信息 X509_NAME *X509_get_subject_name(X509 *a); //得到证书所用者的名字
进行数据传输
经过前面的一系列过程后,就可以进行安全的数据传输了。在数据传输阶段,需要使用SSL_read( )和SSL_write( )来代替普通流套接字所使用的read( )和write( )函数,以此完成对SSL套接字的读写操作
int SSL_read(SSL *ssl,void *buf,int num); //从SSL套接字读取数据 int SSL_write(SSL *ssl,const void *buf,int num); //向SSL套接字写入数据
会话结束
当Client和Server之间的通信过程完成后,就使用以下函数来释放前面过程中申请的SSL资源
int SSL_shutdown(SSL *ssl); //关闭SSL套接字 void SSl_free(SSL *ssl); //释放SSL套接字 void SSL_CTX_free(SSL_CTX *ctx); //释放SSL会话环境
通过利用上述函数在结合TCP连接所需要的相关语句,最终形成代码
截图
总结
- 首先就是证书的生成问题,windows下没有生成成功,最后利用ubuntu系统一系列命令才进行生成并且签名成功。
- 关于环境的配置问题,在执行SSL_connect和SSL_accept进行交互时发生错误,更改TSLv1、SSLv2、SSLv3、SSLv23时发生要么是连接错误,要么是接受错误,自己分析应该是配置环境时缺少相应的动态链接库或相应执行文件
- openssl虽然是开源密码库,涵括非常多的加解密算法,便利书写代码,但一定程度上也弱化了对密码的理解能力,甚至包含漏洞而无法发现造成损失
- 针对于通信加密问题,加密方式很多但重点不在于如何编写XX加密,而是在于如何去组合运用,使得通信更加安全稳定
- 此次作业让我较为深刻的理解SSL加密通信的原理,如何去运用,如何使得自己的信息更加安全
源代码
server
#include <iostream>#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> using namespace std; #pragma comment(lib, "libeay32.lib") #pragma comment(lib, "ssleay32.lib") #pragma comment(lib, "ws2_32.lib")#define CERTF "server.crt" //客户端的证书(需经CA签名)#define KEYF "server.key" //客户端的私钥 #define CACERT "ca.crt" //CA 的证书#define PORT 4000 #define IP_ADDRESS "127.0.0.1"#define MAXLEN 4096 int main() { int iResult; int ListenSock;//监听套接字 int ConnectSock;//连接套接字 SSL_CTX *ctx; SSL *ssl; X509 *client_cert; char buf [MAXLEN] = {0}; SSL_METHOD *meth; WSADATA wsaData; if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0) { cout<<"Init Windows Socket Failed::"<<GetLastError()<<endl; return -1; } SSL_library_init(); OpenSSL_add_ssl_algorithms(); //初始化* SSL_load_error_strings(); //为打印调试信息作准备 meth=(SSL_METHOD *)SSLv23_server_method(); ctx = SSL_CTX_new (meth); //采用什么协议(SSLv2/SSLv3/TLSv1)在此指定 if(ctx == NULL) { cout<<"null"<<endl; } 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<<"服务器端证书检查失败!"<<endl; exit(0); } if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) { cout <<"服务器端key检查失败!"<<endl; system("pause"); exit(0); } else { cout<<"服务器端key检查成功!"<<endl; } if (!SSL_CTX_check_private_key(ctx)) { cout << "服务器端证书和key不匹配!"<<endl; exit(0); } SSL_CTX_set_cipher_list(ctx,"RC4-MD5"); //SSL_CTX_set_cipher_list(ctx, "AES128-SHA"); SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); //不需要任何重试请求 //创建监听套接字 ListenSock = socket (AF_INET, SOCK_STREAM, 0); if(ListenSock == INVALID_SOCKET) { cout << "监听套接字创建失败" << endl; exit(0); } //创建地址 struct sockaddr_in LocalAddr; LocalAddr.sin_family = AF_INET; //LocalAddr.sin_addr.s_addr = INADDR_ANY; LocalAddr.sin_addr.s_addr = inet_addr (IP_ADDRESS); LocalAddr.sin_port = htons (PORT); memset(LocalAddr.sin_zero,0x00,8); //套接字绑定 iResult = bind(ListenSock, (struct sockaddr*)&LocalAddr,sizeof(LocalAddr)); if(iResult == -1) { cout << "监听套接字绑定失败" << endl; exit(0); } /*接受TCP链接*/ iResult = listen (ListenSock, 10); if(iResult == -1) { cout << "监听套接字开启监听失败" << endl; exit(0); } //接受客户端连接 struct sockaddr_in ClientSocket; int len=sizeof(struct sockaddr); ConnectSock = accept(ListenSock, (struct sockaddr *)&ClientSocket, &len); if(iResult == -1) { cout << "监听套接字accept失败" << endl; exit(0); } cout<<"Connection from "<<inet_ntoa(ClientSocket.sin_addr)<<":"<<ntohs(ClientSocket.sin_port)<<endl; cout<<"------------------TCP连接已建立,进行服务端的SSL过程.---------------"<<endl; cout<<"创建SSL连接中...."<<endl; //从初始化的ctx新建SSL ssl = SSL_new (ctx); if(ssl == NULL) { cout << "SSL创建失败" << endl; exit(0); } //(连接)套接字和SSL绑定 SSL_set_fd (ssl, ConnectSock); //SSL连接建立 iResult = SSL_accept (ssl);//等待一个TLS / SSL客户端启动TLS / SSL握手,类似于socket中的accept。 if(iResult == -1) { cout << "SSL连接失败" << endl; exit(1); } else { cout << "SSL连接成功" << endl; } //打印所有加密算法的信息 cout<<"SSL连接算法信息:"<< SSL_get_cipher (ssl) << endl; //得到客户端的证书并打印些信息 client_cert = SSL_get_peer_certificate (ssl); if (client_cert != NULL) { cout<<"客户端证书:"<<endl; cout<<"subject:"<<X509_NAME_oneline(X509_get_subject_name (client_cert), 0, 0)<<endl; cout<<"issuer:"<<X509_NAME_oneline(X509_get_issuer_name (client_cert),0,0)<<endl; X509_free (client_cert);//如不再需要,需将证书释放 } else { cout<<"客户端没有证书信息!"<<endl; //客户端认证失败 } cout<<"--------------------SSL连接已建立,进行通信会话.--------------------"<<endl; //SSL通信 while(true) { //接收消息 iResult = SSL_read (ssl, buf, sizeof(buf) - 1); if(iResult == -1) { cout<<"接收消息失败"<<endl; exit(0); } buf[iResult] = '\0'; cout<<"client--->"<<buf<<endl; //发送消息 cout<<"请输入要发送的消息:"; gets_s(buf); iResult = SSL_write(ssl, buf, strlen(buf)); if(iResult == -1) { cout<<"SSL_write发送消息失败"<<endl; exit(0); } cout<<"server--->"<<buf<<endl; } //关闭套接字和ssl SSL_shutdown (ssl); shutdown (ConnectSock,2); SSL_free (ssl); SSL_CTX_free (ctx); closesocket(ConnectSock); return 0; }
client
#include <iostream>#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> using namespace std; #pragma comment(lib, "libeay32.lib") #pragma comment(lib, "ssleay32.lib") #pragma comment(lib, "ws2_32.lib") #define CERTF "client.crt" //客户端的证书(需经CA签名) #define KEYF "client.key" //客户端的私钥 #define CACERT "ca.crt" //CA 的证书 #define PORT 4000#define IP_ADDRESS "127.0.0.1"#define MAXLEN 4096 int main() { int iResult; int ClientSocket; struct sockaddr_in ServerAddr; SSL_CTX *ctx; SSL *ssl; X509 *server_cert; char buf [MAXLEN] = {0}; SSL_METHOD *meth; int seed_int[100]; /*存放随机序列*/ WSADATA wsaData; SSL_library_init(); if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0) { cout<<"Init Windows Socket Failed::"<<GetLastError()<<endl; return -1; } OpenSSL_add_ssl_algorithms(); /*1.SSL初始化*/ SSL_load_error_strings(); /*2.SSL错误信息初始化 为打印调试信息作准备*/ meth=(SSL_METHOD *)SSLv23_client_method(); ctx = SSL_CTX_new (meth); //采用什么协议(SSLv2/SSLv3/TLSv1)在此指定 if(ctx == NULL) { cout<<"null"<<endl; } //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检查成功!"<<endl; } if (!SSL_CTX_check_private_key(ctx))//9.检查自己的证书和私钥是否匹配 { cout << "客户端证书和key不匹配!" << endl; exit(0); } //加密方式 SSL_CTX_set_cipher_list(ctx, "RC4-MD5"); //SSL_CTX_set_cipher_list(ctx, "AES128-SHA"); //设置ssl的模式为SSL_MODE_AUTO_RETRY,使用这个选项进行设置,如果服务器突然希望进行一次新的握手,那么OpenSSL可以在后台处理它。 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); srand( (unsigned)time( NULL ) ); for( int i = 0; i < 100;i++ ) { seed_int[i] = rand(); } RAND_seed(seed_int, sizeof(seed_int)); //创建TCP连接请求 ClientSocket = socket (AF_INET, SOCK_STREAM, 0); if(ClientSocket == INVALID_SOCKET) { cout << "套接字创建失败!" << endl; exit(0); } memset(ServerAddr.sin_zero, 0x00, 8); ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.s_addr = inet_addr ("127.0.0.1"); ServerAddr.sin_port = htons (PORT); //TCP连接 iResult = connect(ClientSocket, (struct sockaddr*) &ServerAddr,sizeof(ServerAddr)); if(iResult == -1) { cout << "TCP连接失败!" << endl; } else { cout << "TCP连接成功!" << endl; } cout<<"------------------TCP连接已建立,进行服务端的SSL过程.---------------"<<endl; cout<<"创建SSL连接中...."<<endl; //新建SSL ssl = SSL_new (ctx); if(ssl == NULL) { cout << "新建SSL失败!" << endl; exit(0); } //套接字ClientSocket和SSL绑定 SSL_set_fd (ssl, ClientSocket); iResult = SSL_connect (ssl); if(iResult == -1) { cout<<"SSL连接失败"<<endl; } else { cout<<"SSL连接成功"<<endl; } //打印连接信息 cout<<"SSL连接算法信息:" << SSL_get_cipher (ssl) << endl; //得到服务端的证书并打印些信息 server_cert = SSL_get_peer_certificate (ssl);//从SSL套接字中提取对方的证书信息,这些信息已经被SSL验证过了 if (server_cert != NULL) { cout<<"服务器证书:"<<endl; cout<<"subject:"<<X509_NAME_oneline( X509_get_subject_name (server_cert), 0, 0)<<endl;//得到证书所用者的名字 cout<<"issuer:"<<X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0)<<endl; X509_free (server_cert);//如不再需要,需将证书释放 } else { cout<<"客户端没有证书信息!"<<endl; //客户端认证失败 } cout<<"--------------------SSL连接已建立,进行通信会话.--------------------"<<endl; //SSL通信 while(true) { cout<<"请输入要发送的消息:"; gets_s(buf); iResult=SSL_write(ssl, buf, strlen(buf)); if(iResult == -1) { cout<<"发送消息失败"<<endl; exit(0); } cout<<"客户端--->"<<buf<<endl; //接收消息 iResult = SSL_read (ssl, buf, sizeof(buf) - 1); if(iResult == -1) { cout<<"SSL_read接收消息失败"<<endl; exit(0); } buf[iResult] = '\0'; cout<<"服务端--->"<<buf<<endl; } SSL_shutdown (ssl);//关闭SSL套接字 SSL_free (ssl);//释放SSL套接字 SSL_CTX_free (ctx);//释放SSL会话环境 closesocket(ClientSocket);// return 0; }
- 密码学总结
- 密码学总结
- 密码学知识总结
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- 密码学
- C# 输入法
- yii2.0中使用jquery
- Elasticsearch经验总结(持续补充)
- #java#用junix进行单元测试
- js 鼠标经过显示隐藏效果实例
- 密码学总结
- Android.mk简介
- 1787/1832: [Ahoi2008]Meet 紧急集合
- 删除表的主键
- 微信分享
- 【互联网资讯】亚多利是什么?
- MDK调试STM32出现“could not stop cortex-m device”解决办法
- BottomSheetDialog的使用及注意事项
- java中栈的应用-大数的相加