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流程和函数介绍