GmSSL编程实现gmtls协议C/S通信(BIO版本)

GmSSL实现gmtls协议时,服务端必须设置双证书(签名证书和加密证书)才能正常通信。如果服务端只使用单证书(加密证书),会出现如下错误:SSL routines:gmtls_construct_ske_sm2:internal error:ssl/statem/statem_gmtls.c:742

  1. 签名证书和加密证书的生成可以使用TASSL开源项目中的Tassl_demo/mk_tls_cert 下的 SM2certgen.sh 脚本,共生成 15 个 PEM 文件:CA.cert.pem  CA.cert.srl  CA.key.pem  CA.pem  CE.cert.pem  CE.key.pem  CE.pem  CS.cert.pem  CS.key.pem  CS.pem  SE.cert.pem  SE.key.pem  SE.pem  SS.cert.pem  SS.key.pem  SS.pem。
    CA开头的为根证书相关,CS和CE开头的分别为客户端签名证书和加密证书,SS和SE开头的分别为服务端签名证书和加密证书
    注意:修改SM2certgen.sh脚本中的LD_LIBRARY_PATH和OPENSSL_CMD变量值
    暂时还未研究GmSSL如何生成签名证书
  2. 在设置双证书时,需要先设置签名证书,然后再设置加密证书,具体可参考源码。
  3. 示例代码是参考Gmssl中的demos/bio下的saccept.c和sconnect.c所作修改,分别为服务端和客户端。
    /*
     * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
     *
     * Licensed under the OpenSSL license (the "License").  You may not use
     * this file except in compliance with the License.  You can obtain a copy
     * in the file LICENSE in the source distribution or at
     * https://www.openssl.org/source/license.html
     */
    
    /*-
     * A minimal program to serve an SSL connection.
     * It uses blocking.
     * saccept host:port
     * host is the interface IP to use.  If any interface, use *:port
     * The default it *:4433
     *
     * cc -I../../include saccept.c -L../.. -lssl -lcrypto -ldl
     */
    
    #include 
    #include 
    #include 
    #include 
    
    #define CA_CERT_FILE            "CA.cert.pem"
    #define SIGN_CERT_FILE          "SS.cert.pem"
    #define SIGN_KEY_FILE           "SS.key.pem"
    #define ENCODE_CERT_FILE        "SE.cert.pem"
    #define ENCODE_KEY_FILE         "SE.key.pem"
    
    static int done = 0;
    
    void interrupt(int sig)
    {
        done = 1;
    }
    
    void sigsetup(void)
    {
        struct sigaction sa;
    
        /*
         * Catch at most once, and don't restart the accept system call.
         */
        sa.sa_flags = SA_RESETHAND;
        sa.sa_handler = interrupt;
        sigemptyset(&sa.sa_mask);
        sigaction(SIGINT, &sa, NULL);
    }
    
    int main(int argc, char *argv[])
    {
        char *port = "0.0.0.0:4433";
        BIO *in = NULL;
        BIO *ssl_bio, *tmp;
        SSL_CTX *ctx;
        char buf[512];
        int ret = 1, i;
    
        //if (argc <= 1)
        //    port = "*:4433";
        //else
        //    port = argv[1];
    
        if (argc > 1 && 0 == strcmp(argv[1], "gmtls"))
        {
            ctx = SSL_CTX_new(GMTLS_server_method());
        }
        else
        {
            ctx = SSL_CTX_new(SSLv23_server_method());
        }
        SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
    #if 1
        if(!SSL_CTX_load_verify_locations(ctx, CA_CERT_FILE, NULL))
        {
            goto err;
        }
        //if (!SSL_CTX_use_certificate_chain_file(ctx, CERT_FILE))
        if (!SSL_CTX_use_certificate_file(ctx, SIGN_CERT_FILE, SSL_FILETYPE_PEM))
            goto err;
        if (!SSL_CTX_use_PrivateKey_file(ctx, SIGN_KEY_FILE, SSL_FILETYPE_PEM))
            goto err;
        if (!SSL_CTX_use_certificate_file(ctx, ENCODE_CERT_FILE, SSL_FILETYPE_PEM))
            goto err;
        if (!SSL_CTX_use_PrivateKey_file(ctx, ENCODE_KEY_FILE, SSL_FILETYPE_PEM))
            goto err;
        if (!SSL_CTX_check_private_key(ctx))
            goto err;
    #endif//
    
        /* Setup server side SSL bio */
        ssl_bio = BIO_new_ssl(ctx, 0);
    
        if ((in = BIO_new_accept(port)) == NULL)
            goto err;
    
        /*
         * This means that when a new connection is accepted on 'in', The ssl_bio
         * will be 'duplicated' and have the new socket BIO push into it.
         * Basically it means the SSL BIO will be automatically setup
         */
        BIO_set_accept_bios(in, ssl_bio);
    
        /* Arrange to leave server loop on interrupt */
        sigsetup();
    
     again:
        /*
         * The first call will setup the accept socket, and the second will get a
         * socket.  In this loop, the first actual accept will occur in the
         * BIO_read() function.
         */
    
        if (BIO_do_accept(in) <= 0)
            goto err;
    
        while (!done) {
            i = BIO_read(in, buf, 512);
            if (i == 0) {
                /*
                 * If we have finished, remove the underlying BIO stack so the
                 * next time we call any function for this BIO, it will attempt
                 * to do an accept
                 */
                printf("Done\n");
                tmp = BIO_pop(in);
                BIO_free_all(tmp);
                goto again;
            }
            if (i < 0)
                goto err;
            fwrite(buf, 1, i, stdout);
            fflush(stdout);
        }
    
        ret = 0;
     err:
        if (ret) {
            ERR_print_errors_fp(stderr);
        }
        BIO_free(in);
        exit(ret);
        return (!ret);
    }
    
    /*
     * Copyright 1998-2016 The OpenSSL Project Authors. All Rights Reserved.
     *
     * Licensed under the OpenSSL license (the "License").  You may not use
     * this file except in compliance with the License.  You can obtain a copy
     * in the file LICENSE in the source distribution or at
     * https://www.openssl.org/source/license.html
     */
    
    /*-
     * A minimal program to do SSL to a passed host and port.
     * It is actually using non-blocking IO but in a very simple manner
     * sconnect host:port - it does a 'GET / HTTP/1.0'
     *
     * cc -I../../include sconnect.c -L../.. -lssl -lcrypto
     */
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define HOSTPORT "127.0.0.1:4433"
    #define CAFILE "CA.cert.pem"
    
    extern int errno;
    
    int main(argc, argv)
    int argc;
    char *argv[];
    {
        const char *hostport = HOSTPORT;
        const char *CAfile = CAFILE;
        char *hostname;
        char *cp;
        BIO *out = NULL;
        char buf[1024 * 10], *p;
        SSL_CTX *ssl_ctx = NULL;
        SSL *ssl;
        BIO *ssl_bio;
        int i, len, off, ret = 1;
    
        //if (argc > 1)
        //    hostport = argv[1];
        //if (argc > 2)
        //    CAfile = argv[2];
    
        hostname = OPENSSL_strdup(hostport);
        if ((cp = strchr(hostname, ':')) != NULL)
            *cp = 0;
    
    #ifdef WATT32
        dbug_init();
        sock_init();
    #endif
    
        if (argc > 1 && 0 == strcmp(argv[1], "gmtls"))
        {
            ssl_ctx = SSL_CTX_new(GMTLS_client_method());
        }
        else
        {
            ssl_ctx = SSL_CTX_new(SSLv23_client_method());
        }
    
        /* Enable trust chain verification */
        SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
        //SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
        SSL_CTX_load_verify_locations(ssl_ctx, CAfile, NULL);
    
        /* Lets make a SSL structure */
        ssl = SSL_new(ssl_ctx);
        SSL_set_connect_state(ssl);
    
        /* Enable peername verification */
        if (SSL_set1_host(ssl, hostname) <= 0)
            goto err;
    
        /* Use it inside an SSL BIO */
        ssl_bio = BIO_new(BIO_f_ssl());
        BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
    
        /* Lets use a connect BIO under the SSL BIO */
        out = BIO_new(BIO_s_connect());
        BIO_set_conn_hostname(out, hostport);
        BIO_set_nbio(out, 1);
        out = BIO_push(ssl_bio, out);
    
        p = "GET / HTTP/1.0\r\n\r\n";
        len = strlen(p);
    
        off = 0;
        for (;;) {
            i = BIO_write(out, &(p[off]), len);
            if (i <= 0) {
                if (BIO_should_retry(out)) {
                    fprintf(stderr, "write DELAY\n");
                    sleep(1);
                    continue;
                } else {
                    goto err;
                }
            }
            off += i;
            len -= i;
            if (len <= 0)
                break;
        }
    
        for (;;) {
            i = BIO_read(out, buf, sizeof(buf));
            if (i == 0)
                break;
            if (i < 0) {
                if (BIO_should_retry(out)) {
                    fprintf(stderr, "read DELAY\n");
                    sleep(1);
                    continue;
                }
                goto err;
            }
            fwrite(buf, 1, i, stdout);
        }
    
        ret = 1;
        goto done;
    
     err:
        if (ERR_peek_error() == 0) { /* system call error */
            fprintf(stderr, "errno=%d ", errno);
            perror("error");
        } else
            ERR_print_errors_fp(stderr);
     done:
        BIO_free_all(out);
        SSL_CTX_free(ssl_ctx);
        return (ret == 1);
    }
    

    另可参考非BIO版本的代码示例

你可能感兴趣的:(SSL/TLS)