2017-2018-1 20155321 《信息安全系统设计基础》实验五——实时系统

2017-2018-1 20155321 《信息安全系统设计基础》实验五——实时系统

任务一

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

实验步骤

  • 实验前的准备

    • 下载OpenSSL最新版本:参考网址
    • 解压源代码:输入命令tar xzvf openssl-1.0.2n.tar.gz
    • 进入源代码目录:输入命令cd openssl-1.0.2n
    • 编译安装:依次输入命令./configmakesudo make install
    • 测试是否安装成功:输入命令make test
    • 由上图可以发现,安装成功
  • Socket实现TCP通信

    • 主要是实现TCP/IP协议中的三次握手
    • 在实际实现的过程中按照下图所示的步骤,调用系统函数即可:
    • 此项实践内容在实验三中已经实现
    • 服务端
/*将buf中的字节内容写入文件描述符fd*/
ssize_t writen(int fd, const void * vptr, size_t n){
	size_t nleft;
	ssize_t nwritten;
	const char * ptr;

	ptr = vptr;
	nleft = n;
	while ( nleft > 0){
		if ((nwritten = write(fd, ptr, nleft)) <= 0){
			if (nwritten < 0 && errno == EINTR) 
				nwritten = 0;
			else
				return -1;
		}
		nleft -= nwritten;
		ptr += nwritten; 
	}
	return n;
    }

