OpenSSL身份认证 RSA、ECC、SM2

一、生成证书

openSSL生成RSA证书

1 生成自签CA

  • 生成CA密钥
genrsa -aes256 -passout pass:123456 -out ca_rsa_private.pem 2048
  • 自签名证书
req -new -key server_rsa_private.pem -passin pass:server -out server.csr

2 生成服务端证书

  • 生成服务端密钥
genrsa -aes256 -passout pass:server -out server_rsa_private.pem 2048
  • 生成服务端代签名证书
req -new -key server_rsa_private.pem -passin pass:server -out server.csr
  • 使用CA证书及密钥对服务器证书进行签名
x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial -out server.crt

3 生成客户端证书

  • 生成客户端密钥
genrsa -aes256 -passout pass:client -out client_rsa_private.pem 2048
  • 生成客户端代签名证书
req -new -key client_rsa_private.pem -passin pass:client -out client.csr
  • 使用CA证书及密钥对客户端证书进行签名
x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial -out client.crt

openSSL生成SM2证书

1 生成自签CA

  • 生成CA密钥
ecparam -genkey -name SM2 -out ca.key
  • 自签名证书
req -new -x509 -days 3650 -key ca.key -out ca.crt

这里ecdsa with sha256可能需要换成sm3,不过在RFC 5349中规定为ecdsa SHA做digest,所以需要做二次开发,这次暂时用这个.
2 生成服务端证书

  • 生成服务端密钥
ecparam -genkey -name SM2 -out server_sm2_private.pem
  • 生成服务端代签名证书
req -new -key server_sm2_private.pem -out server.csr
  • 使用CA证书及密钥对服务器证书进行签名
x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

3 生成客户端证书

  • 生成客户端密钥
ecparam -genkey -name SM2 -out client_sm2_private.pem
  • 生成客户端代签名证书
req -new -key client_sm2_private.pem -out client.csr
  • 使用CA证书及密钥对客户端证书进行签名
x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt

生成ECC证书

和SM2大同小异,SM2也是ECC改造的国密算法。

1 生成自签CA

  • 生成CA密钥
ecparam -genkey -name prime256v1 -out ca.key
  • 自签名证书
req -new -x509 -days 3650 -key ca.key -out ca.crt

2 生成服务端证书

  • 生成服务端密钥
ecparam -genkey -name prime256v1 -out server_ecc_private.pem
  • 生成服务端代签名证书
req -new -key server_ecc_private.pem -out server.csr
  • 使用CA证书及密钥对服务器证书进行签名
x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

3 生成客户端证书

  • 生成客户端密钥
ecparam -genkey -name  prime256v1 -out client_ecc_private.pem
  • 生成客户端代签名证书
req -new -key client_ecc_private.pem -out client.csr
  • 使用CA证书及密钥对客户端证书进行签名
x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt

证书项

C-----国家(Country Name)
ST----省份(State or Province Name)
L----城市(Locality Name)
O----公司(Organization Name)
OU----部门(Organizational Unit Name)
CN----产品名(Common Name)
emailAddress----邮箱(Email Address)

二、身份认证

Server代码:

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <winsock2.h>
#include<ws2tcpip.h>
#include <tchar.h>
#include <io.h>  
#include <process.h>  
#include <windows.h>
#include <openssl/ssl.h>
#include <openssl/ssl2.h>
#include <openssl/ssl3.h>
#include <openssl/err.h>
#pragma warning(disable:4996)
#define MAXBUF 1024
char caCertFilePath[MAX_PATH]={0};      //ca证书路径
char serverCertFilePath[MAX_PATH]={0};  //服务端证书路径
char serverPrivateFilePath[MAX_PATH]={0};	//服务端私钥路径

