zoukankan      html  css  js  c++  java
  • [转载]OpenSSL身份认证 RSA、ECC、SM2

    一、生成证书
    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("证书验证通过
    ");
        }
        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("无证书信息!
    ");
    }
    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!
    ");
            system("pause");
            exit(1);
        }
        /* 开启一个 socket 监听 */
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
            perror("socket");
            system("pause");
            exit(1);
        } else
            printf("socket created
    ");
        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
    ");
    
        if (listen(sockfd, lisnum) == -1) {
            perror("listen");
            system("pause");
            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");
                    system("pause");
                    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);
                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'
    ", buf, errno,
                    strerror(errno));
                goto finish;
            } else
                printf("消息'%s'发送成功,共发送了%d个字节!
    ", buf, len);
    
            memset(buf,0, 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);
            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!
    ");
            system("pause");
            exit(1);
        }
        /* 开启一个 socket 监听 */
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
            perror("socket");
            system("pause");
            exit(1);
        } else
            printf("socket created
    ");
        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
    ");
    
        if (listen(sockfd, lisnum) == -1) {
            perror("listen");
            system("pause");
            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");
                    system("pause");
                    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);
                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'
    ", buf, errno,
                    strerror(errno));
                goto finish;
            } else
                printf("消息'%s'发送成功,共发送了%d个字节!
    ", buf, len);
    
            memset(buf,0, 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);
            WSACleanup();
        }
        /* 关闭监听的 socket */
        close(sockfd);
        /* 释放 CTX */
        SSL_CTX_free(ctx);
        WSACleanup();
    }
    int _tmain(int argc, _TCHAR* argv[]) {
        
        int alogType=-1;
        printf("服务端---请选择算法:
    ");
        printf("1:RSA   2:SM2
    ");
        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("服务端----请选择认证方式:
    ");
        printf("1:单向认证   2:双向认证
    ");
        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("证书验证通过
    ");
        }
        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("无证书信息!
    ");
    }
    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("参数格式错误!正确用法如下:
    		%s IP地址 端口
    	比如:	%s 127.0.0.1 80
    此程序用来从某个"
                "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!
    ");
            system("pause");
            exit(1);
        }
        /* 创建一个 socket 用于 tcp 通信 */
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("Socket");
            system("pause");
            exit(errno);
        }
        printf("socket created
    ");
    
        /* 初始化服务器端(对方)的地址和端口信息 */
        //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
    ");
    
        /* 连接服务器 */
        if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
            perror("Connect ");
            system("pause");
            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 个字节 */
        memset(buffer,0, 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;
        }
        memset(buffer,0, 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);
        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("参数格式错误!正确用法如下:
    		%s IP地址 端口
    	比如:	%s 127.0.0.1 80
    此程序用来从某个"
                "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!
    ");
            system("pause");
            exit(1);
        }
        /* 创建一个 socket 用于 tcp 通信 */
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("Socket");
            system("pause");
            exit(errno);
        }
        printf("socket created
    ");
    
        /* 初始化服务器端(对方)的地址和端口信息 */
        //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
    ");
    
        /* 连接服务器 */
        if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
            perror("Connect ");
            system("pause");
            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 个字节 */
        memset(buffer,0, 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;
        }
        memset(buffer,0, 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);
        WSACleanup();
    }
    int  _tmain(int argc, _TCHAR* argv[])
    {
        int alogType=-1;
        printf("客户端---请选择算法:
    ");
        printf("1:RSA   2:SM2
    ");
        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("客户端----请选择认证方式:
    ");
        printf("1:单向认证   2:双向认证
    ");
        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
    ecord
    ec_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
    ecord
    ec_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

  • 相关阅读:
    css中滚动条设置
    css 字间距
    使用easyDialog弹出层后会刷新页面
    js页面跳转整理
    父容器不根据内容自适应高度的解决方法
    Freemaker 基本语法
    BeanUtils工具包下载及应用
    Java关键字final、static使用总结(二)
    What is a cross-channel pooling in convolutional neural net works?
    仿射梯度
  • 原文地址:https://www.cnblogs.com/eaglexmw/p/11346492.html
Copyright © 2011-2022 走看看