使用Clion和gmssl动态库实现服务器server和客户端client之间的SSL通信,测试指定密码套件

参考链接

注意事项

GM/T 标准涵盖 2 个协议:

 - SSL VPN 协议 (GM/T 0024-2014)
 - IPSec VPN 协议 (GM/T 0022-2014)

GM/T 0024-2014 SSL VPN 协议与 IETF TLS 在以下方面有所不同:

 - 当前版本的 TLS 是 1.3 (0x0304),而 GM/T SSL 版本是 1.1 (0x0102)。
 - GM/T SSL 的握手协议与 TLS 握手不同。
 - 为 VPN 应用设计的 GM/T SSL 中有一个可选的不同记录协议。
 - GM/T SSL 有 12 个密码套件,其中一些密码不提供前向保密。

GM/T 0024-2014 密码套件:

```
 1. {0xe0,0x01} GMTLS_SM2DHE_SM2SIGN_WITH_SM1_SM3
 2. {0xe0,0x03} GMTLS_SM2ENC_WITH_SM1_SM3
 3. {0xe0,0x05} GMTLS_SM9DHE_SM9SIGN_WITH_SM1_SM3
 4. {0xe0,0x07} GMTLS_SM9ENC_WITH_SM1_SM3
 5. {0xe0,0x09} GMTLS_RSA_WITH_SM1_SM3
 6. {0xe0,0x0a} GMTLS_RSA_WITH_SM1_SHA1
 7. {0xe0,0x11} GMTLS_SM2DHE_SM2SIGN_WITH_SMS4_SM3
 8. {0xe0,0x13} GMTLS_SM2ENC_WITH_SMS4_SM3
 9. {0xe0,0x15} GMTLS_SM9DHE_SM9SIGN_WITH_SMS4_SM3
10. {0xe0,0x17} GMTLS_SM9ENC_WITH_SMS4_SM3
11. {0xe0,0x19} GMTLS_RSA_WITH_SMS4_SM3
12. {0xe0,0x1a} GMTLS_RSA_WITH_SMS4_SHA1
  •  虽然gmssl的源码文件夹里面的 README.md 明确指出,其支持国密算法12种算法套件
  • 但是,使用命令 gmssl ciphers -V 列举其支持的算法TLS1.x密码套件,却根本不涵盖上述7和8
chy-cpabe@ubuntu:~/ssl_server_client/ca$ gmssl ciphers -V
          0xC0,0x2C - ECDHE-ECDSA-AES256-GCM-SHA384  TLSv1.2    Kx=ECDH     Au=ECDSA  Enc=AESGCM(256)             Mac=AEAD
          0xC0,0x30 - ECDHE-RSA-AES256-GCM-SHA384    TLSv1.2    Kx=ECDH     Au=RSA    Enc=AESGCM(256)             Mac=AEAD
          0x00,0x9F - DHE-RSA-AES256-GCM-SHA384      TLSv1.2    Kx=DH       Au=RSA    Enc=AESGCM(256)             Mac=AEAD
          0xCC,0xA9 - ECDHE-ECDSA-CHACHA20-POLY1305  TLSv1.2    Kx=ECDH     Au=ECDSA  Enc=CHACHA20/POLY1305(256)  Mac=AEAD
          0xCC,0xA8 - ECDHE-RSA-CHACHA20-POLY1305    TLSv1.2    Kx=ECDH     Au=RSA    Enc=CHACHA20/POLY1305(256)  Mac=AEAD
          0xCC,0xAA - DHE-RSA-CHACHA20-POLY1305      TLSv1.2    Kx=DH       Au=RSA    Enc=CHACHA20/POLY1305(256)  Mac=AEAD
          0xC0,0x2B - ECDHE-ECDSA-AES128-GCM-SHA256  TLSv1.2    Kx=ECDH     Au=ECDSA  Enc=AESGCM(128)             Mac=AEAD
          0xC0,0x2F - ECDHE-RSA-AES128-GCM-SHA256    TLSv1.2    Kx=ECDH     Au=RSA    Enc=AESGCM(128)             Mac=AEAD
          0x00,0x9E - DHE-RSA-AES128-GCM-SHA256      TLSv1.2    Kx=DH       Au=RSA    Enc=AESGCM(128)             Mac=AEAD
          0xE1,0x07 - ECDHE-SM2-WITH-SMS4-GCM-SM3    TLSv1.2    Kx=ECDH     Au=SM2    Enc=SMS4GCM(128)            Mac=AEAD
          0xC0,0x24 - ECDHE-ECDSA-AES256-SHA384      TLSv1.2    Kx=ECDH     Au=ECDSA  Enc=AES(256)                Mac=SHA384
          0xC0,0x28 - ECDHE-RSA-AES256-SHA384        TLSv1.2    Kx=ECDH     Au=RSA    Enc=AES(256)                Mac=SHA384
          0x00,0x6B - DHE-RSA-AES256-SHA256          TLSv1.2    Kx=DH       Au=RSA    Enc=AES(256)                Mac=SHA256
          0xC0,0x23 - ECDHE-ECDSA-AES128-SHA256      TLSv1.2    Kx=ECDH     Au=ECDSA  Enc=AES(128)                Mac=SHA256
          0xC0,0x27 - ECDHE-RSA-AES128-SHA256        TLSv1.2    Kx=ECDH     Au=RSA    Enc=AES(128)                Mac=SHA256
          0x00,0x67 - DHE-RSA-AES128-SHA256          TLSv1.2    Kx=DH       Au=RSA    Enc=AES(128)                Mac=SHA256
          0xE1,0x02 - ECDHE-SM2-WITH-SMS4-SM3        TLSv1.2    Kx=ECDH     Au=SM2    Enc=SMS4(128)               Mac=SM3 
          0xC0,0x0A - ECDHE-ECDSA-AES256-SHA         SSLv3      Kx=ECDH     Au=ECDSA  Enc=AES(256)                Mac=SHA1
          0xC0,0x14 - ECDHE-RSA-AES256-SHA           SSLv3      Kx=ECDH     Au=RSA    Enc=AES(256)                Mac=SHA1
          0x00,0x39 - DHE-RSA-AES256-SHA             SSLv3      Kx=DH       Au=RSA    Enc=AES(256)                Mac=SHA1
          0xC0,0x09 - ECDHE-ECDSA-AES128-SHA         SSLv3      Kx=ECDH     Au=ECDSA  Enc=AES(128)                Mac=SHA1
          0xC0,0x13 - ECDHE-RSA-AES128-SHA           SSLv3      Kx=ECDH     Au=RSA    Enc=AES(128)                Mac=SHA1
          0x00,0x33 - DHE-RSA-AES128-SHA             SSLv3      Kx=DH       Au=RSA    Enc=AES(128)                Mac=SHA1
          0x00,0xAD - RSA-PSK-AES256-GCM-SHA384      TLSv1.2    Kx=RSAPSK   Au=RSA    Enc=AESGCM(256)             Mac=AEAD
          0x00,0xAB - DHE-PSK-AES256-GCM-SHA384      TLSv1.2    Kx=DHEPSK   Au=PSK    Enc=AESGCM(256)             Mac=AEAD
          0xCC,0xAE - RSA-PSK-CHACHA20-POLY1305      TLSv1.2    Kx=RSAPSK   Au=RSA    Enc=CHACHA20/POLY1305(256)  Mac=AEAD
          0xCC,0xAD - DHE-PSK-CHACHA20-POLY1305      TLSv1.2    Kx=DHEPSK   Au=PSK    Enc=CHACHA20/POLY1305(256)  Mac=AEAD
          0xCC,0xAC - ECDHE-PSK-CHACHA20-POLY1305    TLSv1.2    Kx=ECDHEPSK Au=PSK    Enc=CHACHA20/POLY1305(256)  Mac=AEAD
          0x00,0x9D - AES256-GCM-SHA384              TLSv1.2    Kx=RSA      Au=RSA    Enc=AESGCM(256)             Mac=AEAD
          0x00,0xA9 - PSK-AES256-GCM-SHA384          TLSv1.2    Kx=PSK      Au=PSK    Enc=AESGCM(256)             Mac=AEAD
          0xCC,0xAB - PSK-CHACHA20-POLY1305          TLSv1.2    Kx=PSK      Au=PSK    Enc=CHACHA20/POLY1305(256)  Mac=AEAD
          0x00,0xAC - RSA-PSK-AES128-GCM-SHA256      TLSv1.2    Kx=RSAPSK   Au=RSA    Enc=AESGCM(128)             Mac=AEAD
          0x00,0xAA - DHE-PSK-AES128-GCM-SHA256      TLSv1.2    Kx=DHEPSK   Au=PSK    Enc=AESGCM(128)             Mac=AEAD
          0x00,0x9C - AES128-GCM-SHA256              TLSv1.2    Kx=RSA      Au=RSA    Enc=AESGCM(128)             Mac=AEAD
          0x00,0xA8 - PSK-AES128-GCM-SHA256          TLSv1.2    Kx=PSK      Au=PSK    Enc=AESGCM(128)             Mac=AEAD
          0x00,0x3D - AES256-SHA256                  TLSv1.2    Kx=RSA      Au=RSA    Enc=AES(256)                Mac=SHA256
          0x00,0x3C - AES128-SHA256                  TLSv1.2    Kx=RSA      Au=RSA    Enc=AES(128)                Mac=SHA256
          0xC0,0x38 - ECDHE-PSK-AES256-CBC-SHA384    TLSv1      Kx=ECDHEPSK Au=PSK    Enc=AES(256)                Mac=SHA384
          0xC0,0x36 - ECDHE-PSK-AES256-CBC-SHA       SSLv3      Kx=ECDHEPSK Au=PSK    Enc=AES(256)                Mac=SHA1
          0xC0,0x21 - SRP-RSA-AES-256-CBC-SHA        SSLv3      Kx=SRP      Au=RSA    Enc=AES(256)                Mac=SHA1
          0xC0,0x20 - SRP-AES-256-CBC-SHA            SSLv3      Kx=SRP      Au=SRP    Enc=AES(256)                Mac=SHA1
          0x00,0xB7 - RSA-PSK-AES256-CBC-SHA384      TLSv1      Kx=RSAPSK   Au=RSA    Enc=AES(256)                Mac=SHA384
          0x00,0xB3 - DHE-PSK-AES256-CBC-SHA384      TLSv1      Kx=DHEPSK   Au=PSK    Enc=AES(256)                Mac=SHA384
          0x00,0x95 - RSA-PSK-AES256-CBC-SHA         SSLv3      Kx=RSAPSK   Au=RSA    Enc=AES(256)                Mac=SHA1
          0x00,0x91 - DHE-PSK-AES256-CBC-SHA         SSLv3      Kx=DHEPSK   Au=PSK    Enc=AES(256)                Mac=SHA1
          0x00,0x35 - AES256-SHA                     SSLv3      Kx=RSA      Au=RSA    Enc=AES(256)                Mac=SHA1
          0x00,0xAF - PSK-AES256-CBC-SHA384          TLSv1      Kx=PSK      Au=PSK    Enc=AES(256)                Mac=SHA384
          0x00,0x8D - PSK-AES256-CBC-SHA             SSLv3      Kx=PSK      Au=PSK    Enc=AES(256)                Mac=SHA1
          0xC0,0x37 - ECDHE-PSK-AES128-CBC-SHA256    TLSv1      Kx=ECDHEPSK Au=PSK    Enc=AES(128)                Mac=SHA256
          0xC0,0x35 - ECDHE-PSK-AES128-CBC-SHA       SSLv3      Kx=ECDHEPSK Au=PSK    Enc=AES(128)                Mac=SHA1
          0xC0,0x1E - SRP-RSA-AES-128-CBC-SHA        SSLv3      Kx=SRP      Au=RSA    Enc=AES(128)                Mac=SHA1
          0xC0,0x1D - SRP-AES-128-CBC-SHA            SSLv3      Kx=SRP      Au=SRP    Enc=AES(128)                Mac=SHA1
          0x00,0xB6 - RSA-PSK-AES128-CBC-SHA256      TLSv1      Kx=RSAPSK   Au=RSA    Enc=AES(128)                Mac=SHA256
          0x00,0xB2 - DHE-PSK-AES128-CBC-SHA256      TLSv1      Kx=DHEPSK   Au=PSK    Enc=AES(128)                Mac=SHA256
          0x00,0x94 - RSA-PSK-AES128-CBC-SHA         SSLv3      Kx=RSAPSK   Au=RSA    Enc=AES(128)                Mac=SHA1
          0x00,0x90 - DHE-PSK-AES128-CBC-SHA         SSLv3      Kx=DHEPSK   Au=PSK    Enc=AES(128)                Mac=SHA1
          0xF1,0x20 - ECDHE-PSK-WITH-SMS4-CBC-SM3    TLSv1      Kx=ECDHEPSK Au=PSK    Enc=SMS4(128)               Mac=SM3 
          0xE0,0x17 - SM9-WITH-SMS4-SM3              GMTLSv1.1  Kx=SM9      Au=SM9    Enc=SMS4(128)               Mac=SM3 
          0xE0,0x15 - SM9DHE-WITH-SMS4-SM3           GMTLSv1.1  Kx=SM9DHE   Au=SM9    Enc=SMS4(128)               Mac=SM3 
          0xE0,0x13 - SM2-WITH-SMS4-SM3              GMTLSv1.1  Kx=SM2      Au=SM2    Enc=SMS4(128)               Mac=SM3 
          0xE0,0x11 - SM2DHE-WITH-SMS4-SM3           GMTLSv1.1  Kx=SM2DHE   Au=SM2    Enc=SMS4(128)               Mac=SM3 
          0x00,0x2F - AES128-SHA                     SSLv3      Kx=RSA      Au=RSA    Enc=AES(128)                Mac=SHA1
          0xE0,0x1A - RSA-WITH-SMS4-SHA1             GMTLSv1.1  Kx=RSA      Au=RSA    Enc=SMS4(128)               Mac=SHA1
          0xE0,0x19 - RSA-WITH-SMS4-SM3              GMTLSv1.1  Kx=RSA      Au=RSA    Enc=SMS4(128)               Mac=SM3 
          0x00,0xAE - PSK-AES128-CBC-SHA256          TLSv1      Kx=PSK      Au=PSK    Enc=AES(128)                Mac=SHA256
          0x00,0x8C - PSK-AES128-CBC-SHA             SSLv3      Kx=PSK      Au=PSK    Enc=AES(128)                Mac=SHA1
          0xF1,0x01 - PSK-WITH-SMS4-CBC-SM3          SSLv3      Kx=PSK      Au=PSK    Enc=SMS4(128)               Mac=SM3 
  • 添加代码如下
  • 原因
    • ca、服务器和客户端的证书 均为国密证书
    •         /* 申请SSL会话环境 */         if( nullptr==(ctx=SSL_CTX_new(TLSv1_2_method())) )    //使用SSL_CTX_new()创建会话环境,建立连接时要使用协议由TLS_client_method()来定,服务器由对应的TLS_server_method()来定。如果这一步出错,需要查看错误栈来查看原因;指定了TLSv1_2_method模式
        //指定密码算法套件
        // gmssl ciphers -V 明确支持
        SSL_CTX_set_cipher_list(ctx,"ECDHE-SM2-WITH-SMS4-GCM-SM3");
        // SM2-XXX
//        SSL_CTX_set_cipher_list(ctx,"GMTLS_SM2DHE_SM2SIGN_WITH_SMS4_SM3"); //{0xe0,0x11}
//        SSL_CTX_set_cipher_list(ctx,"GMTLS_SM2ENC_WITH_SMS4_SM3"); //{0xe0,0x13}
  •  ECDHE-SM2-WITH-SMS4-SM3 是默认的算法,且是gmssl自我实现的算法,并未得到官方认证
GmSSL supports the standard TLS 1.2 protocol with SM2/SM3/SM4 ciphersuites and the GM/T SSL VPN protocol and ciphersuites. Currently the following ciphersuites are supported:

```
ECDHE-SM2-WITH-SMS4-SM3
ECDHE-SM2-WITH-SMS4-SHA256
GmSSL 支持带有 SM2/SM3/SM4 密码套件的标准 TLS 1.2 协议以及 GM/T SSL VPN 协议和密码套件。 目前支持以下密码套件:

