• 2017-2018-1 20155312 实验五 通讯协议设计


    2017-2018-1 20155312 实验五 外设驱动程序设计

    实验内容

    任务一

    1. 两人一组
    2. 基于Socket实现TCP通信,一人实现服务器,一人实现客户端
    3. 研究OpenSSL算法,测试对称算法中的AES,非对称算法中的RSA,Hash算法中的MD5
    4. 选用合适的算法,基于混合密码系统实现对TCP通信进行机密性、完整性保护。
    5. 学有余力者,对系统进行安全性分析和改进。

    任务二

    在Ubuntu中实现对实验二中的“wc服务器”通过混合密码系统进行防护

    实验步骤

    任务一

    准备工作

    1. 下载OpenSSL 1.1.0alpha
    2. 解压OpenSSL 1.1.0alpha源代码:tar xzvf openssl-1.1.0-pre1.tar.gz
    3. 然后进入源代码目录:cd openssl-1.1.0-pre1
    4. 编译安装:
    ./Configure//这里注意,第一个字母“C”要大写
    make
    sudo make install
    

    5.这时出现了两个错误,(见问题2),解决后用老师教程中的test_openssl.c例子进行测试:

    #include <stdio.h>
    #include <openssl/evp.h>
    
    int main(){
        
        OpenSSL_add_all_algorithms();
        
        return 0;
    }
    

    6.使用命令gcc -o test_openssl test_openssl.c -L/usr/local/ssl/lib -lcrypto -ldl -lpthread编译,结果生成可执行文件test_openssl

    [注]:-L选项——指定链接库的文件夹地址;-lcrypto——导入OpenSSL所需包;-ldl选项——加载动态库;-lpthread选项——链接POSIX thread库

    7.执行:./test_openssl;echo $?结果出错,显示“./test_openssl: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No such file or directory 127”

    8.在/usr/local/ssl路径下创建lib文件夹,然后把openssl_master目录下所有*.a文件复制到/usr/local/ssl/lib路径下:sudo cp /home/zjy/20155312zjy/zjy20155312/src/2017-2018_Week15/openssl-master/*.a /usr/local/ssl/lib

    9.重新运行./test_openssl;echo $?结果正确显示0,如下图所示:

    课后作业:

    学习openssl几个加密算法

    1.openssl简介

    OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。

    OpenSSL一共提供了8种对称加密算法,其中7种是分组加密算法,仅有的一种流加密算法是RC4。这7种分组加密算法分别是AES、DES、Blowfish、CAST、IDEA、RC2、RC5,都支持电子密码本模式(ECB)、加密分组链接模式(CBC)、加密反馈模式(CFB)和输出反馈模式(OFB)四种常用的分组密码加密模式。其中,AES使用的加密反馈模式(CFB)和输出反馈模式(OFB)分组长度是128位,其它算法使用的则是64位。事实上,DES算法里面不仅仅是常用的DES算法,还支持三个密钥和两个密钥3DES算法。

    2.对称密码中的AES算法

    ①AES工作模式介绍

    • ECB模式简介

    • ECB模式详细介绍

    设置加密密钥

    int AES_set_encrypt_key(const unsigned char *userKey, const int bits,AES_KEY *key);

    参数说明:

    • userKey: 密钥数值;
    • bits:密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128;
    • key: AES_KEY对象指针;
    • 返回值: 0 成功, -1 userkey,key为空, -2: 密钥长度不是128,192,256;

    设置解密密钥

    int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);

    参数说明:

    • userKey: 密钥数值;
    • bits:密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128;
    • key: AES_KEY对象指针;
    • 返回值: 0 成功, -1 userkey,key为空, -2: 密钥长度不是128,192,256;

    对数据进行加解密:

    void AES_ecb_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key, const int enc);

    参数说明:

    • in: 需要加密/解密的数据;
    • out: 计算后输出的数据;
    • key:密钥
    • enc: AES_ENCRYPT 代表加密, AES_DECRYPT代表解密;

    利用如下代码测试openssl中ECB模式的aes算法:

    #include <memory.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <openssl/aes.h>
    #pragma comment(lib,"libeay32.lib")
    int main(int argc, char **argv)
    {
        unsigned char buf[512];
        unsigned char buf2[512];
        unsigned char buf3[512];
        unsigned char aes_keybuf[32];
        memset(buf,1,sizeof(buf));    
        memset(buf,0,sizeof(buf2));    
        memset(buf,0,sizeof(buf3));
        memset(aes_keybuf,0,sizeof(aes_keybuf));
        AES_KEY aeskey;
        AES_set_encrypt_key(aes_keybuf,256,&aeskey);
        for(int i=0;i<sizeof(buf);i+=16)
        AES_encrypt(buf+i,buf2+i,&aeskey);
        AES_set_decrypt_key(aes_keybuf,256,&aeskey);
        for(int i=0;i<sizeof(buf);i+=16)
        AES_decrypt(buf2+i,buf3+i,&aeskey);
        if(memcmp(buf,buf3,sizeof(buf))==0)
            printf("test success
    ");
        else
            printf("test fail
    ");
    }
    

    运行结果如下图所示,显示成功:

    • CBC模式简介

    IV一般为16字节全0,数据块长度为16字节的整数倍,则在此数据块后附加一个8字节长的数据块, 附加的数据块为:16进制的“80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00”

    3.非对称密码中的RSA算法

    ①openssl RSA命令:

    • 生成rsa密钥:openssl genrsa -des3 -out prikey.pem
      • 这个文件包含了公钥和密钥两部分,也就是说这个文件即可用来加密也可以用来解密。后面的1024是生成密钥的长度。
    • 去除掉密钥文件保护密码:openssl rsa -in prikey.pem -out prikey.pem
    • 分离出公钥:openssl rsa -in prikey.pem -pubout -out pubkey.pem(获取证书中的公钥 openssl req -in myreq.pem -out -pubkey.pem)
      • -in指定输入文件,-out指定提取生成公钥的文件名。
    • 对文件进行签名:open rsautl -sign -inkey prikey.pem -in a.txt -out sig.dat
    • 验证签名:openssl rsautl -verify -inkey prikey.pem -in sig.dat
    • 用公钥对文件加密:openssl rsautl -encrypt -pubin -inkey pubkey.pem -in a.text -out b.text
    • 用私钥解密:openssl rsautl -decrypt -inkey prikey.pem -in b.text
    • 用证书中的公钥加密:opensll rsautl -encrypt -certin -inkey cert1.pem -in a.txt
    • 生成一个没有加密的ca私钥:openssl genrsa -out ca.key.pem 1024
    • 生成ca对应的csr文件:openssl req -new -key ca.key.pem -out ca.csr
    • 自签名:openssl x509 -in ca.csr -out ca.cer -req -signkey ca.key.pem -days 7300 -extensions v3_ca
    • 生成DER格式的私钥:openssl pkcs8 -topk8 -inform PEM -outform DER -in ca.key.pem -out ca.private.der -nocrypt
    • 读取证书的内容,显示在屏幕上:openssl x509 -in server.cer -noout -subject -nameopt RFC2253
    • 将der格式的证书转成pem格式:openssl x509 -inform PEM -outform DER -in server.der -out server.pem

    命令行下对hello文件“20155312”进行加解密,成功如下图所示:

    ②编程实现RSA

    利用上面生成的密钥文件,测试加密解密的代码如下:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<openssl/rsa.h>
    #include<openssl/pem.h>
    #include<openssl/err.h>
    #define OPENSSLKEY "test.key"
    #define PUBLICKEY "test_pub.key"
    #define BUFFSIZE 1024
    char* my_encrypt(char *str,char *path_key);//加密
    char* my_decrypt(char *str,char *path_key);//解密
    int main(void){
        char *source="i am 20155312 zjy!";
        char *ptr_en,*ptr_de;
        printf("source is    :%s
    ",source);
        ptr_en=my_encrypt(source,PUBLICKEY);
        printf("after encrypt:%s
    ",ptr_en);
        ptr_de=my_decrypt(ptr_en,OPENSSLKEY);
        printf("after decrypt:%s
    ",ptr_de);
        if(ptr_en!=NULL){
            free(ptr_en);
        }   
        if(ptr_de!=NULL){
            free(ptr_de);
        }   
        return 0;
    }
    char *my_encrypt(char *str,char *path_key){
        char *p_en;
        RSA *p_rsa;
        FILE *file;
        int flen,rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;    
        }   
        if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
        //if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){   换成这句死活通不过,无论是否将公钥分离源文件
            ERR_print_errors_fp(stdout);
            return NULL;
        }   
        flen=strlen(str);
        rsa_len=RSA_size(p_rsa);
        p_en=(unsigned char *)malloc(rsa_len+1);
        memset(p_en,0,rsa_len+1);
        if(RSA_public_encrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_en,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_en;
    }
    char *my_decrypt(char *str,char *path_key){
        char *p_de;
        RSA *p_rsa;
        FILE *file;
        int rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;
        }
        if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
            ERR_print_errors_fp(stdout);
            return NULL;
        }
        rsa_len=RSA_size(p_rsa);
        p_de=(unsigned char *)malloc(rsa_len+1);
        memset(p_de,0,rsa_len+1);
        if(RSA_private_decrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_de,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_de;
    }
    

    运行结果如下:

    4.Hash中的MD5算法

    ①MD5算法简介:

    MD5算法是一个被广泛使用的称为信息摘要的摘要算法,其主要过程包括了对字符串的填充(Padding)、分段摘要(Digesting)和最终摘要的输出(Outputting)三个步骤。其中填充是指首先需要对信息进行填充,使其字节长度对512求余得到结果为448.填充是在信息的后面填充一个1和无数个0,直到满足上面的长度条件时停止。在填充完成后,再往后面附加一个以64位二进制表示填充前信息的长度,使现在的信息字节长度等于N*512+448+64=(N+1)*512,这样长度恰好可以被512整除满足后面处理中对信息长度可以划分为N+1个完整分组的要求。

    ——引用自《摘要算法之MD5介绍及OpenSSL中MD5常用函数使用举例

    常用函数:

    • MD5():计算B 处的B 字节的MD5消息摘要并将其放置在B (其必须具有用于MD5_DIGEST_LENGTH ==输出的16字节的空间)的位置。 如果B 为NULL,则摘要将放置在静态数组中。

    unsigned char*MD5(const unsigned char *d, unsigned long n, unsigned char *md);

    • MD5_Init(): 初始化aB<MD5_CTX>结构

    void MD5_Init(MD5_CTX *c);

    • MD5_Update():可以与待求散列值的消息块(B 字节atB )重复调用。

    void MD5_Update(MD5_CTX *c, const void *data, unsigned long len);

    • MD5_Final():将消息摘要放入B ,它必须有MD5_DIGEST_LENGTH == 16个字节的输出空间,并去除B <MD5_CTX>。

    void MD5_Final(unsigned char *md, MD5_CTX *c);

    ②OpenSSL中的MD5

    测试代码如下:

    #define _GNU_SOURCE
    
    #include <stdio.h>
    
    #include <string.h>
    
    #include <stdlib.h>
    
    #include <errno.h>
    
    #include <ctype.h>
    
    #include "openssl/md5.h"
    
    MD5_CTX  md5_ctx;
    static int MD5mod(const char* str, int length, int mod){
    
        char sign[16] = {0};
    
        MD5_Init(&md5_ctx);
    
        MD5_Update(&md5_ctx, str, length);
    
        MD5_Final(sign, &md5_ctx);
    
    	printf("digest:%s
    ",sign);
    
        int sum = 0;
    
        for (int i=0; i < 16; i ++) {
    
            sum += (sign[i]&0xff);
    
        }
    
        int offset = sum % mod;
    
        return offset;
    
    }
    
    int main(int argc, char** argv)
    
    {
    
        if( argc < 4){
    
            fprintf(stderr, "%s num infile outfile
    ", argv[0]);
    
            exit(-1);
    
        }
    
        int num = atoi(argv[1]) ;
    
        if( num <= 0){
    
            fprintf(stderr, "ERROR: num error: %s
    ", argv[1]);
    
            exit(-1);
    
        }
    
        FILE* in = fopen(argv[2], "r");
    
        if( in == NULL){
    
            perror("fopen");
    
            fprintf(stderr, "ERROR: infile error: %s
    ", argv[2]);
    
            exit(-1);
    
        }
        FILE** OUT = (FILE**)malloc(sizeof(FILE*) * num);
    
        for(int i=0; i<num; ++i){
    
            char buf[256] = {0};
    
            sprintf(buf, "%s_%d", argv[3], i);
    
            OUT[i] = fopen(buf, "w");
    
            if( OUT[i] == NULL){
    
                perror("fopen");
    
                fprintf(stderr, "ERROR: infile error: %s
    ", argv[2]);
    
                exit(-1);
    
            }
    
        }
        size_t len = 0;
        ssize_t read;
        char * line = NULL;
    
        while ((read = getline(&line, &len, in)) != -1) {
    
            int  klen = 0;
    
            while( klen < read ){
    
                if( isspace( *(line+klen)) ) break;
    
                klen++;
            }
    
         //   char id[256]={0};
    
         //   strncpy(id, line, klen);
    
         //   printf("id=%s	klen=%d	read=%ld	line=%s", id, klen, read, line);
    
            fprintf(OUT[MD5mod(line, klen, num)], "%s", line);
    
        }
    
        if(line) free(line);
        return 0;
    }
    

    运行结果如下:

    在socket编程中的简单应用
    • 在客户端服务器通信的基础上,客户端利用rsa算法对文件加密传输给服务器
    • 服务器利用公钥对密文解密得到明文

    客户端代码如下:

    #include<netinet/in.h>                         // for sockaddr_in  
    #include<sys/types.h>                          // for socket  
    #include<sys/socket.h>                         // for socket  
    #include<stdio.h>                              // for printf  
    #include<stdlib.h>                             // for exit  
    #include<string.h>                             // for bzero  
    #include<openssl/rsa.h>
    #include<openssl/pem.h>
    #include<openssl/err.h>
    #define OPENSSLKEY "test.key"
    #define PUBLICKEY "test_pub.key"
    #define BUFFSIZE 1024  
    #define HELLO_WORLD_SERVER_PORT       6000 
    #define BUFFER_SIZE                   1024  
    #define FILE_NAME_MAX_SIZE            512  
    int mywc(char file_name[],int choose);
    char* my_encrypt(char *str,char *path_key);//加密
    char* my_decrypt(char *str,char *path_key);//解密
    int main(int argc, char **argv)  
    {  
    	
    	FILE *fp;
    	if (argc != 2)  
    	{  
    		printf("Usage: ./%s ServerIPAddress
    ", argv[0]);  
    		exit(1);  
    	}  
    
    // 设置一个socket地址结构client_addr, 代表客户机的internet地址和端口  
    	struct sockaddr_in client_addr;  
    	bzero(&client_addr, sizeof(client_addr));  
    	client_addr.sin_family = AF_INET;   
    	client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自动获取本机地址  
    	client_addr.sin_port = htons(0); 
    
    // 创建字节流socket,用client_socket代表客户端socket  
    	int client_socket = socket(AF_INET, SOCK_STREAM, 0);  
    	if (client_socket < 0)  
    	{  
    		printf("Create Socket Failed!
    ");  
    		exit(1);  
    	}  
    
    // 把客户端的socket和客户端的socket地址结构绑定   
    	if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)))  
    	{  
    		printf("Client Bind Port Failed!
    ");  
    		exit(1);  
    	}  
    
    // 设置一个socket地址结构server_addr,代表服务器的internet地址和端口  
    	struct sockaddr_in  server_addr;  
    	bzero(&server_addr, sizeof(server_addr));  
    	server_addr.sin_family = AF_INET;  
    
    // 服务器的IP地址来自程序的参数   
    	if (inet_aton(argv[1], &server_addr.sin_addr) == 0) //一个字符串IP地址转换为一个32位的网络序列IP地址,也可以用inet_addr
    	{  
    		printf("Server IP Address Error!
    ");  
    		exit(1);  
    	}  																												
    	server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);  
    	int server_addr_length = sizeof(server_addr);   
    
    		// 向服务器发起连接请求,连接成功后client_socket代表客户端和服务器端的一个socket连接  
    		if (connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length)==-1)  
    		{  
    			printf("Can Not Connect To %s!
    ", argv[1]);  
    			exit(1);  
    		}  
    
    		char file_name[FILE_NAME_MAX_SIZE];  
    		bzero(file_name, sizeof(file_name));  
    		printf("Please Input File Name.	");  
    		scanf("%s", file_name);  
    		if((fp = fopen(file_name,"r"))==NULL)
           		{
             		printf("Failure to open %s
    ",file_name);
             		exit(0);
    		}
    	
    		char buffer[BUFFER_SIZE];  
    		bzero(buffer, sizeof(buffer));  
    		strcpy(buffer,file_name);
    		if(send(client_socket,buffer,BUFFER_SIZE,0)==-1)
    		{
    			printf("发送文件名失败
    ");
    		}
    		
    		char *ptr_en,*ptr_de;		
    		char ch;
    		int i=0;
    
    		while((ch=fgetc(fp))!=EOF)
    		{
    			buffer[i++]=ch;
    			if(i>=BUFFER_SIZE)
    			{
    				printf("加密前 %s
    ",buffer);				
    				ptr_en=my_encrypt(buffer,PUBLICKEY);//加密buffer
    				printf("加密后发送密文 %s
    ",ptr_en);
    				ptr_de=my_decrypt(buffer,OPENSSLKEY);//解密buffer
    				printf("解密密文  %s
    ",ptr_de);
    				if((send(client_socket, ptr_en, strlen(ptr_en), 0))==-1)
    				{
    					printf("发送文件失败
    ");
    				}
    				bzero(ptr_en, sizeof(ptr_en));
    				bzero(buffer, sizeof(buffer));
    				i=0;
    			}
    		}
    		if(i<BUFFER_SIZE)
    		{
    			printf("加密前 %s
    ",buffer);				
    			ptr_en=my_encrypt(buffer,PUBLICKEY);//加密buffer
    			printf("加密后发送密文 %s
    ",ptr_en);
    printf("%d
    ",strlen(ptr_en));
    			if((send(client_socket, ptr_en, strlen(ptr_en), 0))==-1)
    			{
    				printf("发送文件失败
    ");
    			}
    		}
    		bzero(buffer, sizeof(buffer));
    		printf("发送完毕
    ");
    		if(ptr_en!=NULL){
            		free(ptr_en);
        		}  
    		mywc(file_name,1);
    		mywc(file_name,2);
    
    		bzero(buffer, sizeof(buffer));
    
    		fclose(fp);
    	 
    		close(client_socket); // 传输完毕,关闭socket  
    		return 0;  
    
    }  
    int mywc(char file_name[],int choose)
    {
    	FILE *fp;
            char ch;
            int flag=0,num=0;
           // int choose;
           //  printf("统计单词个数还是实现“wc -w”?(1or2)
    ");
           // scanf("%d",&choose);
    	if((fp = fopen(file_name,"r"))==NULL)
    	{
       	     printf("Failure to open %s
    ",file_name);
                 exit(0);
            }
    		     
    	if(choose==1)
    	{
    		while((ch=fgetc(fp))!=EOF)
    		{
    			if(ch==' ' || ch=='
    ' || ch=='	' ||  ch=='!' || ch=='?' || ch=='"' || ch=='.' || ch== '\,' || ch==':' || ch=='(' || ch==')' || ch==';'     || ch=='-')
    			{
    				flag=0;
    			}
    			else
    			{
    				if(flag==0)
    				{
    					flag=1;
    					num++;
    				}
    						  
    			}
    
    		}
    
    	}
    	else if(choose==2)
    	{
    		while((ch=fgetc(fp))!=EOF)
    		{
    			if(ch==' ' || ch=='
    ' || ch=='	' || ch=='
    ')
    				flag=0;
    			else
    			{
    				if(flag==0)
    				{
    					flag=1;
    					num++;
    				}
    			}
    		}
    	}
    	printf("单词个数为:%d_用方式%d计算
    ",num,choose);
    	fclose(fp);
    	return num;
    }
    char *my_encrypt(char *str,char *path_key){
        char *p_en;
        RSA *p_rsa;
        FILE *file;
        int flen,rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;    
        }   
        if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
        //if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){   换成这句死活通不过,无论是否将公钥分离源文件
            ERR_print_errors_fp(stdout);
            return NULL;
        }   
        flen=strlen(str);
        rsa_len=RSA_size(p_rsa);
        p_en=(unsigned char *)malloc(rsa_len+1);
        memset(p_en,0,rsa_len+1);
        if(RSA_public_encrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_en,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_en;
    }
    char *my_decrypt(char *str,char *path_key){
        char *p_de;
        RSA *p_rsa;
        FILE *file;
        int rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;
        }
        if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
            ERR_print_errors_fp(stdout);
            return NULL;
        }
        rsa_len=RSA_size(p_rsa);
        p_de=(unsigned char *)malloc(rsa_len+1);
        memset(p_de,0,rsa_len+1);
        if(RSA_private_decrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_de,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_de;
    }
    

    服务器代码如下:

    #include<netinet/in.h>   
    #include<sys/types.h>   
    #include<sys/socket.h>   
    #include<stdio.h>   
    #include<stdlib.h>   
    #include<string.h>   
    #include<pthread.h>  
    #include<openssl/rsa.h>
    #include<openssl/pem.h>
    #include<openssl/err.h>
    #define OPENSSLKEY "test.key"
    #define PUBLICKEY "test_pub.key"
    #define BUFFSIZE 1024
    #define HELLO_WORLD_SERVER_PORT    6000 
    #define LENGTH_OF_LISTEN_QUEUE     20  
    #define BUFFER_SIZE                1024  
    #define FILE_NAME_MAX_SIZE         512  
    char* my_encrypt(char *str,char *path_key);//加密
    char* my_decrypt(char *str,char *path_key);//解密
    void *process_client(void *new_server_socket);
    int mywc(char file_name[])
    {
    	char ch;
    	int flag=0,num=0;
    	int choose;
    	FILE *fp;
    	printf("统计单词个数还是实现“wc -w”?(1or2)
    ");
    	scanf("%d",&choose);
    	if((fp = fopen(file_name,"r"))==NULL)
    	{
    		printf("Failure to open %s
    ",file_name);
    	    exit(0);
    	}
    
    	if(choose==1)
    	{
    	while((ch=fgetc(fp))!=EOF)
    	{
    		if(ch==' ' || ch=='
    ' || ch=='	' ||  ch=='!' || ch=='?' || ch=='"' || ch=='.' || ch== '\,' || ch==':' || ch=='(' || ch==')' || ch==';' || ch=='-')
    		{
    			flag=0;
    		}
    		else
    		{
    			if(flag==0)
    			{
    				flag=1;
    				num++;
    			}
    
    		}		   
    	}
    	}
    	else if(choose==2)
    	{
    		while((ch=fgetc(fp))!=EOF)
    		{
    			if(ch==' ' || ch=='
    ' || ch=='	' || ch=='
    ')
    				flag=0;
    			else
    			{
    				if(flag==0)
    				{
    					flag=1;
    					num++;
    				}
    			}
    		}
    	}
    	printf("单词个数为:%d
    ",num);
    	fclose(fp);
    	return num;
    }
    int main(int argc, char **argv)  
    {  
     
    // 设置一个socket地址结构server_addr,代表服务器的IP地址和端口  
    	struct sockaddr_in   server_addr;  
    	bzero(&server_addr, sizeof(server_addr));  
    	server_addr.sin_family = AF_INET;  
    	server_addr.sin_addr.s_addr = htons(INADDR_ANY);  
    	server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);  
    
    // 创建字节流socket:server_socket 
    	int server_socket = socket(AF_INET, SOCK_STREAM, 0);  
    	if (server_socket ==-1)  
    	{  
    		printf("Create Socket Failed!
    ");  
    		exit(1);  
    	}  
    
    // 把socket和socket地址结构绑定   
    	if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))==-1)  
    	{  
    		printf("Server Bind Port: %d Failed!
    ", HELLO_WORLD_SERVER_PORT);  
    		exit(1);  
    	}  
    
    // server_socket用于监听   
    	if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)==-1)  
    	{  
    		printf("Server Listen Failed!
    ");  
    		exit(1);  
    	}  
    // 服务器端一直运行用以持续为客户端提供服务   
    	  
    	while(1)
    	{
    // 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept  
     
    		struct sockaddr_in client_addr;  
    		int length = sizeof(client_addr);    
    // accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信  
    		int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);  		
    		if (new_server_socket ==-1)  
    		{  
    			printf("Server Accept Failed!
    "); 
    			exit(0); 
    		}
    		printf("连接到客户端
    ");
    		pthread_t pid;
    		if(pthread_create(&pid, NULL, process_client,(void *) &new_server_socket) < 0){
    		      printf("pthread_create error
    ");
    		}
    		
    	}
    //	close(server_socket);
    }
    void *process_client(void *new_server_socket)
    {
    		int sockid=*(int *)new_server_socket;
    		FILE *fp;
    		//接受来自客户端的文件
    		char buffer[BUFFER_SIZE]; 
    		char file_name[FILE_NAME_MAX_SIZE];
    		bzero(buffer, sizeof(buffer));  
    		int length=0;
    		if(recv(sockid,buffer,BUFFER_SIZE, 0)==-1)
    		{
    			printf("接受文件名%s失败
    ",buffer);
    		}
    		strcpy(file_name,buffer);
    		strcat(file_name,"_server");
    		printf("创建文件夹%s
    ",file_name);
    		if((fp = fopen(file_name,"w"))==NULL)
    		{
    			printf("Failure to open %s
    ",file_name);
    			exit(0);
    		}
    		/*char *source="i am 20155312 zjy!";
        char *ptr_en,*ptr_de;
        printf("source is    :%s
    ",source);
        ptr_en=my_encrypt(source,PUBLICKEY);
        printf("after encrypt:%s
    ",ptr_en);
        ptr_de=my_decrypt(ptr_en,OPENSSLKEY);
        printf("after decrypt:%s
    ",ptr_de);
        if(ptr_en!=NULL){
            free(ptr_en);
        }   
        if(ptr_de!=NULL){
            free(ptr_de);
        } */
    		char *ptr_en,*ptr_de;
    		while( length = recv(sockid, buffer,10000, 0))
    		{
    			if(length<0)
    			{
    				printf("接受文件出错
    ");
    				exit(0);
    			}
    			printf("接受密文  %s
    ",buffer);
    			ptr_de=my_decrypt(buffer,OPENSSLKEY);//解密buffer
    					
    			printf("解密密文  %s
    ",ptr_de);
    			fwrite(ptr_de,sizeof(char),strlen(ptr_de),fp)<length;
    			
    			bzero(buffer, sizeof(buffer));
    			bzero(ptr_de, sizeof(ptr_de));
    		}
    		fclose(fp);
    		if(ptr_de!=NULL){
            		free(ptr_de);
        		} 
    		printf("接收完毕
    ");
    		bzero(buffer, BUFFER_SIZE);
    		int number=0;
    		number=mywc(file_name);
    		//bzero(buffer, BUFFER_SIZE);  
    		//buffer[0]=number+48;
    
    	        bzero(buffer, sizeof(buffer));  
     		 
    		printf("File Transfer Finished!
    ");   
    		close(new_server_socket);  
    } 
    char *my_encrypt(char *str,char *path_key){
        char *p_en;
        RSA *p_rsa;
        FILE *file;
        int flen,rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;    
        }   
        if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
        //if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){   换成这句死活通不过,无论是否将公钥分离源文件
            ERR_print_errors_fp(stdout);
            return NULL;
        }   
        flen=strlen(str);
        rsa_len=RSA_size(p_rsa);
        p_en=(unsigned char *)malloc(rsa_len+1);
        memset(p_en,0,rsa_len+1);
        if(RSA_public_encrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_en,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_en;
    }
    char *my_decrypt(char *str,char *path_key){
        char *p_de;
        RSA *p_rsa;
        FILE *file;
        int rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;
        }
        if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
            ERR_print_errors_fp(stdout);
            return NULL;
        }
        rsa_len=RSA_size(p_rsa);
        p_de=(unsigned char *)malloc(rsa_len+1);
        memset(p_de,0,rsa_len+1);
        if(RSA_private_decrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_de,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_de;
    }
    

    运行结果如下:

    任务二

    混合密码系统示意图如下所示:

    伪代码

    • 客户端实现思路:
      • 利用c语言中的rand()函数生成32字节的伪随机数数组构成会话密钥
      • 用预先生成的RSA私钥加密会话密钥
      • 将会话密钥发送给服务器
      • 用会话密钥加密指定文件
      • 将密文发送给服务器
      • 调用mywc()函数计算文件中单词数
    • 服务器实现思路:
      • 接收客户端发来的会话密钥密文
      • 利用预先生成的RSA公钥解密得到会话密钥
      • 接收客户端发来的密文
      • 用会话密钥解密密文得到明文
      • 将明文存入文件
      • 调用mywc()函数计算文件中单词数

    代码

    • 客户端
    #include<netinet/in.h>                         // for sockaddr_in  
    #include<sys/types.h>                          // for socket  
    #include<sys/socket.h>                         // for socket  
    #include<stdio.h>                              // for printf  
    #include<stdlib.h>                             // for exit  
    #include<string.h> 			       // for bzero 
    #include<memory.h> 
    #include<time.h>                            
    #include<openssl/rsa.h>
    #include<openssl/pem.h>
    #include<openssl/err.h>
    #include <openssl/aes.h>
    #pragma comment(lib,"libeay32.lib")
    #define OPENSSLKEY "test.key"
    #define PUBLICKEY "test_pub.key"
    #define BUFFSIZE 1024  
    #define HELLO_WORLD_SERVER_PORT       6000 
    #define BUFFER_SIZE                   1024  
    #define FILE_NAME_MAX_SIZE            512  
    int mywc(char file_name[],int choose);
    char* my_encrypt(char *str,char *path_key);//rsa加密
    char* my_decrypt(char *str,char *path_key);//rsa解密
    void aes_encrypt(char *str,char aes_keybuf[],char buf[]);//aes加密
    void aes_decrypt(char *str,char aes_keybuf[],char buf[]);//aes解密
    int main(int argc, char **argv)  
    {  
    	
    	FILE *fp;
    	if (argc != 2)  
    	{  
    		printf("Usage: ./%s ServerIPAddress
    ", argv[0]);  
    		exit(1);  
    	}  
    
    // 设置一个socket地址结构client_addr, 代表客户机的internet地址和端口  
    	struct sockaddr_in client_addr;  
    	bzero(&client_addr, sizeof(client_addr));  
    	client_addr.sin_family = AF_INET;   
    	client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自动获取本机地址  
    	client_addr.sin_port = htons(0); 
    
    // 创建字节流socket,用client_socket代表客户端socket  
    	int client_socket = socket(AF_INET, SOCK_STREAM, 0);  
    	if (client_socket < 0)  
    	{  
    		printf("Create Socket Failed!
    ");  
    		exit(1);  
    	}  
    
    // 把客户端的socket和客户端的socket地址结构绑定   
    	if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)))  
    	{  
    		printf("Client Bind Port Failed!
    ");  
    		exit(1);  
    	}  
    
    // 设置一个socket地址结构server_addr,代表服务器的internet地址和端口  
    	struct sockaddr_in  server_addr;  
    	bzero(&server_addr, sizeof(server_addr));  
    	server_addr.sin_family = AF_INET;  
    
    // 服务器的IP地址来自程序的参数   
    	if (inet_aton(argv[1], &server_addr.sin_addr) == 0) //一个字符串IP地址转换为一个32位的网络序列IP地址,也可以用inet_addr
    	{  
    		printf("Server IP Address Error!
    ");  
    		exit(1);  
    	}  																												
    	server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);  
    	int server_addr_length = sizeof(server_addr);   
    
    		// 向服务器发起连接请求,连接成功后client_socket代表客户端和服务器端的一个socket连接  
    		if (connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length)==-1)  
    		{  
    			printf("Can Not Connect To %s!
    ", argv[1]);  
    			exit(1);  
    		}  
    
    		char file_name[FILE_NAME_MAX_SIZE];  
    		bzero(file_name, sizeof(file_name));  
    		printf("Please Input File Name.	");  
    		scanf("%s", file_name);  
    		if((fp = fopen(file_name,"r"))==NULL)
           		{
             		printf("Failure to open %s
    ",file_name);
             		exit(0);
    		}
    	
    		char buffer[BUFFER_SIZE];  
    		bzero(buffer, sizeof(buffer));  
    		strcpy(buffer,file_name);
    		if(send(client_socket,buffer,BUFFER_SIZE,0)==-1)
    		{
    			printf("发送文件名失败
    ");
    		}
    		printf("发送文件名成功
    ");
    		bzero(buffer, sizeof(buffer)); 
    		//利用伪随机数生成32字节会话密钥:
    		unsigned char aes_keybuf[32];
    		int i;
    		srand((unsigned) time(NULL));
    		for(i=0;i<32;i++)
    		{
    			aes_keybuf[i]=rand()%10+48;
    		}
    		//用rsa对会话密钥加密
    		char *aeskey_en;
    		aeskey_en=my_encrypt(aes_keybuf,PUBLICKEY);
    		printf("会话密钥:%s
    加密后的会话密钥为%s
    
    
    ",aes_keybuf,aeskey_en);
    		if((send(client_socket, aeskey_en, strlen(aeskey_en)+1, 0))==-1)
    		{
    			printf("发送文件失败
    ");
    		}
    		printf("发送密钥完毕,长度为%d
    ",strlen(aeskey_en)+1);
    		printf("是否发送密文?y or n
    ");
    		char choose;
    		char ch;while((ch=getchar())!='
    '&&ch!=EOF);
    		scanf("%c",&choose);
    		if(choose=='y')
    	{//使用会话密钥对消息进行aes加密
    		char ptr_en[512],ptr_de[512];		
    		char ch;
    		
    		i=0;
    		while((ch=fgetc(fp))!=EOF)
    		{
    			buffer[i++]=ch;
    			if(i>=BUFFER_SIZE)
    			{
    				printf("加密前 %s
    ",buffer);
    				aes_encrypt(buffer,aes_keybuf,ptr_en);				
    				
    				//printf("加密后发送密文 %s
    ",ptr_en);
    									 
    				if((send(client_socket, ptr_en, strlen(ptr_en), 0))==-1)
    				{
    					printf("发送文件失败
    ");
    				}
    				printf("发送密文长度 :%d
    ",strlen(ptr_en));
    				bzero(ptr_en, sizeof(ptr_en));
    				bzero(buffer, sizeof(buffer));
    				i=0;
    			}
    		}
    		if(i<BUFFER_SIZE)
    		{
    			
    			printf("加密前消息 %s
    ",buffer);			
    			aes_encrypt(buffer,aes_keybuf,ptr_en);			
    			
    			printf("加密后发送密文 %s
    ",ptr_en);
    			//ptr_de=my_decrypt(buffer,OPENSSLKEY);//解密buffer
    			
    			if((send(client_socket, ptr_en, strlen(ptr_en), 0))==-1)
    			{
    				printf("发送文件失败
    ");
    			}
    			printf("发送密文长度 :%d
    ",strlen(ptr_en));
    			
    		}
    		bzero(buffer, sizeof(buffer));
    		printf("发送密文完毕
    ");
            }
    		/*
    		if(ptr_en!=NULL){
            		free(ptr_en);
        		} */ 
    		mywc(file_name,1);
    		mywc(file_name,2);
    
    		bzero(buffer, sizeof(buffer));
    
    		fclose(fp);
    	 
    		close(client_socket); // 传输完毕,关闭socket  
    		return 0;  
    
    }  
    int mywc(char file_name[],int choose)
    {
    	FILE *fp;
            char ch;
            int flag=0,num=0;
           // int choose;
           //  printf("统计单词个数还是实现“wc -w”?(1or2)
    ");
           // scanf("%d",&choose);
    	if((fp = fopen(file_name,"r"))==NULL)
    	{
       	     printf("Failure to open %s
    ",file_name);
                 exit(0);
            }
    		     
    	if(choose==1)
    	{
    		while((ch=fgetc(fp))!=EOF)
    		{
    			if(ch==' ' || ch=='
    ' || ch=='	' ||  ch=='!' || ch=='?' || ch=='"' || ch=='.' || ch== '\,' || ch==':' || ch=='(' || ch==')' || ch==';'     || ch=='-')
    			{
    				flag=0;
    			}
    			else
    			{
    				if(flag==0)
    				{
    					flag=1;
    					num++;
    				}
    						  
    			}
    
    		}
    
    	}
    	else if(choose==2)
    	{
    		while((ch=fgetc(fp))!=EOF)
    		{
    			if(ch==' ' || ch=='
    ' || ch=='	' || ch=='
    ')
    				flag=0;
    			else
    			{
    				if(flag==0)
    				{
    					flag=1;
    					num++;
    				}
    			}
    		}
    	}
    	printf("单词个数为:%d_用方式%d计算
    ",num,choose);
    	fclose(fp);
    	return num;
    }
    char *my_encrypt(char *str,char *path_key){
        char *p_en;
        RSA *p_rsa;
        FILE *file;
        int flen,rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;    
        }   
        if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
        //if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){   换成这句死活通不过,无论是否将公钥分离源文件
            ERR_print_errors_fp(stdout);
            return NULL;
        }   
        flen=strlen(str);
        rsa_len=RSA_size(p_rsa);
        p_en=(unsigned char *)malloc(rsa_len+1);
        memset(p_en,0,rsa_len+1);
        if(RSA_public_encrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_en,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_en;
    }
    char *my_decrypt(char *str,char *path_key){
        char *p_de;
        RSA *p_rsa;
        FILE *file;
        int rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;
        }
        if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
            ERR_print_errors_fp(stdout);
            return NULL;
        }
        rsa_len=RSA_size(p_rsa);
        p_de=(unsigned char *)malloc(rsa_len+1);
        memset(p_de,0,rsa_len+1);
        if(RSA_private_decrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_de,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_de;
    }
    void aes_encrypt(char *str,char aes_keybuf[],char buf[]){
    	
        AES_KEY aeskey;
        AES_set_encrypt_key(aes_keybuf,256,&aeskey);
        for(int i=0;i<sizeof(str);i+=16)
        AES_encrypt(str+i,buf+i,&aeskey);
        //printf("
    
    in_加密后:%s
    
    
    ",buf);
           
    }
    void aes_decrypt(char *str,char aes_keybuf[],char buf[]){
    	
    	AES_KEY aeskey;
        AES_set_decrypt_key(aes_keybuf,256,&aeskey);
        for(int i=0;i<sizeof(str);i+=16)
        AES_decrypt(str+i,buf+i,&aeskey);
        
    
    }
    
    • 服务器
    #include<netinet/in.h>   
    #include<sys/types.h>   
    #include<sys/socket.h>   
    #include<stdio.h>   
    #include<stdlib.h>   
    #include<string.h>   
    #include<pthread.h> 
    #include<memory.h> 
    #include<openssl/rsa.h>
    #include<openssl/pem.h>
    #include<openssl/err.h>
    #include <openssl/aes.h>
    #pragma comment(lib,"libeay32.lib")
    #define OPENSSLKEY "test.key"
    #define PUBLICKEY "test_pub.key"
    #define BUFFSIZE 1024
    #define HELLO_WORLD_SERVER_PORT    6000 
    #define LENGTH_OF_LISTEN_QUEUE     20  
    #define BUFFER_SIZE                1024  
    #define FILE_NAME_MAX_SIZE         512  
    char* my_encrypt(char *str,char *path_key);//rsa加密
    char* my_decrypt(char *str,char *path_key);//rsa解密
    void aes_encrypt(char *str,char aes_keybuf[],char buf[]);//aes加密
    void aes_decrypt(char *str,char aes_keybuf[],char buf[]);//aes解密
    void *process_client(void *new_server_socket);
    int mywc(char file_name[])
    {
    	char ch;
    	int flag=0,num=0;
    	int choose;
    	FILE *fp;
    	printf("统计单词个数还是实现“wc -w”?(1or2)
    ");
    	scanf("%d",&choose);
    	if((fp = fopen(file_name,"r"))==NULL)
    	{
    		printf("Failure to open %s
    ",file_name);
    	    exit(0);
    	}
    
    	if(choose==1)
    	{
    	while((ch=fgetc(fp))!=EOF)
    	{
    		if(ch==' ' || ch=='
    ' || ch=='	' ||  ch=='!' || ch=='?' || ch=='"' || ch=='.' || ch== '\,' || ch==':' || ch=='(' || ch==')' || ch==';' || ch=='-')
    		{
    			flag=0;
    		}
    		else
    		{
    			if(flag==0)
    			{
    				flag=1;
    				num++;
    			}
    
    		}		   
    	}
    	}
    	else if(choose==2)
    	{
    		while((ch=fgetc(fp))!=EOF)
    		{
    			if(ch==' ' || ch=='
    ' || ch=='	' || ch=='
    ')
    				flag=0;
    			else
    			{
    				if(flag==0)
    				{
    					flag=1;
    					num++;
    				}
    			}
    		}
    	}
    	printf("单词个数为:%d
    ",num);
    	fclose(fp);
    	return num;
    }
    int main(int argc, char **argv)  
    {  
     
    // 设置一个socket地址结构server_addr,代表服务器的IP地址和端口  
    	struct sockaddr_in   server_addr;  
    	bzero(&server_addr, sizeof(server_addr));  
    	server_addr.sin_family = AF_INET;  
    	server_addr.sin_addr.s_addr = htons(INADDR_ANY);  
    	server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);  
    
    // 创建字节流socket:server_socket 
    	int server_socket = socket(AF_INET, SOCK_STREAM, 0);  
    	if (server_socket ==-1)  
    	{  
    		printf("Create Socket Failed!
    ");  
    		exit(1);  
    	}  
    
    // 把socket和socket地址结构绑定   
    	if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))==-1)  
    	{  
    		printf("Server Bind Port: %d Failed!
    ", HELLO_WORLD_SERVER_PORT);  
    		exit(1);  
    	}  
    
    // server_socket用于监听   
    	if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)==-1)  
    	{  
    		printf("Server Listen Failed!
    ");  
    		exit(1);  
    	}  
    // 服务器端一直运行用以持续为客户端提供服务   
    	  
    	while(1)
    	{
    // 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept  
     
    		struct sockaddr_in client_addr;  
    		int length = sizeof(client_addr);    
    // accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信  
    		int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);  		
    		if (new_server_socket ==-1)  
    		{  
    			printf("Server Accept Failed!
    "); 
    			exit(0); 
    		}
    		printf("连接到客户端
    ");
    		pthread_t pid;
    		if(pthread_create(&pid, NULL, process_client,(void *) &new_server_socket) < 0){
    		      printf("pthread_create error
    ");
    		}
    		
    	}
    //	close(server_socket);
    }
    void *process_client(void *new_server_socket)
    {
    		int sockid=*(int *)new_server_socket;
    		FILE *fp;
    		//接受来自客户端的文件
    		char buffer[BUFFER_SIZE]; 
    		char file_name[FILE_NAME_MAX_SIZE];
    		bzero(buffer, sizeof(buffer));  
    		int length=0;
    		if(recv(sockid,buffer,BUFFER_SIZE, 0)==-1)
    		{
    			printf("接受文件名%s失败
    ",buffer);
    		}
    		strcpy(file_name,buffer);
    		strcat(file_name,"_server");
    		printf("创建文件夹%s
    ",file_name);
    		if((fp = fopen(file_name,"w"))==NULL)
    		{
    			printf("Failure to open %s
    ",file_name);
    			exit(0);
    		}
    		bzero(buffer, sizeof(buffer));
    		//接收密钥密文
    		unsigned char *aes_keybuf;
    int lenn;
    		if((lenn=recv(sockid, buffer,BUFFER_SIZE, 0))<0)
    		{
    			printf("接受密钥出错
    ");
    		}
    		printf("接受密钥密文  %s
    
    ",buffer);
    printf("接受密钥length  %d
    
    ",lenn);
    		aes_keybuf=my_decrypt(buffer,OPENSSLKEY);//解密buffer
    		printf("解密得会话密钥  %s
    
    
    ",aes_keybuf);
    		bzero(buffer, sizeof(buffer));
    		char ptr_en[512],ptr_de[512];
    		//length = recv(sockid, buffer,BUFFER_SIZE, 0);
    	    //printf(" recv length :%d
    ",length);
    		while( length = recv(sockid, buffer,BUFFER_SIZE, 0))
    		{
    			if(length<0)
    			{
    				printf("接受文件出错
    ");
    				exit(0);
    			}
    			printf("接受密文  %s
    
    
    ",buffer);
    			aes_decrypt(buffer,aes_keybuf,ptr_de);//解密buffer
    					
    			printf("解密密文  %s
    ",ptr_de);
    			fwrite(ptr_de,sizeof(char),strlen(ptr_de),fp)<length;
    			
    			bzero(buffer, sizeof(buffer));
    			bzero(ptr_de, sizeof(ptr_de));
    		}
    		fclose(fp);
    		/*if(ptr_de!=NULL){
            		free(ptr_de);
        		} */
    		printf("接收密文完毕
    ");
    		bzero(buffer, BUFFER_SIZE);
    		int number=0;
    		number=mywc(file_name);
    		//bzero(buffer, BUFFER_SIZE);  
    		//buffer[0]=number+48;
    
    	        bzero(buffer, sizeof(buffer));  
     		 
    		printf("File Transfer Finished!
    ");   
    		close(new_server_socket);  
    } 
    char *my_encrypt(char *str,char *path_key){
        char *p_en;
        RSA *p_rsa;
        FILE *file;
        int flen,rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;    
        }   
        if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
        //if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){   换成这句死活通不过,无论是否将公钥分离源文件
            ERR_print_errors_fp(stdout);
            return NULL;
        }   
        flen=strlen(str);
        rsa_len=RSA_size(p_rsa);
        p_en=(unsigned char *)malloc(rsa_len+1);
        memset(p_en,0,rsa_len+1);
        if(RSA_public_encrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_en,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_en;
    }
    char *my_decrypt(char *str,char *path_key){
        char *p_de;
        RSA *p_rsa;
        FILE *file;
        int rsa_len;
        if((file=fopen(path_key,"r"))==NULL){
            perror("open key file error");
            return NULL;
        }
        if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
            ERR_print_errors_fp(stdout);
            return NULL;
        }
        rsa_len=RSA_size(p_rsa);
        p_de=(unsigned char *)malloc(rsa_len+1);
        memset(p_de,0,rsa_len+1);
        if(RSA_private_decrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_de,p_rsa,RSA_NO_PADDING)<0){
            return NULL;
        }
        RSA_free(p_rsa);
        fclose(file);
        return p_de;
    }
    void aes_encrypt(char *str,char aes_keybuf[],char buf[]){
    	
    	AES_KEY aeskey;
        AES_set_encrypt_key(aes_keybuf,256,&aeskey);
        for(int i=0;i<sizeof(str);i+=16)
        AES_encrypt(str+i,buf+i,&aeskey);
        
    }
    void aes_decrypt(char *str,char aes_keybuf[],char buf[]){
    	
    	AES_KEY aeskey;
        AES_set_decrypt_key(aes_keybuf,256,&aeskey);
        for(int i=0;i<sizeof(str);i+=16)
        AES_decrypt(str+i,buf+i,&aeskey);
        
    }
    

    运行结果

    问题及解决过程

    • 问题1:gcc -o to test_openssl.c -I /usr/local/ssl/inlcude /usr/local/ssl/lib -ldl -lpthread指令如何理解?
    • 解决方案:查询gcc命令参数-I如下所述

    -l参数就是用来指定程序要链接的库,-l参数紧接着就是库名。 放在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接了。

    • 与-L命令的区别:

    如果库文件 没放在这三个目录里,而是放在其他目录里,链接程序ld在那3个目录里找不到libxxx.so,参数-L就派上用场了,比如常用的X11的库,它放在/usr/X11R6/lib目录下,我们编译时就要用-L/usr/X11R6/lib -lX11参数,-L参数跟着的是库文件所在的目录名。

    -- 引用自《GCC常用参数详解

    • 这条指令用-I参数指定了两个链接库 /usr/local/ssl/inlcude 和/usr/local/ssl/lib。接着对后面选项-ldl -lpthread进行了查询,结果如下:

    如果你的程序中使用dlopen、dlsym、dlclose、dlerror 显示加载动态库,需要设置链接选项 -ldl。

    -- 引用自《gcc -ldl 选项作用

    其中dlopen用于打开动态链接库,dlsym用于取函数执行地址,dlclose用于关闭动态链接库,dlerror为动态库错误函数,他们的函数原型如下:

    void *dlopen (const char *filename, int flag); 
    //dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄
    void *dlsym(void *handle, char *symbol);
    // dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址
    int dlclose (void *handle);
    //dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载
    const char *dlerror(void);
    //当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功
    

    -lpthread选项——链接POSIX thread库

    • 问题2:编译安装OpenSSL 1.1.0alpha时出现两个错误,如下图所示:

    • 解决方案:去官网上重新下载了“openssl-master.zip”,删除了原来的压缩包和解压后的文件夹。
      • 解压压缩包:unzip openssl-master.zip
      • 进入目录openssl-master:cd openssl-master
      • 使用以下命令编译安装:
    $ ./config
    $ make
    $ make test
    $ make install
    
    • 问题3:使用make install命令安装时出现下图所示找不到路径的错误:

    • 解决方案:在make install前加上“sudo” 增加权限,最终安装成功,如下图所示:

    • 问题4:在完成任务二时出现了客户端与服务器发送接收数据不同步的问题,客户端发送密钥的密文和消息密文,但是服务器将后部分消息密文也作为密钥密文读入,如下图所示:

    • 解决过程:在客户端发送密钥密文后,增加一个确认是否发送的环节,接收用户输入,再根据输入选择是否发送密文。
      • 但出现了未等用户输入就直接将缓冲区中数据读入的问题。
      • 解决:利用语句 char ch;while((ch=getchar())!=' '&&ch!=EOF);清空缓冲区

    最终通过增加输入解决了同步问题,如下图所示:

    参考资料

  • 相关阅读:
    JSE-1.1.4 内存屏障和CPU缓存
    Ajax
    R手册(Common)--R6 and S4
    掌握 小程序项目新建后的 初始代码 及 git远程管理(2)
    微信小程序 网课学习笔记 开发前的准备工作(1)
    vuex中action如何互相调用
    ajax请求时,请求路径自动拼上页面路径?
    10个免费的CDN
    java面向对象
    java中方法的递归调用
  • 原文地址:https://www.cnblogs.com/zjy1997/p/8030357.html
Copyright © 2020-2023  润新知