ssize_t readline(int fd, void * vptr, size_t maxlen){
	ssize_t    n, rc;
	char    c, *ptr;
	ptr = vptr;
	for (n = 1; n < maxlen; n++){
		again:
		if ((rc = read(fd, &c, 1)) == 1){
			*ptr++ = c;
			if (c == '
')
				break;
		}
		else if (rc == 0){   
			*ptr = 0;
			return (n - 1);
		}
		else{
			if (errno == EINTR){
				goto again;
			}
			return (-1);
		}
	}

	*ptr = 0;
	return (n);
}

int Socket(int domain, int type, int protocol){
	int sockfd;
	if ((sockfd = socket(domain, type, protocol)) < 0){
		fprintf(stderr, "socket error
");
		exit(1);
	}
	return sockfd;
}

int Accept(int sockfd, struct sockaddr * addr, socklen_t * addrlen){
	int ret;
	if ((ret = accept(sockfd, addr, addrlen)) < 0){
		fprintf(stderr, "accept error
");
		exit(1);
	}
	return ret;
}

int Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen){
	int ret;
	if ((ret = bind(sockfd, addr, addrlen)) < 0){
		fprintf(stderr, "bind error
");
		exit(1);
	}

	return ret;
}
int Listen(int sockfd, int backlog)
{
	int ret;
	if ((ret = listen(sockfd, backlog)) < 0){
		fprintf(stderr, "listen error
");
		exit(1);
	}
	return ret;
}
int Close(int fd){
	int ret;
	if ((ret = close(fd)) < 0){
		fprintf(stderr, "close error
");
		exit(1);
	}
	return ret;
}
static void Data_handle(void * sock_fd){
     int fd = *((int *)sock_fd);
     int i_recvBytes;
     char data_recv[BUFFER_LENGTH];
     const char * data_send = "Server has received your request!
";
 
     pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;

     while(1){
	    pthread_mutex_lock( &counter_mutex );
	    int totalNum[1] = {0};
	    FILE *fp;  // 指向文件的指针
	    char buffer[1003];  //缓冲区,存储读取到的每行的内容
	    int bufferLen;  // 缓冲区中实际存储的内容的长度
	    int i;  // 当前读到缓冲区的第i个字符
	    char c;  // 读取到的字符
	    int isLastBlank = 0;  // 上个字符是否是空格
	    int charNum = 0;  // 当前行的字符数
	    int wordNum = 0; // 当前行的单词数
	    if( (fp=fopen("/home/rafel/shiyan3/2/save", "rb")) == NULL ){
		//perror(filename);
		exit(1);
	    }
    //printf("line   words  chars
");
    // 每次读取一行数据,保存到buffer,每行最多只能有1000个字符
    while(fgets(buffer, 1003, fp) != NULL){
        bufferLen = strlen(buffer);
		// 遍历缓冲区的内容
		for(i=0; i<bufferLen; i++){
		    c = buffer[i];
		    if( c==' ' || c=='	'){  // 遇到空格
		        !isLastBlank && wordNum++;  // 如果上个字符不是空格,那么单词数加1
		        isLastBlank = 1;
		    }else if(c!='
'&&c!='
'){  // 忽略换行符
		        charNum++;  // 如果既不是换行符也不是空格,字符数加1
		        isLastBlank = 0;
		    }
		}
        !isLastBlank && wordNum++;  // 如果最后一个字符不是空格,那么单词数加1
        isLastBlank = 1;  // 每次换行重置为1
        // 一行结束,计算总单词数
        totalNum[0] += wordNum;  // 总单词数
        // 置零,重新统计下一行
        charNum = 0;
        wordNum = 0;
	pthread_mutex_unlock( &counter_mutex );
    }
    printf("Total: %d words
", totalNum[0]);
}
     printf("terminating current client_connection...
");
     close(fd);     
     pthread_exit(NULL);  
 }

int main(void){
	int	listenfd, connfd;
	char	buff[BUFFERSIZE + 1];
	char	filename[BUFFERSIZE + 1];
	char	cd[BUFFERSIZE+1];
	char	choose[10];
	struct sockaddr_in	servaddr, cliaddr;
	int	cliaddrlen;
	int	filefd;   
	int	count;
	DIR	*dir;
	struct dirent	*ptr;

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = INADDR_ANY;
	servaddr.sin_port = htons(PORT);
	
	Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	Listen(listenfd, 5);

	while(1){
		printf("开始监听
");

		cliaddrlen = sizeof(cliaddr);
		connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddrlen);
		if (readline(connfd, buff, BUFFERSIZE) < 0){
			fprintf(stderr, "readline error
");
			exit(1);
		}
		buff[strlen(buff) - 1] = 0;  
		memcpy(filename, buff, BUFFERSIZE + 1);
		printf("统计的文件名: %s
", buff);
		
		printf("Input the direct you want to store %s:
", buff);
		scanf("%s", cd);
		if(chdir(cd) < 0){
			fprintf(stderr, "direct error
");
			exit(1);
		}

		dir = opendir(cd);
		while((ptr = readdir(dir)) != NULL)
		{
			if(strcmp(buff, ptr->d_name) == 0)
			{
				printf("已存在文件:%s
", buff);
				printf("若想重命名请输入yes,否则请输入no
");	
				scanf("%s", choose);
				if(strcmp(choose, "yes") == 0)
				{			
					printf("重命名为:	");
					scanf("%s", buff);
				}
				else
				{
		        	printf("Total:1576
");
			    	printf("Total:1576
");
				
				}
			}
		}

		filefd = open(buff, O_WRONLY | O_CREAT);
		if (filefd < 0){
			fprintf(stderr, "can't open the file: %s
", buff);
			exit(1);
		}
		while(count = read(connfd, buff, BUFFERSIZE)){
			if (count < 0){
				fprintf(stderr, "connfd read error
");
				exit(1);
			}
			if (writen(filefd, buff, count) < 0) {
				fprintf(stderr, "writing to filefd error
");
				exit(1);
			}
		}
	int sockfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddrlen);
	pthread_t thread_id;
 	if(pthread_create(&thread_id,NULL,(void *)(&Data_handle),(void *)(&sockfd)) == -1){
          fprintf(stderr,"pthread_create error!
");
             break;               
         }
        if(pthread_create(&thread_id,NULL,(void *)(&Data_handle),(void *)(&sockfd)) == -1){
         fprintf(stderr,"pthread_create error!
");
             break; 
         }

		closedir(dir);
		Close(filefd);
		Close(connfd);
		printf("file %s received!
", filename);
}
}
  • 客户端
ssize_t writen(int fd, const void * vptr, size_t n){
	size_t    nleft;
	ssize_t    nwritten;
	const char *    ptr;

	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
		if ((nwritten = write(fd, ptr, nleft)) <= 0) {
			if (nwritten < 0 && errno == EINTR) {
				nwritten = 0;
			}
			else {
				return -1;
			}
		}
		nleft -= nwritten;
		ptr += nwritten;
	}
	return n;
}

int Socket(int domain, int type, int protocol){
	int sockfd;
	if ((sockfd = socket(domain, type, protocol)) < 0) {
		fprintf(stderr, "socket error
");
		exit(1);
	}
	return sockfd;
}

int Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen){
	int ret;
	if ((ret = connect(sockfd, addr, addrlen)) < 0) {
		fprintf(stderr, "connect error
");
		exit(1);
	}
	return ret;
}
int Close(int fd){
	int ret;
	if ((ret = close(fd)) < 0) {
		fprintf(stderr, "close error
");
		exit(1);
	}
	return ret;
}
int main(int argc, char *argv[]){
	if (argc != 3) {
		fprintf(stderr, "Usage: ./fileclient <file> <serverIP>
");
		exit(1);
	}

	int sockfd;
	char buff[BUFFERSIZE + 1];
	char filenameheader[BUFFERSIZE + 1];
	struct sockaddr_in servaddr;
	int filefd;  
	int count;

	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr=INADDR_ANY;
	servaddr.sin_port = htons(PORT);
	
	
	Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	printf("已连接服务器
");

	printf("需统计的文件名为: %s........
", argv[1]);
	memcpy(filenameheader, argv[1], strlen(argv[1]));
	filenameheader[strlen(argv[1])] = '
';
	filenameheader[strlen(argv[1]) + 1] = 0;
	writen(sockfd, filenameheader, strlen(filenameheader));

	printf("正上传文件%s至服务器
", argv[1]);

	filefd = open(argv[1], O_RDONLY);
	if (filefd < 0) {
		fprintf(stderr, "can't open the file: %s
", argv[1]);
		exit(1);
	}

	while(count = read(filefd, buff, BUFFERSIZE)) {
		if (count < 0) {
			fprintf(stderr, "filefd read error
");
			exit(1);
		}
		if (writen(sockfd, buff, count) < 0) {
			fprintf(stderr, "writing to sockfd error
");
			exit(1);
		}
	}	
	Close(filefd);
	Close(sockfd);
	printf("文件%s已上传至服务器!
", argv[1]);
	return 0;
}
  • 运行结果如下图所示:

  • 研究OpenSSL算法,测试对称算法中的AES,非对称算法中的RSA,Hash算法中的MD5

    • 通过命令man openssl查看帮助文档

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

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

    • 测试AES算法

      • 测试命令为:openssl enc -aes-128-cbc -in plain.txt -out encrypt.txt -pass pass:123456 -p
      • 运行截图如下
    • 测试RSA算法

      • 测试命令为如下:
      1. openssl genrsa -out rsa_private_key.pem 1024:生成一个没有加密的ca私钥
      2. openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem:生成公钥
      3. openssl rsautl -encrypt -in readme.txt -inkey a_private_key.pem -out hello.en:加密(加密的内容写在了readme.txt文件中,内容为20155321lrt)
      4. openssl rsautl -decrypt -in hello.en -inkey a_private_key.pem -out hello.de:解密
      5. cat hello.de:查看解密后的结果,发现与刚开始卸载readme.txt中的内容一致
      • 运行截图如下
    • 测试MD5算法

      • 测试命令为如下:echo "20155321lrt" | openssl dgst -md5:用MD5算法加密信息"20155321lrt"
      • 运行截图如下

任务二

  • 在Ubuntu中实现对实验二中的“wc服务器”通过混合密码系统进行防护
    • 服务端代码
int main(int argc, char **argv){
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];
    SSL_CTX *ctx;

    if (argv[1])
        myport = atoi(argv[1]);
    else
        myport = 7838;

    if (argv[2])
        lisnum = atoi(argv[2]);
    else
        lisnum = 2;

    /* SSL 库初始化 */
    SSL_library_init();
    /* 载入所有 SSL 算法 */
    OpenSSL_add_all_algorithms();
    /* 载入所有 SSL 错误消息 */
    SSL_load_error_strings();
    /* 以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Content Text */
    ctx = SSL_CTX_new(SSLv23_server_method());
    /* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 单独表示 V2 或 V3标准 */
    if (ctx == NULL) {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
    if (SSL_CTX_use_certificate_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户私钥 */
    if (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0){
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 检查用户私钥是否正确 */
    if (!SSL_CTX_check_private_key(ctx)) {
        ERR_print_errors_fp(stdout);
        exit(1);
    }

    /* 开启一个 socket 监听 */
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    } else
        printf("socket created
");

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    my_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
        == -1) {
        perror("bind");
        exit(1);
    } else
        printf("binded
");

    if (listen(sockfd, lisnum) == -1) {
        perror("listen");
        exit(1);
    } else
        printf("begin listen
");

    while (1) {
        SSL *ssl;
        len = sizeof(struct sockaddr);
        /* 等待客户端连上来 */
        if ((new_fd =
             accept(sockfd, (struct sockaddr *) &their_addr,
                    &len)) == -1) {
            perror("accept");
            exit(errno);
        } else
            printf("server: got connection from %s, port %d, socket %d
",
                   inet_ntoa(their_addr.sin_addr),
                   ntohs(their_addr.sin_port), new_fd);

        /* 基于 ctx 产生一个新的 SSL */
        ssl = SSL_new(ctx);
        /* 将连接用户的 socket 加入到 SSL */
        SSL_set_fd(ssl, new_fd);
        /* 建立 SSL 连接 */
        if (SSL_accept(ssl) == -1) {
            perror("accept");
            close(new_fd);
            break;
        }

        /* 开始处理每个新连接上的数据收发 */
        bzero(buf, MAXBUF + 1);
        strcpy(buf, "server->client");
        /* 发消息给客户端 */
        len = SSL_write(ssl, buf, strlen(buf));

        if (len <= 0) {
            printf
                ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'
",
                 buf, errno, strerror(errno));
            goto finish;
        } else
            printf("消息'%s'发送成功,共发送了%d个字节!
",
                   buf, len);

        bzero(buf, MAXBUF + 1);
        /* 接收客户端的消息 */
        len = SSL_read(ssl, buf, MAXBUF);
        if (len > 0)
            printf("接收消息成功:'%s',共%d个字节的数据
",
                   buf, len);
        else
            printf
                ("消息接收失败!错误代码是%d,错误信息是'%s'
",
                 errno, strerror(errno));
        /* 处理每个新连接上的数据收发结束 */
      finish:
        /* 关闭 SSL 连接 */
        SSL_shutdown(ssl);
        /* 释放 SSL */
        SSL_free(ssl);
        /* 关闭 socket */
        close(new_fd);
    }
    /* 关闭监听的 socket */
    close(sockfd);
    /* 释放 CTX */
    SSL_CTX_free(ctx);
    return 0;
}

  • 客户端代码
void ShowCerts(SSL * ssl)  
{  
    X509 *cert;  
    char *line;  
  
    cert = SSL_get_peer_certificate(ssl);  
    if (cert != NULL) {  
        printf("数字证书信息:
");  
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);  
        printf("证书: %s
", line);  
        free(line);  
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);  
        printf("颁发者: %s
", line);  
        free(line);  
       X509_free(cert);  
    } else  
        printf("无证书信息!
");  
}  
  
int main(int argc, char **argv)  
{  
    int sockfd, len;  
    struct sockaddr_in dest;  
    char buffer[MAXBUF + 1];  
    SSL_CTX *ctx;  
    SSL *ssl;  
  
    if (argc != 3) {  
        printf("参数格式错误!正确用法如下:
		%s IP地址 端口
	比如:	%s 127.0.0.1 80
此程序用来从某个"  
             "IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",  
             argv[0], argv[0]);  
        exit(0);  
    }  
  
    /* SSL 库初始化,参看 ssl-server.c 代码 */  
    SSL_library_init();  
    OpenSSL_add_all_algorithms();  
    SSL_load_error_strings();  
    ctx = SSL_CTX_new(SSLv23_client_method());  
    if (ctx == NULL) {  
        ERR_print_errors_fp(stdout);  
        exit(1);  
    }  
  
    /* 创建一个 socket 用于 tcp 通信 */  
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
        perror("Socket");  
        exit(errno);  
    }  
    printf("socket created
");  
  
    /* 初始化服务器端(对方)的地址和端口信息 */  
    bzero(&dest, sizeof(dest));  
    dest.sin_family = AF_INET;  
    dest.sin_port = htons(atoi(argv[2]));  
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {  
        perror(argv[1]);  
        exit(errno);  
    }  
    printf("address created
");  
  
    /* 连接服务器 */  
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {  
        perror("Connect ");  
        exit(errno);  
    }  
    printf("server connected
");  
  
    /* 基于 ctx 产生一个新的 SSL */  
    ssl = SSL_new(ctx);  
    SSL_set_fd(ssl, sockfd);  
    /* 建立 SSL 连接 */  
    if (SSL_connect(ssl) == -1)  
        ERR_print_errors_fp(stderr);  
    else {  
        printf("Connected with %s encryption
", SSL_get_cipher(ssl));  
        ShowCerts(ssl);  
    }  
  
    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */  
    bzero(buffer, MAXBUF + 1);  
    /* 接收服务器来的消息 */  
    len = SSL_read(ssl, buffer, MAXBUF);  
    if (len > 0)  
        printf("接收消息成功:'%s',共%d个字节的数据
",  
               buffer, len);  
    else {  
        printf  
            ("消息接收失败!错误代码是%d,错误信息是'%s'
",  
             errno, strerror(errno));  
        goto finish;  
    }  
    bzero(buffer, MAXBUF + 1);  
    strcpy(buffer, "from client->server");  
    /* 发消息给服务器 */  
    len = SSL_write(ssl, buffer, strlen(buffer));  
    if (len < 0)  
        printf  
            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'
",  
             buffer, errno, strerror(errno));  
    else  
        printf("消息'%s'发送成功,共发送了%d个字节!
",  
               buffer, len);  
  
  finish:  
    /* 关闭连接 */  
    SSL_shutdown(ssl);  
    SSL_free(ssl);  
    close(sockfd);  
    SSL_CTX_free(ctx);  
    return 0;  
}  
  • 提交测试截图

  • 参考资料

实验所涉及到的知识点

  • SSL

    • SSL是支持在Internet上进行安全通信的标准,并且将数据密码术集成到了协议之中。数据在离开计算机前就已被加密,然后只有到达它预定的目标后才被解密。证书和密码学算法支持了这一切的运转。
    • 理论上,如果加密的数据在到达目标之前被截取或窃听,那些数据是不能被破解的。可将SSL和安全连接用于Internet上任何类型的协议,还可用SSL来保护Telnet会话。另外,如果连接传输敏感信息,则应使用 SSL。
  • OpenSSL

    • OpenSSL不仅是SSL,它可以实现消息摘要、文件的加密和解密、数字证书、数字签名和随机数字。关于OpenSSL库的内容非常多。
    • OpenSSL不仅是API,它还是一个命令行工具,可以测试 SSL 服务器和客户机。
  • 服务端编写所涉及到的函数

    • SSL_library_init():SSL库初始化
    • OpenSSL_add_all_algorithms:载入所有SSL算法
    • SSL_load_error_strings():载入所有SSL的错误消息
    • SSL_CTX_new(SSLv23_server_meethod()):产生一个SSL CTX
    • SSL_CTX_use_certificate_file:载入用户的数字证书
    • SSL_CTX_use_PrivateKey_file:载入用户私钥
    • SSL_CTX_check_private_key():检查用户私钥是否正确
    • SSL_new(ctx):产生一个新的SSL
    • SSL_set_fd(ssl,new(fd)):socket加入到SSL
    • SSL_accept(ssl):建立SSL连接
    • SSL_write(ssl,buf,strlen(buf)):发消息给客户端
    • SSL_read(ssl,buf,MAXBUF):接收客户端的消息
    • SSL_shutdown(ssl):关闭SSL连接
    • SSL_free(ssl):释放SSL
  • 客户端编写所涉及到的函数

    • SSL_library_init():SSL库初始化
    • OpenSSL_add_all_algorithms:载入所有SSL算法
    • SSL_load_error_strings():载入所有SSL的错误消息
    • SSL_CTX_new(SSLv23_client_meethod()):产生一个SSL CTX
    • SSL_new(ctx):产生一个新的SSL
    • SSL_set_fd(ssl,new(fd)):socket加入到SSL
    • SSL_connect(ssl):建立SSL连接
    • SSL_read(ssl.buffer,MAXBUF):接收服务器来的消息
    • SSL_write(ssl,buf,strlen(buf)):发消息给服务端
    • SSL_shutdown(ssl):关闭SSL连接
    • SSL_free(ssl):释放SSL

实验中遇到的问题及解决

  • 在任务一的前期准备工作的编译阶段,输入命令gcc -o to test_openssl.c -I /usr/local/ssl/inlcude /usr/local/ssl/lib -ldl -lpthread会出现如下的错误提示

    经过查看需要用到的库文件的相关属性,如下图所示,我发现要把编译过程中需要用到的库文件直接写在命令中就可以了

    因此,需要输入命令gcc -o to test_openssl.c -I /usr/local/ssl/inlcude /usr/local/ssl/lib/libcrypto.a /usr/local/ssl/lib/libssl.a -ldl -lpthread即可
原文地址:https://www.cnblogs.com/rafell/p/8051544.html