void ShowCerts(SSL * ssl)
{
	X509 *cert;
	char *line;

	cert = SSL_get_peer_certificate(ssl);
	// SSL_get_verify_result()是重点,SSL_CTX_set_verify()只是配置启不启用并没有执行认证,调用该函数才会真证进行证书认证
	// 如果验证不通过,那么程序抛出异常中止连接
	if(SSL_get_verify_result(ssl) == X509_V_OK){
		printf("证书验证通过\n");
	}
	if (cert != NULL) {
		printf("数字证书信息:\n");
		line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
		printf("证书: %s\n", line);
		//free(line);
		line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
		printf("颁发者: %s\n", line);
		//free(line);
		X509_free(cert);
	} else
		printf("无证书信息!\n");
}
void Two_Auth()
{
	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 = 1;

	/* 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);
		system("pause");
		exit(1);
	}

	// 双向验证
	// SSL_VERIFY_PEER---要求对证书进行认证,没有证书也会放行
	// SSL_VERIFY_FAIL_IF_NO_PEER_CERT---要求客户端需要提供证书,但验证发现单独使用没有证书也会放行
	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
	// 设置信任根证书
	if (SSL_CTX_load_verify_locations(ctx, caCertFilePath,NULL)<=0){
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}

	/* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
	/*FILE *caf=fopen("../file/server.crt","r");
	char bufStr[5000]={0};
	fread(bufStr,1,5000,caf);
	fclose(caf);*/
	if (SSL_CTX_use_certificate_file(ctx, serverCertFilePath, SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	/* 载入用户私钥 */
	/*memset(bufStr,0,5000);
	FILE *prif=fopen("../file/server_rsa_private.pem.unsecure","r");
	fread(bufStr,1,5000,prif);
	fclose(prif);*/
	if (SSL_CTX_use_PrivateKey_file(ctx,serverPrivateFilePath, SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		exit(1);
	}
	/* 检查用户私钥是否正确 */
	if (!SSL_CTX_check_private_key(ctx)) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		system("pause");
		exit(1);
	}
	/* 开启一个 socket 监听 */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
		perror("socket");
		system("pause");
		exit(1);
	} else
		printf("socket created\n");
	memset(&my_addr,0,sizeof(my_addr));
	//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");
			system("pause");
			exit(1);
	} else
		printf("binded\n");

	if (listen(sockfd, lisnum) == -1) {
		perror("listen");
		system("pause");
		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");
				system("pause");
				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);
			system("pause");
			break;
		}
		ShowCerts(ssl);

		/* 开始处理每个新连接上的数据收发 */
		//bzero(buf, MAXBUF + 1);
		memset(buf,0,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);

		memset(buf,0, 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));
		/* 处理每个新连接上的数据收发结束 */
finish:
		/* 关闭 SSL 连接 */
		SSL_shutdown(ssl);
		/* 释放 SSL */
		SSL_free(ssl);
		/* 关闭 socket */
		//close(new_fd);
		WSACleanup();
	}
	/* 关闭监听的 socket */
	close(sockfd);
	/* 释放 CTX */
	SSL_CTX_free(ctx);
	WSACleanup();
}
void One_Auth()
{
	int sockfd=0, new_fd=0;
	socklen_t len=0;
	struct sockaddr_in my_addr, their_addr;
	unsigned int myport, lisnum;
	char buf[MAXBUF + 1]={0};
	SSL_CTX *ctx;

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

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

	/* 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);
		system("pause");
		exit(1);
	}

	// 单向验证
	// SSL_VERIFY_PEER---要求对证书进行认证,没有证书也会放行
	// SSL_VERIFY_FAIL_IF_NO_PEER_CERT---要求客户端需要提供证书,但验证发现单独使用没有证书也会放行
	//SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
	// 设置信任根证书
	/*if (SSL_CTX_load_verify_locations(ctx, "../file/ca.crt",NULL)<=0){
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}*/

	/* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
	/*FILE *caf=fopen("../file/server.crt","r");
	char bufStr[5000]={0};
	fread(bufStr,1,5000,caf);
	fclose(caf);*/
	if (SSL_CTX_use_certificate_file(ctx, serverCertFilePath, SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	/* 载入用户私钥 */
	/*memset(bufStr,0,5000);
	FILE *prif=fopen("../file/server_rsa_private.pem.unsecure","r");
	fread(bufStr,1,5000,prif);
	fclose(prif);*/
	if (SSL_CTX_use_PrivateKey_file(ctx,serverPrivateFilePath , SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		exit(1);
	}
	/* 检查用户私钥是否正确 */
	if (!SSL_CTX_check_private_key(ctx)) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		system("pause");
		exit(1);
	}
	/* 开启一个 socket 监听 */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
		perror("socket");
		system("pause");
		exit(1);
	} else
		printf("socket created\n");
	memset(&my_addr,0,sizeof(my_addr));
	//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");
			system("pause");
			exit(1);
	} else
		printf("binded\n");

	if (listen(sockfd, lisnum) == -1) {
		perror("listen");
		system("pause");
		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");
				system("pause");
				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);
			system("pause");
			break;
		}
		ShowCerts(ssl);

		/* 开始处理每个新连接上的数据收发 */
		//bzero(buf, MAXBUF + 1);
		memset(buf,0,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);

		memset(buf,0, 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));
		/* 处理每个新连接上的数据收发结束 */
