SSL探索01

1. OPENSSL简介

OpenSSL项目是一个协作开发一个健壮的,商业级的,全功能的,并且开放源代码工具包,它实现了安全套接字层(SSL v2/v3)和传输层安全(TLS v1)协议以及全强大的通用加密库。

2.使用SSL进行安全IO

使用SSL进行安全IO与原本的socketIO通信区别不大,只是增加了SSL部分内容.

下面说明SSL IO的基本过程:

客户端流程

① // SSL初始化

②//socket,connect,

③//ssl,建立SSL连接,SSL_connect(ssl)

④//SSL_write,SSL_read

⑤//关闭操作SSL_shutdown,SSL_free,close,SSL_CTX_free

服务端流程

① // SSL初始化

②//socket,bind,listen,accept

③//ssl,建立SSL连接,SSL_accept(ssl)

④//SSL_write,SSL_read

⑤//关闭操作SSL_shutdown,SSL_free,close,SSL_CTX_free

3.使用BIO进行安全IO

BIO是Openssl对IO类型的抽象封装,包括:内存、文件、日志、标准输入输出、socket(TCP/UDP)、加解密、摘要和ssl通道等。Openssl
BIO通过回调函数为用户隐藏了底层实现细节。BIO的使用使得代码得到很大简化.

使用BIO进行安全IO的基本过程

①//SSL 初始化

②//加载可信认证书库(注意: 若不加载或加载失败,则证书无效,但仍可以继续连接(由客户端自己控制是否继续进行通信))

SSL_CTX_load_verify_locations(ctx, "TrustStore.pem", NULL)

③//建立连接

bio = BIO_new_ssl_connect(ctx);

/* Set the SSL_MODE_AUTO_RETRY flag */

BIO_get_ssl(bio, &ssl);

SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

/* Create and setup the connection */

BIO_set_conn_hostname(bio, "www.baidu.com:https");

④//BIO_write,BIO_read

⑤//关闭操作BIO_free_all,SSL_CTX_free

对比直接使用SSL进行IO与使用BIO进行通信,发现使用BIO更加简洁方便,建议更多的使用BIO.

4.操作SSL过程中的错误检测

OpenSSL 抛出了某种类型的错误。首先,需要得到错误代码本身; ERR_get_error 可以完成这项任务;

然后,需要将错误代码转换为错误字符串,它是一个指向由 SSL_load_error_strings 或 ERR_load_BIO_strings 加载到内存中的永久字符串的指针。 方法:ERR_reason_error_string,ERR_lib_error_string,ERR_func_error_string.

还可以将错误进行转储:ERR_print_errors_fp(FILE *);ERR_print_errors(BIO *);

代码如下:

a.client_SSL.cpp