```
ECDHE-SM2-WITH-SMS4-SM3
ECDHE-SM2-WITH-SMS4-SHA256

 代码

服务端 server

#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define MAXBUF 1500

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 != nullptr) {
        printf("数字证书信息:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0);
        printf("证书: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), nullptr, 0);
        printf("颁发者: %s\n", line);
        free(line);
        X509_free(cert);
    } else
        printf("无证书信息!\n");
}

int main(int argc, char **argv) {
    int listen_fd = -1; /* TCP监听套接字 */
    int accept_fd = -1; /* 已连接TCP套接字 */
    struct sockaddr_in server_addr, client_addr;
    bzero(&server_addr, sizeof(server_addr));

    SSL_CTX *ctx = nullptr; /* SSL会话环境 */

    SSL *ssl = nullptr; /* SSL安全套接字 */
    socklen_t len;
    char buf[MAXBUF]={0};  /* 服务器接收数据buffer */


    if( 3!=argc )
    {
        printf("argcment wrong:ip port\n");
    }

    SSL_library_init(); /* SSL 库初始化 */
    SSLeay_add_ssl_algorithms();
    OpenSSL_add_all_algorithms();  /* 载入所有 SSL 算法 */
    SSL_load_error_strings(); /* 载入所有 SSL 错误消息 */
//    ERR_load_BIO_strings();


    //TCP服务器:创建、绑定、监听
    if ((listen_fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket create wrong\n");
        exit(1);
    } else
        printf("socket created\n");

    server_addr.sin_family = PF_INET;
    server_addr.sin_port = htons(atoi(argv[2]));
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);;

    if (bind(listen_fd, (struct sockaddr *) &server_addr, sizeof(struct sockaddr))
        == -1) {
        perror("bind wrong\n");
        exit(1);
    } else
        printf("binded success\n");
    int lisnum = 2;

    do{
        //使用SSL_CTX_new()创建会话环境,建立连接时要使用协议由TLS_server_method()来定。如果这一步出错,需要查看错误栈来查看原因
        if(nullptr == (ctx = SSL_CTX_new( TLSv1_2_method())))		//using sm3, TLSv1_2_method
        {
            ERR_print_errors_fp(stdout);
            break;
        }
//        SSL_CTX_set_security_level(ctx,0);

        //指定密码算法套件
        // gmssl ciphers -V 明确支持
        SSL_CTX_set_cipher_list(ctx,"ECDHE-SM2-WITH-SMS4-GCM-SM3");
        // SM2-XXX
//        SSL_CTX_set_cipher_list(ctx,"GMTLS_SM2DHE_SM2SIGN_WITH_SMS4_SM3"); //{0xe0,0x11}
//        SSL_CTX_set_cipher_list(ctx,"GMTLS_SM2ENC_WITH_SMS4_SM3"); //{0xe0,0x13}

        // 双向验证
        // 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, nullptr);

        // 设置信任根证书
        if(SSL_CTX_load_verify_locations(ctx, "/home/chy-cpabe/CLionProjects/learn_GmSSL_server/pem/CaCert.pem", nullptr) != 1)
//        if(SSL_CTX_load_verify_locations(ctx, "/home/chy-cpabe/ssl_server_client.openssl_bak/ca/ca.crt", nullptr) != 1)
        {
            printf("SSL_CTX_load_verify_locations error\n");
            ERR_print_errors_fp(stdout);
            break;
        }

        /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
        if( 0>=SSL_CTX_use_certificate_file(ctx, "/home/chy-cpabe/CLionProjects/learn_GmSSL_server/pem/HuiguanCert.pem", SSL_FILETYPE_PEM/*SSL_FILETYPE_ASN1*/) ) /* 为SSL会话加载用户证书 */
//        if( 0>=SSL_CTX_use_certificate_file(ctx, "/home/chy-cpabe/ssl_server_client.openssl_bak/server/pem/server.crt", SSL_FILETYPE_PEM/*SSL_FILETYPE_ASN1*/) ) /* 为SSL会话加载用户证书 */
        {
            ERR_print_errors_fp(stdout);
            break;
        }
        /* 载入用户私钥 */
        if( 0>=SSL_CTX_use_PrivateKey_file(ctx, "/home/chy-cpabe/CLionProjects/learn_GmSSL_server/pem/HuiguanKey.pem", SSL_FILETYPE_PEM/*SSL_FILETYPE_ASN1*/) ) /* 为SSL会话加载用户私钥 */
//        if( 0>=SSL_CTX_use_PrivateKey_file(ctx, "/home/chy-cpabe/ssl_server_client.openssl_bak/server/pem/server_rsa_private.pem.unsecure", SSL_FILETYPE_PEM/*SSL_FILETYPE_ASN1*/) ) /* 为SSL会话加载用户私钥 */
        {
            ERR_print_errors_fp(stdout);
            break;
        }
        /* 检查用户私钥是否正确 */
        if(!SSL_CTX_check_private_key(ctx))                                 										 /* 验证私钥和证书是否相符 */
        {
            ERR_print_errors_fp(stdout);
            break;
        }

        if (listen(listen_fd, lisnum) == -1) {
            perror("listen wrong\n");
            exit(1);
        } else
            printf("begin listen\n");

        len = sizeof(struct sockaddr);
        /* 等待客户端连上来 */
        if ((accept_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &len))
            == -1) {
            perror("accept wrong\n");
            exit(errno);
        } else{
            printf("server: got connection from %s, port %d, socket %d\n",
                   inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port),
                   accept_fd);
        }

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

        /* 开始处理每个新连接上的数据收发 */
        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));
        /* 处理每个新连接上的数据收发结束 */
        finish:
        /* 关闭 SSL 连接 */
        SSL_shutdown(ssl);
        /* 释放 SSL */
        SSL_free(ssl);
        ssl = nullptr;
        /* 关闭 socket */
        close(accept_fd);
        accept_fd = -1;
    }while(1);

    /* 关闭监听的 socket */
    close(listen_fd);
    listen_fd = -1;
    /* 释放 CTX */
    SSL_CTX_free(ctx);
    ctx = nullptr;
    return 0;
}

客户端 client

#include <cstdio>
#include <cstring>
#include <cerrno>
#include <sys/socket.h>
#include <cstdlib>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define MAXBUF 1024

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 != nullptr) {
        printf("数字证书信息:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0);
        printf("证书: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), nullptr, 0);
        printf("颁发者: %s\n", line);
        free(line);
        X509_free(cert);
    } else
        printf("无证书信息!\n");
}

static void PrintData(char *p, char *buf,int len,char *filename)
{
    char *name=p;
    printf("%s[%d]:\n",p,len);
    for (p=buf; p && p++-buf<len;)
        printf("%02x%c",(unsigned char)p[-1],(!((p-buf)%16) || p-buf==len)?'\n':' ');
//	if (filename) FileWrite(name,buf,len,filename);
}

int main(int argc, char **argv)
{
    int sock_fd = -1;            /* TCP套接字    */
    int len = 0;                 /* SSL会话环境 */
    SSL *ssl = nullptr;          /* SSL安全套接字 */
    struct sockaddr_in ser_addr; /* 服务器地址 */
    bzero(&ser_addr, sizeof(ser_addr));
    SSL_CTX *ctx = nullptr;
    char buffer[MAXBUF + 1];

    if( argc != 3 )
    {
        printf("argcment wrong:ip port content\n");
        exit(0);
    }

    /* SSL 库初始化,参看 ssl-server.c 代码 */
    SSL_library_init();
    SSLeay_add_ssl_algorithms();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
//    ERR_load_BIO_strings();

    do{
        /* 申请SSL会话环境 */
        if( nullptr==(ctx=SSL_CTX_new(TLSv1_2_method())) )    //使用SSL_CTX_new()创建会话环境,建立连接时要使用协议由TLS_client_method()来定,服务器由对应的TLS_server_method()来定。如果这一步出错,需要查看错误栈来查看原因
        {
            ERR_print_errors_fp(stdout);
            break;
        }

        //指定密码算法套件
        // gmssl ciphers -V 明确支持
        SSL_CTX_set_cipher_list(ctx,"ECDHE-SM2-WITH-SMS4-GCM-SM3");
        // SM2-XXX
//        SSL_CTX_set_cipher_list(ctx,"GMTLS_SM2DHE_SM2SIGN_WITH_SMS4_SM3"); //{0xe0,0x11}
//        SSL_CTX_set_cipher_list(ctx,"GMTLS_SM2ENC_WITH_SMS4_SM3"); //{0xe0,0x13}

        // 双向验证
        // 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, nullptr);
        // 设置信任根证书
        if (SSL_CTX_load_verify_locations(ctx, "/home/chy-cpabe/CLionProjects/learn_GmSSL_server/pem/CaCert.pem",nullptr)<=0)
//        if(SSL_CTX_load_verify_locations(ctx, "/home/chy-cpabe/ssl_server_client.openssl_bak/ca/ca.crt", nullptr) != 1)
        {
            ERR_print_errors_fp(stdout);
            exit(1);
        }

        /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
        if (SSL_CTX_use_certificate_file(ctx, "/home/chy-cpabe/CLionProjects/ssl_client/src/pem/TerminalCert.pem", SSL_FILETYPE_PEM) <= 0)
//        if (SSL_CTX_use_certificate_file(ctx, "/home/chy-cpabe/ssl_server_client.openssl_bak/client/pem/client.crt", SSL_FILETYPE_PEM) <= 0)
        {
            ERR_print_errors_fp(stdout);
            exit(1);
        }
        /* 载入用户私钥 */
        if (SSL_CTX_use_PrivateKey_file(ctx, "/home/chy-cpabe/CLionProjects/ssl_client/src/pem/TerminalKey.pem", SSL_FILETYPE_PEM) <= 0)
//        if (SSL_CTX_use_PrivateKey_file(ctx, "/home/chy-cpabe/ssl_server_client.openssl_bak/client/pem/client_rsa_private.pem.unsecure", 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);
        }
        //https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_mode.html
        SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
        /* 创建一个 socket 用于 tcp 通信 */
        if(-1==(sock_fd=socket(AF_INET, SOCK_STREAM, 0)) )
        {
            printf("creat socket wrong\n");
            break;
        }
        printf("socket created\n");
        /* 初始化服务器端(对方)的地址和端口信息 */
        ser_addr.sin_family = AF_INET;
        ser_addr.sin_port = htons(atoi(argv[2]));
        ser_addr.sin_addr.s_addr = inet_addr(argv[1]);
        //将网络地址转成网络二进制的数字
        //http://c.biancheng.net/cpp/html/362.html
        //另外一种写法
/*		if (inet_aton(argv[1], (struct in_addr *) &ser_addr.sin_addr.s_addr) == 0) {
			perror(argv[1]);
			exit(errno);
		}
*/
        printf("address created\n");
        //建立连接
        if( -1==(connect(sock_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr))) )
        {
            printf("connect wrong\n");
            break;
        }
        printf("server connected\n");

        /* 基于 ctx 产生一个新的 SSL */
        ssl = SSL_new(ctx);
        SSL_set_fd(ssl, sock_fd);
        /* 建立 SSL 连接 */
        if (SSL_connect(ssl) == -1)
            ERR_print_errors_fp(stderr);
        else {
            printf("The relevant information is as follows:\n");
            printf("-->ssl version %s\n",SSL_get_version(ssl));
            printf("-->ssleay version %s\n",SSLeay_version(0));
            printf("-->Connected with %s encryption\n", SSL_get_cipher(ssl));
            ShowCerts(ssl);
        }

        //导出key和salt
        unsigned char buf[16];
        int err = -1;
        err = SSL_export_keying_material(ssl, buf, 16, nullptr,0, nullptr, 0, 1);
        if(err != 1){
            printf("err=%d\n",err);
        }else{
            PrintData("SSL_export_keying_material", (char*)buf, 16, nullptr);
        }


        /* 接收对方发过来的消息,最多接收 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 连接 */
        SSL_shutdown(ssl);
        /* 释放 SSL */
        SSL_free(ssl);
        ssl = nullptr;
    }while(0);

    /* 关闭socket */
    close(sock_fd);
    sock_fd = -1;
    /* 释放 CTX */
    SSL_CTX_free(ctx);
    ctx = nullptr;
    return 0;
}

测试

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CLion中设置头文件和文件,你可以按照以下步骤进行操作: 1. 打开CLion并打开你的项目。 2. 在项目窗口中,右键单击你的项目文件夹,然后选择"Open Folder as Project"。 3. 在项目窗口中,右键单击你的项目文件夹,然后选择"New" -> "C/C++ Source File"。 4. 在弹出的对话框中,选择"Header File",然后点击"OK"。 5. 在新建的头文件中,添加你需要的头文件内容。 6. 在项目窗口中,右键单击你的项目文件夹,然后选择"Open Folder as Project"。 7. 在项目窗口中,右键单击你的项目文件夹,然后选择"New" -> "C/C++ Source File"。 8. 在弹出的对话框中,选择"Source File",然后点击"OK"。 9. 在新建的源文件中,添加你需要的文件内容。 请注意,以上步骤是基于CLion的默认设置。如果你的项目需要特定的头文件和文件路径,你可以在CLion的设置中进行配置。具体的设置步骤可以参考CLion的官方文档或者使用CLion的帮助功能。 #### 引用[.reference_title] - *1* *3* [Go (Golang) 工具之copyright 添加 | go源码添加授权头](https://blog.csdn.net/inthat/article/details/124004352)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Windows下Clion安装boost](https://blog.csdn.net/weixin_43488671/article/details/115286101)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值