finish:
		/* 关闭 SSL 连接 */
		SSL_shutdown(ssl);
		/* 释放 SSL */
		SSL_free(ssl);
		/* 关闭 socket */
		//close(new_fd);
		WSACleanup();
	}
	/* 关闭监听的 socket */
	close(sockfd);
	/* 释放 CTX */
	SSL_CTX_free(ctx);
	WSACleanup();
}
int _tmain(int argc, _TCHAR* argv[]) {
	
	int alogType=-1;
	printf("服务端---请选择算法:\n");
	printf("1:RSA   2:SM2\n");
	scanf("%d",&alogType);
	if (alogType==1)
	{
		char *rsaCaCertFile="../file/ca.crt";
		char *rsaServerCertFile="../file/server.crt";
		char *rsaServerPrivateFile="../file/server_rsa_private.pem.unsecure";
		strcpy(caCertFilePath,rsaCaCertFile);
		strcpy(serverCertFilePath,rsaServerCertFile);
		strcpy(serverPrivateFilePath,rsaServerPrivateFile);
	}
	else if (alogType==2)
	{
		char *sm2CaCertFile="../SM2_Cert/ca.crt";
		char *sm2ServerCertFile="../SM2_Cert/server.crt";
		char *sm2ServerPrivateFile="../SM2_Cert/server_sm2_private.pem";
		strcpy(caCertFilePath,sm2CaCertFile);
		strcpy(serverCertFilePath,sm2ServerCertFile);
		strcpy(serverPrivateFilePath,sm2ServerPrivateFile);
	}
	
		 
	int type=-1;
	printf("服务端----请选择认证方式:\n");
	printf("1:单向认证   2:双向认证\n");
	scanf("%d",&type);
	switch (type)
	{
	case 1: One_Auth();break;
	case 2: Two_Auth();break;
	default:
		break;
	}
	system("pause");
	return 0;
}

Client代码:

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <winsock2.h>
#include<ws2tcpip.h>
#include <winsock.h>
#include <io.h>  
#include <process.h>  
#include <windows.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#pragma warning(disable:4996)
#define MAXBUF 1024
char caCertFilePath[MAX_PATH]={0};      //ca证书路径
char clientCertFilePath[MAX_PATH]={0};  //服务端证书路径
char clientPrivateFilePath[MAX_PATH]={0};	//服务端私钥路径

