
最近一直在学习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设计一个简单的界面,在按钮的槽函数中进行相关的操作,也就是调用PolarSSL库函数进行编程,初始化ssl相关结构体,监听端口,等等。
SSL中最重要的就是执行握手操作。这里需要注意一点,由于涉及到socket编程,像accept函数都是阻塞的,如果在gui主线程中调用会造成界面冻结,也就是我们常说的ANR。解决方法就是将这些操作放在一个线程中,Qt中创建一个线程比较容易,创建一个类,继承自QThread,实现run函数,即可,最后启动线程也比较简单,调用该类的start()
函数即可。好了,不多说了,上代码,首先看看服务器端的代码结构:workThread即是线程,实现SSL相关的功能,监听套接字,实现SSL握手,读取客户端发来的消息,向客户端发送消息。
mianwindow即是主窗口界面,有个按钮,在按钮的槽函数中启动线程
代码:
工程文件:
- #-------------------------------------------------
- #
- # 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\n ! x509parse_crt returned %d\n\n", ret );
- return;
- }
- ret = x509parse_crt( &srvcert, (unsigned char *) test_ca_crt,
- strlen( test_ca_crt ) );
- if( ret != 0 )
- {
- printf( " failed\n ! x509parse_crt returned %d\n\n", ret );
- return;
- }
- ret = x509parse_key( &rsa, (unsigned char *) test_srv_key,
- strlen( test_srv_key ), NULL, 0 );
- if( ret != 0 )
- {
- printf( " failed\n ! x509parse_key returned %d\n\n", 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\n" );
- break;
- case POLARSSL_ERR_NET_CONN_RESET:
- printf( " connection was reset by peer\n" );
- break;
- default:
- printf( " ssl_read returned %d\n", ret );
- break;
- }
- break;
- }
- len = ret;
- printf( " %d bytes read\n\n%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\n ! peer closed the connection\n\n" );
- return;
- }
- if( ret != POLARSSL_ERR_NET_TRY_AGAIN )
- {
- printf( " failed\n ! ssl_write returned %d\n\n", 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 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\r\n\r\n"
- */
- #define SERVER_NAME "polarssl.org"
- #define GET_REQUEST \
- "GET /hello/ HTTP/1.1\r\n" \
- "Host: polarssl.org\r\n\r\n"
- #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( "\n . 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\n ! net_connect returned %d\n\n", ret );
- return;
- }
- printf( " ok\n" );
- /*
- * 2. Setup stuff
- */
- printf( " . Setting up the SSL/TLS structure..." );
- fflush( stdout );
- if( ( ret = ssl_init( &ssl ) ) != 0 )
- {
- printf( " failed\n ! ssl_init returned %d\n\n", ret );
- return;
- }
- printf( " ok\n" );
- 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\n ! ssl_write returned %d\n\n", ret );
- return;
- }
- }
- len = ret;
- printf( " %d bytes written\n\n%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\n ! ssl_read returned %d\n\n", ret );
- break;
- }
- len = ret;
- printf( " %d bytes read\n\n%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 );
- }
http://blog.****.net/chenlong12580/article/details/30556955