zoukankan      html  css  js  c++  java
  • Qt调用PolarSSL库(一个)

            最近一直在学习SSL相关知识,也明白了理论相关知识,主要SSL基本概念和连接建立。主要依据PolarSSL开源库学习。学习完了之后就希望能给有所运用,就想用Qt写一个简单的程序,添加对SSL相关概念的把握和对PolarSSL库的运用。当然,终于希望是能够使用Qt做一个比較完好的工具,帮助大家更好的理解和学习SSL相关知识。这都是后话,在第一篇里面,我们就简单用样例展示怎样在Qt里面调用PolarSSL库。


    这篇博客主要是解说Qt里面调用PolarSSL库,至于SSL相关概念在后面的博客再具体介绍。

    SSL握手须要客户端和服务器端交互。这里我们分别介绍。


    1、编译PolarSSL库

    我们准备使用的方式就是编译PolarSSL为.a静态库,然后在Qt中连接,使用的PolarSSL的版本号是0.10.1。

    下载相应的软件版本号。解压缩后在library文件夹下执行make就可以生成libpolarssl.a库文件,例如以下图:


    2、服务器端

    使用Qt设计一个简单的界面。在button的槽函数中进行相关的操作。也就是调用PolarSSL库函数进行编程。初始化ssl相关结构体,监听port。等等。

    SSL中最重要的就是执行握手操作。这里须要注意一点,因为涉及到socket编程。像accept函数都是堵塞的。假设在gui主线程中调用会造成界面冻结,也就是我们常说的ANR。解决方法就是将这些操作放在一个线程中,Qt中创建一个线程比較easy。创建一个类,继承自QThread,实现run函数。就可以。最后启动线程也比較简单,调用该类的start()

    函数就可以。

    好了,不多说了。上代码。首先看看服务器端的代码结构:workThread即是线程。实现SSL相关的功能,监听套接字,实现SSL握手,读取客户端发来的消息,向客户端发送消息。

    mianwindow即是主窗体界面,有个button,在button的槽函数中启动线程

    代码:

    project文件:

    #-------------------------------------------------
    #
    # Project created by QtCreator 2014-05-11T22:28:07
    #
    #-------------------------------------------------
    
    QT       += core gui
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    TARGET = MyPolarSSLToolSrv
    TEMPLATE = app
    
    INCLUDEPATH += /home/chenlong12580/develop/polarTool/polarssl/include
    LIBS += -L "/home/chenlong12580/develop/polarTool/polarssl/lib/" -lpolarssl
    
    SOURCES += main.cpp
            widget.cpp 
        workthread.cpp
    
    HEADERS  += widget.h 
        workthread.h
    
    FORMS    += widget.ui

    线程类:

    void WorkThread::run()
    {
        qDebug() << "I am a thread!";
    
        int listen_fd = 0;
        int client_fd =0;
        int ret= 0;
        havege_state hs;
        ssl_context ssl;
        ssl_session ssn;
        x509_cert srvcert;
        rsa_context rsa;
        unsigned char buf[1024];
        int len = 0;
    
        memset( &srvcert, 0, sizeof( x509_cert ) );
    
        ret = x509parse_crt( &srvcert, (unsigned char *) test_srv_crt,
                             strlen( test_srv_crt ) );
        if( ret != 0 )
        {
            printf( " failed
      !  x509parse_crt returned %d
    
    ", ret );
            return;
        }
    
        ret = x509parse_crt( &srvcert, (unsigned char *) test_ca_crt,
                             strlen( test_ca_crt ) );
        if( ret != 0 )
        {
            printf( " failed
      !  x509parse_crt returned %d
    
    ", ret );
            return;
        }
    
        ret =  x509parse_key( &rsa, (unsigned char *) test_srv_key,
                              strlen( test_srv_key ), NULL, 0 );
        if( ret != 0 )
        {
            printf( " failed
      !  x509parse_key returned %d
    
    ", ret );
            return;
        }
    
        ret = net_bind( &listen_fd, NULL, 8443 );
        if (0 != ret)
        {
            qDebug() << ret;
            return;
        }
    
        qDebug() << "bind ok";
    
        /* socket is block */
        ret = net_accept( listen_fd, &client_fd, NULL );
        if (0 != ret)
        {
            return;
        }
    
        qDebug() << "accept ok";
    
        havege_init( &hs );
    
        ret = ssl_init( &ssl );
        if (0 != ret)
        {
            return;
        }
    
        ssl_set_endpoint( &ssl, SSL_IS_SERVER );
        ssl_set_authmode( &ssl, SSL_VERIFY_NONE );
    
        ssl_set_rng( &ssl, havege_rand, &hs );
        ssl_set_dbg( &ssl, my_debug, stdout );
        ssl_set_bio( &ssl, net_recv, &client_fd,
                     net_send, &client_fd );
        ssl_set_scb( &ssl, my_get_session,
                     my_set_session );
    
        ssl_set_ciphers( &ssl, my_ciphers );
        ssl_set_session( &ssl, 1, 0, &ssn );
    
        memset( &ssn, 0, sizeof( ssl_session ) );
    
        ssl_set_ca_chain( &ssl, srvcert.next, NULL );
        ssl_set_own_cert( &ssl, &srvcert, &rsa );
        ssl_set_dh_param( &ssl, my_dhm_P, my_dhm_G );
    
        qDebug() << "before ssl_handshake ok";
    
        while( ( ret = ssl_handshake( &ssl ) ) != 0 )
        {
            ;
        }
    
        qDebug() << "ssl_handshake ok";
    
        do
        {
            len = sizeof( buf ) - 1;
            memset( buf, 0, sizeof( buf ) );
            ret = ssl_read( &ssl, buf, len );
    
            if( ret == POLARSSL_ERR_NET_TRY_AGAIN )
                continue;
    
            if( ret <= 0 )
            {
                switch( ret )
                {
                case POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY:
                    printf( " connection was closed gracefully
    " );
                    break;
    
                case POLARSSL_ERR_NET_CONN_RESET:
                    printf( " connection was reset by peer
    " );
                    break;
    
                default:
                    printf( " ssl_read returned %d
    ", ret );
                    break;
                }
    
                break;
            }
    
            len = ret;
            printf( " %d bytes read
    
    %s", len, (char *) buf );
        }while( 0 );
    
        char *cc = (char *)buf;
        QString ss(cc);
    
        qDebug() << ss;
    
        (void)sprintf( (char *) buf, HTTP_RESPONSE,
                           ssl_get_cipher( &ssl ) );
    
        while( ( ret = ssl_write( &ssl, buf, len ) ) <= 0 )
        {
            if( ret == POLARSSL_ERR_NET_CONN_RESET )
            {
                printf( " failed
      ! peer closed the connection
    
    " );
                return;
            }
    
            if( ret != POLARSSL_ERR_NET_TRY_AGAIN )
            {
                printf( " failed
      ! ssl_write returned %d
    
    ", ret );
                return;
            }
        }
    
        ssl_close_notify( &ssl );
    
        net_close( client_fd );
        x509_free( &srvcert );
        rsa_free( &rsa );
        ssl_free( &ssl );
    
        cur = s_list_1st;
        while( cur != NULL )
        {
            prv = cur;
            cur = cur->next;
            memset( prv, 0, sizeof( ssl_session ) );
            free( prv );
        }
    
        memset( &ssl, 0, sizeof( ssl_context ) );
    }
    

    主窗体类:

    #include "widget.h"
    #include "ui_widget.h"
    #include <QFileDialog>
    #include <QDebug>
    #include <QMessageBox>
    #include "workthread.h"
    #include "polarssl/havege.h"
    #include "polarssl/certs.h"
    #include "polarssl/x509.h"
    #include "polarssl/ssl.h"
    #include "polarssl/net.h"
    
    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        qDebug() << "server";
    
        connect(this, SIGNAL(emit_parse_cer()), this, SLOT(slot_parse_cer()));
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    void Widget::on_BrowseBtn_clicked()
    {
        QString pathStr = QFileDialog::getOpenFileName(this, QString("选择证书文件"), QString("C:\Users\Administrator\Desktop"), QString("*.*"));
        if (pathStr.length() == 0)
        {
            qDebug() << "please select a cer file!";
            return;
        }
    
        ui->PathEdit->setText(pathStr);
    
        emit emit_parse_cer();
    }
    
    void Widget::slot_parse_cer()
    {
        x509_cert crt;
        memset(&crt, 0, sizeof(crt));
    
        int res = x509parse_crtfile( &crt, ui->PathEdit->text().toLatin1().data());
        if (0 != res)
        {
            QMessageBox::warning(this, "警告", "解析证书失败,请选择正确的证书文件", QMessageBox::Ok);
            return;
        }
    
        ui->CrtInfo->setText(QString("是否为根证书:") + QString::number(crt.ca_istrue));
        ui->CrtInfo->append(QString("证书版本号号:") + QString::number(crt.version));
        ui->CrtInfo->append(QString("有效期:") + QString::number(crt.valid_from.year) + "-" + QString::number(crt.valid_from.mon)
        + QString("  到:") + QString::number(crt.valid_to.year) + "-" + QString::number(crt.valid_to.mon));
    
        qDebug() << crt.ca_istrue;
        qDebug() << crt.valid_from.year;
    }
    
    void Widget::on_pushButton_clicked()
    {
        WorkThread *workThread = new WorkThread;
        workThread->start();
    }

    3、客户端

    客户端比較简单,直接在界面类进行的SSL功能相关的实现,就是创建套接字,链接服务器,进行SSL握手,向服务器发消息,读取服务器发来的消息。

    project文件:

    #-------------------------------------------------
    #
    # Project created by QtCreator 2014-05-11T22:28:07
    #
    #-------------------------------------------------
    
    QT       += core gui
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    TARGET = MyPolarSSLToolCli
    TEMPLATE = app
    
    INCLUDEPATH += /home/chenlong12580/develop/polarTool/polarssl/include
    LIBS += -L "/home/chenlong12580/develop/polarTool/polarssl/lib/" -lpolarssl
    
    SOURCES += main.cpp
            widget.cpp
    
    HEADERS  += widget.h
    
    FORMS    += widget.ui
    

    主窗体类:

    #include "widget.h"
    #include "ui_widget.h"
    #include <QFileDialog>
    #include <QDebug>
    #include <QMessageBox>
    #include "polarssl/havege.h"
    #include "polarssl/certs.h"
    #include "polarssl/x509.h"
    #include "polarssl/ssl.h"
    #include "polarssl/net.h"
    
    #define SERVER_PORT 8443
    /*
    #define SERVER_NAME "localhost"
    #define GET_REQUEST "GET / HTTP/1.0
    
    "
    */
    #define SERVER_NAME "polarssl.org"
    #define GET_REQUEST 
        "GET /hello/ HTTP/1.1
    " 
        "Host: polarssl.org
    
    "
    
    #define DEBUG_LEVEL 0
    
    void my_debug( void *ctx, int level, char *str )
    {
        if( level < DEBUG_LEVEL )
        {
            fprintf( (FILE *) ctx, "%s", str );
            fflush(  (FILE *) ctx  );
        }
    }
    
    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        qDebug() << "client";
    
        connect(this, SIGNAL(emit_parse_cer()), this, SLOT(slot_parse_cer()));
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    void Widget::on_BrowseBtn_clicked()
    {
        QString pathStr = QFileDialog::getOpenFileName(this, QString("选择证书文件"), QString("C:\Users\Administrator\Desktop"), QString("*.*"));
        if (pathStr.length() == 0)
        {
            qDebug() << "please select a cer file!";
            return;
        }
    
        ui->PathEdit->setText(pathStr);
    
        emit emit_parse_cer();
    }
    
    void Widget::slot_parse_cer()
    {
        x509_cert crt;
        memset(&crt, 0, sizeof(crt));
    
        int res = x509parse_crtfile( &crt, ui->PathEdit->text().toLatin1().data());
        if (0 != res)
        {
            QMessageBox::warning(this, "警告", "解析证书失败,请选择正确的证书文件", QMessageBox::Ok);
            return;
        }
    
        ui->CrtInfo->setText(QString("是否为根证书:") + QString::number(crt.ca_istrue));
        ui->CrtInfo->append(QString("证书版本号号:") + QString::number(crt.version));
        ui->CrtInfo->append(QString("有效期:") + QString::number(crt.valid_from.year) + "-" + QString::number(crt.valid_from.mon)
        + QString("  到:") + QString::number(crt.valid_to.year) + "-" + QString::number(crt.valid_to.mon));
    
        qDebug() << crt.ca_istrue;
        qDebug() << crt.valid_from.year;
    }
    
    void Widget::on_pushButton_clicked()
    {
        int ret, len, server_fd;
        unsigned char buf[1024];
        havege_state hs;
        ssl_context ssl;
        ssl_session ssn;
    
        /*
             * 0. Initialize the RNG and the session data
             */
        havege_init( &hs );
        memset( &ssn, 0, sizeof( ssl_session ) );
    
        /*
             * 1. Start the connection
             */
        printf( "
      . Connecting to tcp/%s/%4d...", SERVER_NAME,
                SERVER_PORT );
        fflush( stdout );
    
        if( ( ret = net_connect( &server_fd, "127.0.0.1",
                                 SERVER_PORT ) ) != 0 )
        {
            printf( " failed
      ! net_connect returned %d
    
    ", ret );
            return;
        }
    
        printf( " ok
    " );
    
        /*
             * 2. Setup stuff
             */
        printf( "  . Setting up the SSL/TLS structure..." );
        fflush( stdout );
    
        if( ( ret = ssl_init( &ssl ) ) != 0 )
        {
            printf( " failed
      ! ssl_init returned %d
    
    ", ret );
            return;
        }
    
        printf( " ok
    " );
    
        ssl_set_endpoint( &ssl, SSL_IS_CLIENT );
        ssl_set_authmode( &ssl, SSL_VERIFY_NONE );
    
        ssl_set_rng( &ssl, havege_rand, &hs );
        ssl_set_dbg( &ssl, my_debug, stdout );
        ssl_set_bio( &ssl, net_recv, &server_fd,
                     net_send, &server_fd );
    
        ssl_set_ciphers( &ssl, ssl_default_ciphers );
        ssl_set_session( &ssl, 1, 600, &ssn );
    
        /*
             * 3. Write the GET request
             */
        printf( "  > Write to server:" );
        fflush( stdout );
    
        len = sprintf( (char *) buf, GET_REQUEST );
    
        while( ( ret = ssl_write( &ssl, buf, len ) ) <= 0 )
        {
            if( ret != POLARSSL_ERR_NET_TRY_AGAIN )
            {
                printf( " failed
      ! ssl_write returned %d
    
    ", ret );
                return;
            }
        }
    
        len = ret;
        printf( " %d bytes written
    
    %s", len, (char *) buf );
    
        /*
             * 7. Read the HTTP response
             */
        printf( "  < Read from server:" );
        fflush( stdout );
    
        do
        {
            len = sizeof( buf ) - 1;
            memset( buf, 0, sizeof( buf ) );
            ret = ssl_read( &ssl, buf, len );
    
            if( ret == POLARSSL_ERR_NET_TRY_AGAIN )
                continue;
    
            if( ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY )
                break;
    
            if( ret <= 0 )
            {
                printf( "failed
      ! ssl_read returned %d
    
    ", ret );
                break;
            }
    
            len = ret;
            printf( " %d bytes read
    
    %s", len, (char *) buf );
        }
        while( 0 );
    
       char *cc= (char *)buf;
       QString ss(cc);
    
        qDebug() << ss;
    
        ssl_close_notify( &ssl );
    
        return;
    }
    

    4、执行效果

    以下说说执行的效果,首先启动服务器端。服务器端启动线程,监听套接字:



    接着启动客户端。客户端链接服务器端,写入消息,读取服务器端的响应:



    最后依据打印能够看出服务器端和客户端握手成功。写入读取消息成功:




    我们能够依据抓包来看看SSL握手的过程。例如以下:



    通过抓包能够看到服务器端和客户端的握手过程,以及在不同的握手阶段中做得事情:

    服务器端:

    /*
     * SSL handshake -- server side
     */
    int ssl_handshake_server( ssl_context *ssl )
    {
        int ret = 0;
    
        SSL_DEBUG_MSG( 2, ( "=> handshake server" ) );
    
        while( ssl->state != SSL_HANDSHAKE_OVER )
        {
            SSL_DEBUG_MSG( 2, ( "server state: %d", ssl->state ) );
    
            if( ( ret = ssl_flush_output( ssl ) ) != 0 )
                break;
    
            switch( ssl->state )
            {
                case SSL_HELLO_REQUEST:
                    ssl->state = SSL_CLIENT_HELLO;
                    break;
    
                /*
                 *  <==   ClientHello
                 */
                case SSL_CLIENT_HELLO:
                    ret = ssl_parse_client_hello( ssl );
                    break;
    
                /*
                 *  ==>   ServerHello
                 *        Certificate
                 *      ( ServerKeyExchange  )
                 *      ( CertificateRequest )
                 *        ServerHelloDone
                 */
                case SSL_SERVER_HELLO:
                    ret = ssl_write_server_hello( ssl );
                    break;
    
                case SSL_SERVER_CERTIFICATE:
                    ret = ssl_write_certificate( ssl );
                    break;
    
                case SSL_SERVER_KEY_EXCHANGE:
                    ret = ssl_write_server_key_exchange( ssl );
                    break;
    
                case SSL_CERTIFICATE_REQUEST:
                    ret = ssl_write_certificate_request( ssl );
                    break;
    
                case SSL_SERVER_HELLO_DONE:
                    ret = ssl_write_server_hello_done( ssl );
                    break;
    
                /*
                 *  <== ( Certificate/Alert  )
                 *        ClientKeyExchange
                 *      ( CertificateVerify  )
                 *        ChangeCipherSpec
                 *        Finished
                 */
                case SSL_CLIENT_CERTIFICATE:
                    ret = ssl_parse_certificate( ssl );
                    break;
    
                case SSL_CLIENT_KEY_EXCHANGE:
                    ret = ssl_parse_client_key_exchange( ssl );
                    break;
    
                case SSL_CERTIFICATE_VERIFY:
                    ret = ssl_parse_certificate_verify( ssl );
                    break;
    
                case SSL_CLIENT_CHANGE_CIPHER_SPEC:
                    ret = ssl_parse_change_cipher_spec( ssl );
                    break;
    
                case SSL_CLIENT_FINISHED:
                    ret = ssl_parse_finished( ssl );
                    break;
    
                /*
                 *  ==>   ChangeCipherSpec
                 *        Finished
                 */
                case SSL_SERVER_CHANGE_CIPHER_SPEC:
                    ret = ssl_write_change_cipher_spec( ssl );
                    break;
    
                case SSL_SERVER_FINISHED:
                    ret = ssl_write_finished( ssl );
                    break;
    
                case SSL_FLUSH_BUFFERS:
                    SSL_DEBUG_MSG( 2, ( "handshake: done" ) );
                    ssl->state = SSL_HANDSHAKE_OVER;
                    break;
    
                default:
                    SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) );
                    return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
            }
    
            if( ret != 0 )
                break;
        }
    
        SSL_DEBUG_MSG( 2, ( "<= handshake server" ) );
    
        return( ret );
    }

    客户端:

    /*
     * SSL handshake -- client side
     */
    int ssl_handshake_client( ssl_context *ssl )
    {
        int ret = 0;
    
        SSL_DEBUG_MSG( 2, ( "=> handshake client" ) );
    
        while( ssl->state != SSL_HANDSHAKE_OVER )
        {
            SSL_DEBUG_MSG( 2, ( "client state: %d", ssl->state ) );
    
            if( ( ret = ssl_flush_output( ssl ) ) != 0 )
                break;
    
            switch( ssl->state )
            {
                case SSL_HELLO_REQUEST:
                    ssl->state = SSL_CLIENT_HELLO;
                    break;
    
                /*
                 *  ==>   ClientHello
                 */
                case SSL_CLIENT_HELLO:
                    ret = ssl_write_client_hello( ssl );
                    break;
    
                /*
                 *  <==   ServerHello
                 *        Certificate
                 *      ( ServerKeyExchange  )
                 *      ( CertificateRequest )
                 *        ServerHelloDone
                 */
                case SSL_SERVER_HELLO:
                    ret = ssl_parse_server_hello( ssl );
                    break;
    
                case SSL_SERVER_CERTIFICATE:
                    ret = ssl_parse_certificate( ssl );
                    break;
    
                case SSL_SERVER_KEY_EXCHANGE:
                    ret = ssl_parse_server_key_exchange( ssl );
                    break;
    
                case SSL_CERTIFICATE_REQUEST:
                    ret = ssl_parse_certificate_request( ssl );
                    break;
    
                case SSL_SERVER_HELLO_DONE:
                    ret = ssl_parse_server_hello_done( ssl );
                    break;
    
                /*
                 *  ==> ( Certificate/Alert  )
                 *        ClientKeyExchange
                 *      ( CertificateVerify  )
                 *        ChangeCipherSpec
                 *        Finished
                 */
                case SSL_CLIENT_CERTIFICATE:
                    ret = ssl_write_certificate( ssl );
                    break;
    
                case SSL_CLIENT_KEY_EXCHANGE:
                    ret = ssl_write_client_key_exchange( ssl );
                    break;
    
                case SSL_CERTIFICATE_VERIFY:
                    ret = ssl_write_certificate_verify( ssl );
                    break;
    
                case SSL_CLIENT_CHANGE_CIPHER_SPEC:
                    ret = ssl_write_change_cipher_spec( ssl );
                    break;
    
                case SSL_CLIENT_FINISHED:
                    ret = ssl_write_finished( ssl );
                    break;
    
                /*
                 *  <==   ChangeCipherSpec
                 *        Finished
                 */
                case SSL_SERVER_CHANGE_CIPHER_SPEC:
                    ret = ssl_parse_change_cipher_spec( ssl );
                    break;
    
                case SSL_SERVER_FINISHED:
                    ret = ssl_parse_finished( ssl );
                    break;
    
                case SSL_FLUSH_BUFFERS:
                    SSL_DEBUG_MSG( 2, ( "handshake: done" ) );
                    ssl->state = SSL_HANDSHAKE_OVER;
                    break;
    
                default:
                    SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) );
                    return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
            }
    
            if( ret != 0 )
                break;
        }
    
        SSL_DEBUG_MSG( 2, ( "<= handshake client" ) );
    
        return( ret );
    }



    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    sqlserver2005转换到2000
    vs2008的注册
    给联想K46装系统
    两个调片技巧
    mapx集合的问题
    audio player播放多文件
    JQuery查询ul嵌套结构中当前所处的位置
    .Net中使用带UI的OCX的方法
    U盘启动和量产
    winrar的图标
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4615730.html
Copyright © 2011-2022 走看看