void ShowCerts(SSL * ssl)
{
	X509 *cert;
	char *line;

	cert = SSL_get_peer_certificate(ssl);
	// SSL_get_verify_result()是重点,SSL_CTX_set_verify()只是配置启不启用并没有执行认证,调用该函数才会真证进行证书认证
	// 如果验证不通过,那么程序抛出异常中止连接
	if(SSL_get_verify_result(ssl) == X509_V_OK){
		printf("证书验证通过\n");
	}
	if (cert != NULL) {
		printf("数字证书信息:\n");
		line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
		printf("证书: %s\n", line);
		//free(line);
		line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
		printf("颁发者: %s\n", line);
		//free(line);
		X509_free(cert);
	} else
		printf("无证书信息!\n");
}
void Auth_Two()
{
	int sockfd, len;
	struct sockaddr_in dest;
	char buffer[MAXBUF + 1];
	SSL_CTX *ctx;
	SSL *ssl;
	unsigned int myport;
	char *myip="127.0.0.1";
	myport=7838;
	
	/*if (argc != 5) {
		printf("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个"
			"IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
			argv[0], argv[0]);
		system("pause");
		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);
		system("pause");
		exit(1);
	}

	// 双向验证
	// SSL_VERIFY_PEER---要求对证书进行认证,没有证书也会放行
	// SSL_VERIFY_FAIL_IF_NO_PEER_CERT---要求客户端需要提供证书,但验证发现单独使用没有证书也会放行
	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
	// 设置信任根证书
	if (SSL_CTX_load_verify_locations(ctx, caCertFilePath,NULL)<=0){
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}

	/* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
/*	FILE *clientf=fopen("../file/client.crt","r");
	char bufStr[5000]={0};
	fread(bufStr,1,5000,clientf);
	fclose(clientf);*/
	if (SSL_CTX_use_certificate_file(ctx, clientCertFilePath, SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	/* 载入用户私钥 */
	/*FILE *clientRsaf=fopen("../file/client_rsa_private.pem.unsecure","r");
	memset(bufStr,0,5000);
	fread(bufStr,1,5000,clientRsaf);
	fclose(clientRsaf);*/
	if (SSL_CTX_use_PrivateKey_file(ctx, clientPrivateFilePath, SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	/* 检查用户私钥是否正确 */
	if (!SSL_CTX_check_private_key(ctx)) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		system("pause");
		exit(1);
	}
	/* 创建一个 socket 用于 tcp 通信 */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Socket");
		system("pause");
		exit(errno);
	}
	printf("socket created\n");

	/* 初始化服务器端(对方)的地址和端口信息 */
	//bzero(&dest, sizeof(dest));
	memset(&dest,0,sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = htons(myport);
	
	unsigned long l1=0;
	l1=inet_addr(myip);
	in_addr addr1;
	memcpy(&addr1, &l1, 4);
	if (inet_ntoa(addr1) == 0) {
		perror(myip);
		system("pause");
		exit(errno);
	}
	dest.sin_addr=addr1;
	printf("address created\n");

	/* 连接服务器 */
	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
		perror("Connect ");
		system("pause");
		exit(errno);
	}
	printf("server connected\n");

	/* 基于 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\n", SSL_get_cipher(ssl));
		ShowCerts(ssl);
	}

	/* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
	memset(buffer,0, 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;
	}
	memset(buffer,0, 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);
	WSACleanup();
}
void Auth_One()
{
	int sockfd, len;
	struct sockaddr_in dest;
	char buffer[MAXBUF + 1];
	SSL_CTX *ctx;
	SSL *ssl;
	unsigned int myport;
	char *myip="127.0.0.1";
	myport=7838;
	
	/*if (argc != 5) {
		printf("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个"
			"IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
			argv[0], argv[0]);
		system("pause");
		exit(0);
	}*/

	/* SSL 库初始化,参看 ssl-server.c 代码 */
	SSL_library_init();
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();
	ctx = SSL_CTX_new(SSLv23_method());
	if (ctx == NULL) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}

	// 单向验证
	/*// SSL_VERIFY_PEER---要求对证书进行认证,没有证书也会放行
	// SSL_VERIFY_FAIL_IF_NO_PEER_CERT---要求客户端需要提供证书,但验证发现单独使用没有证书也会放行
	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
	// 设置信任根证书
	if (SSL_CTX_load_verify_locations(ctx,rsaCaCertFile,NULL)<=0){
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}

	/* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 * /
	if (SSL_CTX_use_certificate_file(ctx, rsaServerCertFile, SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	/* 载入用户私钥 * /
	if (SSL_CTX_use_PrivateKey_file(ctx, clientPrivateFilePath, SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}
	/* 检查用户私钥是否正确 * /
	if (!SSL_CTX_check_private_key(ctx)) {
		ERR_print_errors_fp(stdout);
		system("pause");
		exit(1);
	}*/
	WSADATA wsd;
	int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);
	if(0 != resStartup)
	{
		printf("failed to WSAStartup!\n");
		system("pause");
		exit(1);
	}
	/* 创建一个 socket 用于 tcp 通信 */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Socket");
		system("pause");
		exit(errno);
	}
	printf("socket created\n");

	/* 初始化服务器端(对方)的地址和端口信息 */
	//bzero(&dest, sizeof(dest));
	memset(&dest,0,sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = htons(myport);
	
	unsigned long l1=0;
	l1=inet_addr(myip);
	in_addr addr1;
	memcpy(&addr1, &l1, 4);
	if (inet_ntoa(addr1) == 0) {
		perror(myip);
		system("pause");
		exit(errno);
	}
	dest.sin_addr=addr1;
	printf("address created\n");

	/* 连接服务器 */
	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
		perror("Connect ");
		system("pause");
		exit(errno);
	}
	printf("server connected\n");

	/* 基于 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\n", SSL_get_cipher(ssl));
		ShowCerts(ssl);
	}

	/* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
	memset(buffer,0, 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;
	}
	memset(buffer,0, 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);
	WSACleanup();
}
int  _tmain(int argc, _TCHAR* argv[])
{
	int alogType=-1;
	printf("客户端---请选择算法:\n");
	printf("1:RSA   2:SM2\n");
	scanf("%d",&alogType);
	if (alogType==1)
	{
		char *rsaCaCertFile="../file/ca.crt";
		char *rsaClientCertFile="../file/client.crt";
		char *rsaClientPrivateFile="../file/client_rsa_private.pem.unsecure";
		strcpy(caCertFilePath,rsaCaCertFile);
		strcpy(clientCertFilePath,rsaClientCertFile);
		strcpy(clientPrivateFilePath,rsaClientPrivateFile);
	}
	else if (alogType==2)
	{
		char *sm2CaCertFile="../SM2_Cert/ca.crt";
		char *sm2ClientCertFile="../SM2_Cert/client.crt";
		char *sm2ClientPrivateFile="../SM2_Cert/client_sm2_private.pem";
		strcpy(caCertFilePath,sm2CaCertFile);
		strcpy(clientCertFilePath,sm2ClientCertFile);
		strcpy(clientPrivateFilePath,sm2ClientPrivateFile);
	}
	int type=-1;
	printf("客户端----请选择认证方式:\n");
	printf("1:单向认证   2:双向认证\n");
	scanf("%d",&type);
	switch (type)
	{
	case 1: Auth_One();break;
	case 2: Auth_Two();break;
	default:
		break;
	}
	system("pause");
	return 0;
}

以上代码RSA和ECC都测试通过了,但是SM2测试时候报错了。

45444:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl\record\rec_layer_s3.c:1528:SSL alert number 40
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 311 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
error in s_client

以上是用命令行进行身份认证时报的错误信息,用上述的代码测试同样会报此类错误信息,在握手的时候直接崩溃

45444:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl\record\rec_layer_s3.c:1528:SSL alert number 40

应该是openssl1.1.1和1.1.1a都是这样的错误,通过抓包可以看到客户端和服务端在握手的时候使用TLS版本不一致在这里插入图片描述
一个是TLS1.2一个是TLS1.3,个人感觉是因为不能识别证书,导致降低了TLS版本,从而不能握手成功,不知道这个是不是openssl1.1.1没有完善的地方,后续如果解决了会及时更新。

提供一个完善的代码和文档下载链接,方便大家研究openssl身份认证
在这里插入图片描述
下载链接:https://download.csdn.net/download/xuebing1995/10947453

欢迎大佬讨论指点。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

离水的鱼儿

一分也是爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值