int main(int argc, char * *argv) {
	int sockfd, len;
	struct sockaddr_in dest;
	char buffer[MAXBUF + 1];
	SSL_CTX * ctx;
	SSL * ssl;

	/* 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_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\n");
	/* 初始化服务器端(对方)的地址和端口信息*/
	bzero(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	//设置连接的端口
	dest.sin_port = htons(12345);
	//设置连接的IP地址
	char *addr = "127.0.0.1";
	//115.239.210.27百度
	//char *addr2 = "115.239.210.27";
	if (inet_aton(addr, (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
		perror(argv[0]);
		exit(errno);
	}
	printf("address created\n");
	/* 连接服务器*/
	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
		perror("Connect ");
		exit(errno);
	}
	printf("server connected\n");
	/* 基于ctx 产生一个新的SSL */
	ssl = SSL_new(ctx);
	/* 将新连接的socket 加入到SSL */
	SSL_set_fd(ssl, sockfd);

	//----------------------------------------------------------

//	/* Load the trust store */
//	if (!SSL_CTX_load_verify_locations(ctx, "TrustStore.pem", NULL)) {
//		fprintf(stderr, "Error loading trust store\n");
//		ERR_print_errors_fp(stderr);
//		SSL_CTX_free(ctx);
//		return 0;
//	}
	//----------------------------------------------------------

	/* 建立SSL 连接*/
	if (SSL_connect(ssl) == -1) {
		ERR_print_errors_fp(stderr);
	} else {
		printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
		ShowCerts(ssl);
	}

	/* 接收对方发过来的消息,最多接收MAXBUF 个字节*/
	bzero(buffer, MAXBUF + 1);
	/* 接收服务器来的消息*/
	len = SSL_read(ssl, buffer, MAXBUF);
	if (len > 0) {
		printf("接收消息成功:'%s',共%d 个字节的数据\n", buffer, len);
	} else {
		printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", 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'\n", buffer, errno,
				strerror(errno));
	} else {
		printf("消息'%s'发送成功,共发送了%d 个字节!\n", buffer, len);
	}
	finish:
	/* 关闭连接*/
	SSL_shutdown(ssl);
	SSL_free(ssl);
	close(sockfd);
	SSL_CTX_free(ctx);

	return 0;
}

b.server_SSL.cpp

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;
	//指定监听端口
	myport = 12345;
	//最大客户端连接数
	lisnum = 10;

	/* 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,
			"/home/shuyan/workspace/Openssl_Server/files/cacert.pem",
			SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		exit(1);
	}
	/* 载入用户私钥*/
	if (SSL_CTX_use_PrivateKey_file(ctx,
			"/home/shuyan/workspace/Openssl_Server/files/privkey.pem",
			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\n");
	}
	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\n");
	}
	if (listen(sockfd, lisnum) == -1) {
		perror("listen");
		exit(1);
	} else {
		printf("begin listen\n");
	}
	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\n",
					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'\n", buf, errno,
					strerror(errno));
			goto finish;
		} else {
			printf("消息'%s'发送成功,共发送了%d 个字节!\n", buf, len);
		}
		bzero(buf, MAXBUF + 1);
		/* 接收客户端的消息*/
		len = SSL_read(ssl, buf, MAXBUF);
		if (len > 0) {
			printf("接收消息成功:'%s',共%d 个字节的数据\n", buf, len);
		} else {
			printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno));
			ERR_reason_error_string(ERR_get_error());
		}
		/* 处理每个新连接上的数据收发结束*/
		finish:
		/* 关闭SSL 连接*/
		SSL_shutdown(ssl);
		/* 释放SSL */
		SSL_free(ssl);
		/* 关闭socket */
		close(new_fd);
	}
	/* 关闭监听的socket */
	close(sockfd);
	/* 释放CTX */
	SSL_CTX_free(ctx);
	return 0;
}

c.client_BIO.cpp

int main() {
	BIO * bio;
	SSL * ssl;
	SSL_CTX * ctx;
	int p;
	char * request =
				"GET / HTTP/1.1\x0D\x0AHost: www.baidu.com\x0D\x0A\x43onnection: Close\x0D\x0A\x0D\x0A";
	char r[1024];

	/* SSL 库初始化*/
	SSL_library_init();
	/* 载入所有SSL 算法*/

	OpenSSL_add_all_algorithms();
	/* 载入所有SSL 错误消息*/
	SSL_load_error_strings();
	/* Set up the library */
	ERR_load_BIO_strings();

	/* Set up the SSL context */

	ctx = SSL_CTX_new(SSLv23_client_method());

	/* Load the trust store */
	if (!SSL_CTX_load_verify_locations(ctx, "TrustStore.pem", NULL)) {
		fprintf(stderr, "Error loading trust store\n");
		ERR_print_errors_fp(stderr);
		SSL_CTX_free(ctx);
		return 0;
	}
	/* Setup the connection */

	bio = BIO_new_ssl_connect(ctx);
	bio = BIO_new(BIO_s_socket());
	/* Set the SSL_MODE_AUTO_RETRY flag */

	BIO_get_ssl(bio, &ssl);
	SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

	/* Create and setup the connection */
	BIO_set_conn_hostname(bio, "www.baidu.com:https");

	if (BIO_do_connect(bio) <= 0) {
		fprintf(stderr, "Error attempting to connect\n");
		ERR_print_errors_fp(stderr);
		BIO_free_all(bio);
		SSL_CTX_free(ctx);
		return 0;
	}

	/* Check the certificate */

	if (SSL_get_verify_result(ssl) != X509_V_OK) {
		fprintf(stderr, "Certificate verification error: %i\n",
				SSL_get_verify_result(ssl));
		BIO_free_all(bio);
		SSL_CTX_free(ctx);
		return 0;
	}

	ShowCerts(ssl);

	/* Send the request */

	BIO_write(bio, request, strlen(request));

	/* Read in the response */

	for (;;) {
		p = BIO_read(bio, r, 1023);
		if (p <= 0)
			break;
		r[p] = 0;
		printf("%s", r);
	}

	/* Close the connection and free the context */

	BIO_free_all(bio);
	SSL_CTX_free(ctx);
	return 0;
}
时间: 2024-08-06 04:31:19

