2017-2018-1 20155326《信息安全系统设计基础》 实验五 通讯协议设计
实验任务一
在Ubuntu中完成 http://www.cnblogs.com/rocedu/p/5087623.html 中的作业
基于Socket实现TCP通信,一人实现服务器,一人实现客户端
研究OpenSSL算法,测试对称算法中的AES,非对称算法中的RSA,Hash算法中的MD5
选用合适的算法,基于混合密码系统实现对TCP通信进行机密性、完整性保护。
学有余力者,对系统进行安全性分析和改进。
实验过程
OpenSSL的简介
-
OpenSSL 是一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。OpenSSL 是一个SSL协议的开源实现,采用C语言作为开发语言,具备了跨平台的能力,支持Unix/Linux、Windows、Mac OS等多种平台。
-
OpenSSL整个软件包大概可以分成三个主要的功能部分:SSL协议库、应用程序以及密码算法库。OpenSSL的目录结构自然也是围绕这三个功能部分进行规划的。
-
作为一个基于密码学的安全开发包,OpenSSL提供的功能相当强大和全面,囊括了主要的密码算法、常用的密钥和证书封装管理功能以及SSL协议,并提供了丰富的应用程序供测试或其它目的使用。
-
OpenSSL的应用程序已经成为了OpenSSL重要的一个组成部分,其重要性恐怕是OpenSSL的开发者开始没有想到的。如OpenCA,就是完全使用OpenSSL的应用程序实现的。OpenSSL的应用程序是基于OpenSSL的密码算法库和SSL协议库写成的,所以也是一些非常好的OpenSSL的API使用范例,读懂所有这些范例,你对OpenSSL的API使用了解就比较全面了,当然,这也是一项锻炼你的意志力的工作。
-
OpenSSL的应用程序提供了相对全面的功能,在相当多的人看来,OpenSSL已经为自己做好了一切,不需要再做更多的开发工作了,所以,他们也把这些应用程序称为OpenSSL的指令。OpenSSL的应用程序主要包括密钥生成、证书管理、格式转换、数据加密和签名、SSL测试以及其它辅助配置功能。
Linux下OpenSSL的安装
- 在https://www.openssl.org/source/中安装OpenSSL最新版本。
- 将压缩包在虚拟机中解压
-
依次输入如下命令进行安装
./config
make
make test
make install
过程图如下:
Linux下OpenSSL的使用
- 通过man openssl查看帮助文档。
- 编写一个测试代码test_openssl.c:
- 使用gcc -o test_openssl test_openssl.c -L/usr/local/ssl/lib -lcrypto -ldl -lpthread命令编译。
在这个过程中,出现了错误,提示没有openssl/evp.h,在网上查找了相应教程,输入sudo apt-get install libssh-dev后即可。
- 生成“test_openssl”可执行文件,运行程序,并执行echo $?,结果打印0,测试结果表明安装成功。
Linux的SOCKET编程
-
TCP/IP协议族包括运输层、网络层、链路层,而socket所在位置如图,Socket是应用层与TCP/IP协议族通信的中间软件抽象层。
-
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
-
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。其中使用到的函数有:socket()函数、bind()函数、listen()、connect()函数、accept()函数、read()、write()函数等。
- 我和20155320组队,他实现客户端,我实现的是服务器,以下是我的代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define DEFAULT_PORT 8000
#define MAXLINE 4096
int main(int argc, char** argv)
{
int socket_fd, connect_fd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
//初始化Socket
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//初始化
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT
//将本地地址绑定到所创建的套接字上
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//开始监听是否有客户端连接
if( listen(socket_fd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1){
//阻塞直到有客户端连接,不然多浪费CPU资源。
if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
//接受客户端传过来的数据
n = recv(connect_fd, buff, MAXLINE, 0);
//向客户端发送回应数据
if(!fork()){ /*紫禁城*/
if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1)
perror("send error");
close(connect_fd);
exit(0);
}
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connect_fd);
}
close(socket_fd);
}
运行截图如下:
通过openssl测试密码算法
- enc中可以指定的对称加密算法指令可能并没有以单独指令的形式存在,所以我们常用enc方式来调用加密算法。
测试AES
-
此测试例子是使用的openssl库中提供的EVP系列函数对数据进行解密的一个测试,选择的算法为:AES128,加密模式为:CBC。
-
命令行输入密码123456 :openssl enc -aes-128-cbc -in lmc.txt -out out.txt -pass pass:123456 ;
-
文件输入,密码123456:echo 123456 > passwd.txt openssl enc -aes-128-cbc -in lmc.txt -out out.txt -pass file:passwd.txt;
-
从标准输入输入:openssl enc -aes-128-cbc -in lmc.txt -out out.txt -pass stdin。
测试RSA
-
RSA是一个非对称加密算法。简单说来,非对称加密算法就是说加密解密一个文件需要有两个密钥,一个用来加密,为公钥,一个用来解密,为私钥。证书可以用来授权公钥的使用。
-
openssl的rsa加密,其中主要涉及利用公钥和密钥加解密文件,没有涉及对证书的操作。
-
首先生成一个rsa私钥:
这里-out指定生成文件的。需要注意的是这个文件包含了公钥和密钥两部分,也就是说这个文件即可用来加密也可以用来解密。后面的1024是生成密钥的长度。
- openssl可以将这个文件中的公钥提取出来:
-in指定输入文件,-out指定提取生成公钥的文件名。至此,我们手上就有了一个公钥,一个私钥(包含公钥)。现在可以将用公钥来加密文件了。
- 我在目录中创建一个hello的文本文件,然后利用此前生成的公钥加密文件:
-in指定要加密的文件,-inkey指定密钥,-pubin表明是用纯公钥文件加密,-out为加密后的文件。
- 解密文件:
-in指定被加密的文件,-inkey指定私钥文件,-out为解密后的文件。
- 查看解密文件
至此,一次加密解密的过程结束。
生成rsa私钥文件:openssl genrsa -out rsaprivatekey.pem 1024。
生成公钥文件,指明输出文件是公钥文件openssl rsa -in rsaprivatekey.pem -pubou -out rsapubckey.pem。
用公钥匙rsapubckey.pem加密文件plain.txt,输出到文件hello.en:openssl rsautl -encrypt -in plain.txt -inkey rsapubckey.pem -pubin -out hello.en;
使用私钥匙rsaprivatekey.pem解密密文hello.en,输出到文件hello.de:openssl rsautl -decrypt -in hello.en -inkey rsaprivatekey.pem -pubin -out hello.de
测试 MD5
实验任务二:
在Ubuntu中实现对实验二中的“wc服务器”通过混合密码系统进行防护
OpenSSL流程和函数介绍
-
首先初始化
int SSL_Library_init (void); -
接着选择会话协议和创建会话环境
目前支持的会话协议包括:TLSv1.0, SSLv2, SSLv3, SSLv2/v3。
OpenSSL中一个会话的环境称为“CTX”,申请CTX的函数是:
SSL_CTX *SSL_CTX_new (SSL_METHOD *method);
其中method就是会话协议,比如SSLv2/v3的client,则传入函数调用的返回值:
const SSL_METHOD *SSLv23_client_method (void);
-
接下来是设置CTX的属性,比如制定证书验证方式:
int SSL_CTX_set_verify (SSL_CTX ctx,
int mode,
int (verify_callback),
int (X509_STROE_CTX *)); -
加载CA证书:
SSL_CTX_load_varify_location (SSL_CTX *ctx, const char *Cafile, const char *Capath);
-
加载用户私钥:
SSL_CTX_use_Private_file (SSL_CTX *ctx, const char *file, int type);
-
加载用户证书:
SSL_CTX_use_certificate_file (SSL_CTX *ctx, const char *file, int type);
-
验证私钥和证书是否相等
int SSL_CTX_check_private_key (SSL_CTX *ctx);
-
建立SSL套接字
SSL *SSL_new (SSL_CTX *ctx); -
使用socket绑定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); // 只写
注意:上述三个函数成功时返回TRUE,失败时返回FALSE
-
完成SSL握手
int SSL_connect (SSL *ssl); // 用于client
int SSL_accept (SSL *ssl); // 用于client -
从SSL套接字中提取对方的证书信息
X509 *SSL_get_peer_certificate (SSL *ssl); -
获取证书所有者的名字:
X509_NAME *X509_get_subject_name (X509 *a);
-
数据传输
int SSL_read (SSL *ssl, void *buf, int num);
int SSL_write (SSL *ssl, const void *buf, int num); -
结束SSL通信
-
关闭SSL套接字
int SSL_shitdown (SSL *ssl);
-
释放SSL套接字
void SSL_free (SSL *ssl);
-
释放SSL会话
void SSL_CTX_free (SSL_CTX *ctx);
-
下面大致的伪代码说明了调用SSL的一整个流程:
Client端:
ctx = SSL_CTX_new (SSLv23_client_method());
ssl = SSL_new (ctx);
fd = socket ();
connect ();
SSL_set_fd (ssl, fd);
SSL_connect (ssl);
SSL_write ();
Server端:
ctx = SSL_CTX_new (SSLv23_server_method());
ssl = SSL_new (ctx);
fd = socket ();
bind ();
listen ();
accept ();
SSL_set_fd (ssl, fd);
SSL_accept (ssl);
SSL_read ();
- 具体代码见码云
运行结果
实验中遇到的问题及解决方法
- 使用gcc -o test_openssl test_openssl.c -L/usr/local/ssl/lib -lcrypto -ldl -lpthread命令编译。
在这个过程中,出现了错误,提示没有openssl/evp.h,在网上查找了相应教程,输入sudo apt-get install libssh-dev后即可。
参考文献
ubuntu php编译安装 openssl/evp.h: 没有那个文件或目录
Linux下OpenSSL的安装与使用
Linux的SOCKET编程详解
使用openssl中的EVP通用加密算法接口的之--解密测试例子
利用openssl进行RSA加密解密
OpenSSL流程和函数介绍