一、生成证书
openSSL生成RSA证书
1 生成自签CA

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

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

生成客户端密钥
genrsa -aes256 -passout pass:client -out client_rsa_private.pem 2048
1
生成客户端代签名证书
req -new -key client_rsa_private.pem -passin pass:client -out client.csr
1
使用CA证书及密钥对客户端证书进行签名
x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca_rsa_private.pem -passin pass:123456 -CAcreateserial -out client.crt
1
openSSL生成SM2证书
1 生成自签CA
生成CA密钥
ecparam -genkey -name SM2 -out ca.key
1
自签名证书
req -new -x509 -days 3650 -key ca.key -out ca.crt
1
这里ecdsa with sha256可能需要换成sm3,不过在RFC 5349中规定为ecdsa SHA做digest,所以需要做二次开发,这次暂时用这个.
2 生成服务端证书

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

生成客户端密钥
ecparam -genkey -name SM2 -out client_sm2_private.pem
1
生成客户端代签名证书
req -new -key client_sm2_private.pem -out client.csr
1
使用CA证书及密钥对客户端证书进行签名
x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt
1
生成ECC证书
和SM2大同小异,SM2也是ECC改造的国密算法。

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

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

生成客户端密钥
ecparam -genkey -name prime256v1 -out client_ecc_private.pem
1
生成客户端代签名证书
req -new -key client_ecc_private.pem -out client.csr
1
使用CA证书及密钥对客户端证书进行签名
x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt
1
证书项
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);
}
elseprintf("无证书信息!\n");
}
voidTwo_Auth()
{
intsockfd, new_fd;
socklen_t len;
structsockaddr_in my_addr, their_addr;
unsigned
intmyport, 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);
}
elseprintf("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(structsockaddr))== -1) {
perror(
"bind");
system(
"pause");
exit(
1);
}
elseprintf("binded\n");if (listen(sockfd, lisnum) == -1) {
perror(
"listen");
system(
"pause");
exit(
1);
}
elseprintf("begin listen\n");while (1) {
SSL
*ssl;
len
= sizeof(structsockaddr);/*等待客户端连上来*/ if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len))== -1) {
perror(
"accept");
system(
"pause");
exit(errno);
}
elseprintf("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));
gotofinish;
}
elseprintf("消息'%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);elseprintf("消息接收失败!错误代码是%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();
}
voidOne_Auth()
{
int sockfd=0, new_fd=0;
socklen_t len
=0;structsockaddr_in my_addr, their_addr;
unsigned
intmyport, 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);
}
elseprintf("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(structsockaddr))== -1) {
perror(
"bind");
system(
"pause");
exit(
1);
}
elseprintf("binded\n");if (listen(sockfd, lisnum) == -1) {
perror(
"listen");
system(
"pause");
exit(
1);
}
elseprintf("begin listen\n");while (1) {
SSL
*ssl;
len
= sizeof(structsockaddr);/*等待客户端连上来*/ if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len))== -1) {
perror(
"accept");
system(
"pause");
exit(errno);
}
elseprintf("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));
gotofinish;
}
elseprintf("消息'%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);elseprintf("消息接收失败!错误代码是%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);
}
elseprintf("无证书信息!\n");
}
voidAuth_Two()
{
intsockfd, len;structsockaddr_in dest;char buffer[MAXBUF + 1];
SSL_CTX
*ctx;
SSL
*ssl;
unsigned
intmyport;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));
gotofinish;
}
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));
elseprintf("消息'%s'发送成功,共发送了%d个字节!\n",
buffer, len);

finish:
/*关闭连接*/SSL_shutdown(ssl);
SSL_free(ssl);
//close(sockfd); SSL_CTX_free(ctx);
WSACleanup();
}
voidAuth_One()
{
intsockfd, len;structsockaddr_in dest;char buffer[MAXBUF + 1];
SSL_CTX
*ctx;
SSL
*ssl;
unsigned
intmyport;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));
gotofinish;
}
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));
elseprintf("消息'%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

---------------------
原文链接:https://blog.csdn.net/xuebing1995/article/details/86742078

 

标签: none

添加新评论