SSL探索01的相关文章

SSL探索03

本文探索Openssl的Engine机制.Openssl硬件引擎(Engine)能够使用户比较容易地将自己的硬件加入到openssl中去,替换其提供的软件算法. ENGINE 是 OPENSSL 预留的用以加载第三方加密库引擎,主要包括了动态库加载的代码和加密函数指针管理的一系列接口.如果要使用 Engine(假设你已经加载上 该 Engine 了 ) , 那 么 首 先 要 加 载 该Engine(比如 ENGINE_load_XXXX),然后选择要使用的算法或者使用支持的所有加密算法.这样你

SSL探索02

这篇文章探索TLS -ticket 的重用机制. 完整的SSL握手过程为: Client Server ClientHello (empty SessionTicket extension)--------> ServerHello (empty SessionTicket extension) Certificate* ServerKeyExchange* CertificateRequest* <-------- ServerHelloDone Certificate* ClientKey

一套代码小程序&amp;Web&amp;Native运行的探索01

前言 前面我们对微信小程序进行了研究:[微信小程序项目实践总结]30分钟从陌生到熟悉 并且用小程序翻写了之前一个demo:[组件化开发]前端进阶篇之如何编写可维护可升级的代码 之前一直在跟业务方打交道后面研究了下后端,期间还做了一些运营.管理相关工作,哈哈,最近一年工作经历十分丰富啊,生命在于不断的尝试嘛. 当然,不可避免的在前端技术一块也稍微有点落后,对React&Vue没有进行过深入一点的研究,这里得空我们便来一起研究一番(回想起来写代码的日子才是最快乐的??),因为我们现在也慢慢在切Rea

MySQL技术探索01实现SQL语法解析器

本文将介绍如何使用开源的语法和词法分析框架bison和flex来实现SQL解析器.出于技术学习的目的,本文做描述的微型SQL解析器仅能实现对微型SQL的语法解析. 1.MySQL中的SQL解析器 包括JDBC.ODBC.ADO等等关系数据库客户端应用开发框架在内的各种SDK,核心功能是帮助程序员简化各种客户端的数据库操作,同时将SQL语句通过网络形式发送给MySQL等关系数据库的服务器进程.MySQL服务器进行负责解析并执行这些SQL语句.SQL语句中的语法规则多种多样,MySQL服务器是如何实

redis应用之安装配置介绍

一.redis介绍: 1.redis定义: Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发工作由VMware主持.redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set --有序集合)和hash(哈希类型).这些数据类型都

企业级gitlab仓库环境搭建

目录: 1.gitlab简介 2.安装配置gitlab 2.1.实验环境介绍 2.2.更改仓库存储位置 2.3.开启https访问 2.4.启用SMTP服务 3.GitLab的基本使用 4.备份及恢复 5.总结 1.gitlab简介 GitLab 是一个用于仓库管理系统的开源项目.使用Git作为代码管理工具,并在此基础上搭建起来的web服务.GitLab拥有强大的功能,可实现git仓库管理,代码审查,问题跟踪,WIkI等功能,而且配合GitLab CI能更简单的实现持续集成和自动部署.GitLa

A quest for the full InnoDB status

When running InnoDB you are able to dig into the engine internals, look at various gauges and counters, see past deadlocks and the list of all open transactions. This is in your reach with one simple command - SHOW ENGINE INNODB STATUS . On most occa

openssl使用多种方法签名、自签名

1.自建CA 自建CA的机制:1.生成私钥2.创建证书请求,在创建证书请求过程中由于需要提供公钥,而公钥来源于私钥,所以也需要指定私钥来创建证书请求,而实际上这里提供私钥的作用就是提取其中的公钥,这一点在后文给出了证明3.使用私钥对证书请求签名. 由于测试环境,所以自建的CA只能是根CA.配置文件如下.本文将使用该配置文件/ssl/ssl.conf进行CA相关命令的演示. [default] name = root-ca /* 变量*/ default_ca = CA_default name_

centos 6.5 httpd 自建CA 认证 实现 https 服务

httpd 自建CA 认证 实现 https 服务 需要的软件: httpd mod_ssl openssl [[email protected] CA]# httpd -v #httpd版本 Server version: Apache/2.2.15 (Unix) Server built:   Jul 23 2014 14:15:00 [[email protected] CA]# uname -r #内核版本 2.6.32-431.el6.i686 [[email protected] C