从证书生成,到QSslSocket 等类的应用

鄙人因需要折腾了一下QSslSocket,以下知识均来自网络以及 Qt 官方文档。


Ssl 相关背景知识:

CA,X509标准等,可自行搜索.网上也挺多的.

下面三点来自网络,可以对后面的QSslSocket编程有个感性认识.

1. 信息内容加密

接收者的公钥:用于加密

接收者的私钥:用于解密

--------验证接收者(保证数据私密性)

2. 数字签名

发送者的私钥:用于加密

发送者的公钥:用于解密

--------验证发送者(保证不可抵赖性、数据完整性)

3. 证书

用于确认公钥所对应的私钥持有者的身份

证书与公共密钥相关联

证书内容包括:证书的名称(含国家,,城市,组织等信息),公匙;认证机构的名称,签名;有效时间;证书签发的流水号等信息.

证书的概念:首先要有一个根证书,然后用根证书来签发用户证书。

对于申请证书的用户:在生成证书之前,一般会有一个私钥,然后用私钥生成证书请求(证书请求里应含有公钥信息),再利用证书服务器的根证书来签发证书。

特别的:

1)自签名证书(一般用于顶级证书、根证书):证书的名称和认证机构的名称相同.

2)根证书:根证书是CA认证中心给自己颁发的证书,是信任链的起始点。安装根证书意味着对这个CA认证中心的信任

数字证书则是由证书认证机构(CA)对证书申请者真实身份验证之后,用CA的根证书对申请人的一些基本信息以及申请人的公钥进行签名(相当于加盖发证书机构的公章)后形成的一个数字文件。数字证书包含证书中所标识的实体的公钥(就是说你的证书里有你的公钥),由于证书将公钥与特定的个人匹配,并且该证书的真实性由颁发机构保证(就是说可以让大家相信你的证书是真的),因此,数字证书为如何找到用户的公钥并知道它是否有效这一问题提供了解决方案。





Qt 文档对QSslSocket 的简介(读者可以看英文原文,但实际应用中有些地方跟文档里描述的有点不一样

,下面这些会加一点鄙人实践之后的经验):

QSslSocket 在Qt4.3引进。 

        QSslSocket 类为服务端和客户端提供了SSL加密套接字. 

你可以使用 QSslSocket 建立的一个安全的,加密的 TCP  连接去传输加密数据.

它可以工作在服务器模式和客户端模式。以及它支持最新的SSL协议,包括 SSLv3 和 TLSvxx (到了Qt 5.2.0已经支持多个TLS版本).

QSslSocket 默认使用 SSLv3 ,你可以在握手之前调用 setProtocol() 去改变 SSL 协议.

         SSL 运行于已进入连接状态的TCP流之上. 使用 QSslSocket 有两种简单的方法建立安全连接.

1.通过直接 SSL 握手。

2.延迟 SSL 握手,延迟 SSL  握手发生在连接已经建立在非加密模式之后.


通常的方法是使用 QSslSocket 构造一个对象通过调用其 connectToHostEncrypted() 来开始一个安全连接.

下面这个方案是当连接已经建立时开始的直接握手。

QSslSocket *socket = new QSslSocket(this);
 connect(socket, SIGNAL(encrypted()), this, SLOT(ready()));

 socket->connectToHostEncrypted("imap.example.com", 993);


延迟握手可以在调用连接函数后,根据是服务端还是客户端调用下面两个函数中的一个:

        socket->startClientEncryption();
        socket->startServerEncryption();

        就像一个普通的 QTcpSocket, QSslSocket 进入 HostLookupState(主机查找), ConnectingState(连接中状态),最后是 ConnectedState(已连接状态),

如果连接成功,握手会自动开始,如果握手成功, encrypted() 信号会被发出,以指明这个套接字已经进入加密状态以供使用。

      注意,当connectToHostEncrypted()函数返回后(encrypted()信号发射之前),就可以马上向套接字写数据了,这些数据在会被保存在套接字的队列中,直到 encrypted()信号被发射.


    一个使用延迟SSL握手去加密一个存在的连接例子,这个例子是一个 SSL  server 加密一个新进入的连接. 假设你创建了一个 SSL server 作为QTcpServer 的子

类. 你要重载 QTcpServer:;incomingConnection() 和一些其它的东西,就像后面这个例子一样. 首先构造一个 QSslSocket 实例,并调用其 setSocketDescriptor()

去设置这个新的套接字的描述符为新传进来的套接字描述符.然后通过调用 starServerEncryption()  初始化 SSL 握手.

void SslServer::incomingConnection(int socketDescriptor)
 {
     QSslSocket *serverSocket = new QSslSocket;
     if (serverSocket->setSocketDescriptor(socketDescriptor)) {
         connect(serverSocket, SIGNAL(encrypted()), this, SLOT(ready()));
         serverSocket->startServerEncryption();
     } else {
         delete serverSocket;
     }
 }


     如果发生错误,QSslSocket 会发射 sslErrors() 信号. 在这个例子里,如果没有执行忽略错误的动作,一旦发生错误,这个连接会被丢弃(droped). 继续,如果要无视所发生的错误,你可以调用 ignoreSslErrors(),可以在以下情况去调用 ignoreSslErrors()

1. 错误发生后,在这个incomingConnection() 这个槽里,或连接 void    sslErrors ( const QList & errors ) 信号的槽里。

2.在构造 QSslSocket 之后,尝试连接之前的任何时刻。

这样会允许QSslSocket 忽视与对端建立认证时所遇到的错误. 在SSL握手期间忽视错误一定要谨慎。

一个安全连接的基本特征就是必须建立了一个成功的握手(此握手,不是TCP连接时所进行的握手,而是建立安全认证时所进行的握手)。

3. 注意:如果你在连接前通过调用了带参的 void QSslSocket::ignoreSslErrors ( const QList & errors ), sslErrors()信号还是会被发射的.

只是连接会成功。


        一旦加密后,你可以将QSslSocket 当做正常的 QTcpSocket 来使用。当 readyRead() 信号被发射时,你可以调用 read(), canReadLine() 和 readLine(),

或者 getChar() 从 QSslSocket 的内部缓冲区读取加密数据。 并且你可以调用 write() 或者 putChar() 向对端写数据. QSslSocket 会自动为你所写入的数据进行加密,并在

数据已经写入到对端时会发出 encryptedBytesWritten() 信号.

 

       为方便起见,QSslSocket 支持 QTcpSocket 的阻塞函数, waitForConnected(), waitForReadyRead(), waitForBytesWritten(), 和 waitForDisconnected(). 同时也提供 .

waitForEncrypted(), waitForEncrypted()在加密连接建立之前会阻塞当前的调用线程.

waitForBytesWritten(),如果在连接调用写函数的话,如果你的数据字节流顺序对不上,建议调用这个函数.

QSslSocket socket;
 socket.connectToHostEncrypted("http.example.com", 443);
 if (!socket.waitForEncrypted()) {
     qDebug() << socket.errorString();
     return false;
 }

 socket.write("GET / HTTP/1.0\r\n\r\n");
 while (socket.waitForReadyRead())
     qDebug() << socket.readAll().data();





QSslSocket 提供了一个延伸, 易用的处理加密用的密码,私钥,本地,对端,认证机构证书(CA certificattes)的 API,也提供了处理握手阶段所发生的错误的API.


下面这些功能也可以被定制:

1. 套接字的加密通信套件可以在握手前通过 setCiphers() 和 setDefaultCiphers().定制.

2. 套接字的本地证书和私钥可以在握手前通过 setLocalCertificate() 和 setPrivateKey()定制.

3. CA证书数据库可以被扩展和定制,可通过这些函数定制,addCaCertificate(), addCaCertificates(), setCaCertificates(), addDefaultCaCertificate(),addDefaultCaCertificates和 setDefaultCaCertificates().


关于密码和证书的更多信息,请参照 QSslCipher 和 QSslCertificate.

注: 要知道bytesWritten() 与 encryptedBytesWritten() 这两个信号之间的不同. 对于 QTcpSocket, bytesWritten() 信号在数据一写入TCP套接字时会马上被发射.对于 QSslSocket, bytesWritten() 会在数据正在加密时发射. 当数据加密后写入到TCP套接字时encryptedBytesWritten()会被发射.



鄙人实践代码:

实践环境: Fedora19 , Qt4.8.5 ,Qt5.2.0(Qt5.2.0,有变化,代码要改动一些),OpenSsl(Fedora 更新到什么版本就是什么版本了)

整个例子,是结合简单的服务端多线程来写的,开启服务端后,可开启多个客户端进行测试.



服务器端(为了简化例子,有一些汲及鄙人自己的东西没有贴上来):

#ifndef SERVER_H
#define SERVER_H
#include "include/common/net_heads.h"
#include 
#include 
#include 
#include 
#include 

class ServerThread;
class Server : public QTcpServer
{
    Q_OBJECT
public:
    Server(QObject *parent = 0);
public slots:
    void startService();
    void stopService();
signals:

private slots:
    void debugMsgThreadDestory();
    void debugMsgThreadFinished();
    void serverThreadDisconnected(int initSocketDescriptor);
protected:
    void incomingConnection(int socketDescriptor);

private:
    QList serverThreadList;
    QMutex threadListLock;
};
#endif // SERVER_H

#include "server.h"
#include "serverthread.h"
#include 
#include "include/debug/debug.h"
Server::Server(QObject *parent)
    : QTcpServer(parent)
{
    /*设置socket的默认ca数据库*/
    QList caCertList = QSslCertificate::fromPath("../certificates/cacert.pem");
    QSslSocket::setDefaultCaCertificates(caCertList);
}

void Server::startService()
{
    listen(QHostAddress::Any,TCP_PORT);
    PrintMsg("listening...");
}

void Server::stopService()
{
    close();
}

void Server::incomingConnection(int socketDescriptor)
{
    ServerThread *thread = new ServerThread(socketDescriptor);
    connect(thread, SIGNAL(socketDisconnected(int)), this, SLOT(serverThreadDisconnected(int)),Qt::QueuedConnection);
    connect(thread,SIGNAL(finished()),this,SLOT(debugMsgThreadFinished()));
    connect(thread,SIGNAL(destroyed()),this,SLOT(debugMsgThreadDestory()));


    threadListLock.lock();
    serverThreadList.append(thread);
    threadListLock.unlock();
    thread->start();
}

void Server::serverThreadDisconnected(int initSocketDescriptor)
{
    QMutexLocker lock(&threadListLock);
    Q_UNUSED(lock)
    for(int i = 0; i < serverThreadList.count(); i++)
    {  
        if(serverThreadList.at(i)->getInitSocketDescriptor() == initSocketDescriptor)
        {
            PrintMsg("serverThreadList.at(%d)->getSocketDescriptor() = %d",i,serverThreadList.at(i)->getSocketDescriptor());
            serverThreadList.at(i)->wait();
            delete serverThreadList.at(i);
            serverThreadList.removeAt(i);
            PrintMsg("serverThreadList.removeAt(%d) initSocketDescriptor = %d list count = %d",i,initSocketDescriptor,serverThreadList.count());
            return;
        }
    }
    return;
}

void Server::debugMsgThreadDestory()
{
    //PrintMsg("thread destory.");
}

void Server::debugMsgThreadFinished()
{
    //PrintMsg("thread finished.");
}


#ifndef SERVERTHREAD_H
#define SERVERTHREAD_H
#include 
#include 
#include 
#include 
class ServerThread : public QThread
{
    Q_OBJECT
public:
    ServerThread(int socketDescriptor, QObject *parent = 0);
    ~ServerThread();
    int getSocketDescriptor() const;
    int getInitSocketDescriptor() const;
protected:
    void run();

private slots:
    void _run();
    void datareceive();
    void handleSocketEncrypted();
    void handleSslModeChanged(QSslSocket::SslMode mode);
    void handleSocketConnected();
    void handleSocketDisconnected();
    void handleSockStateChange(const QAbstractSocket::SocketState &socketState);

    void handleSocketError(const QAbstractSocket::SocketError &socketError);
    void handleSslErrorList(const QList &errorList);
    void handlePeerVerifyError(const QSslError &error);
    void handleSslError(const QSslError &error);

private:
    void requestDestroyed();
signals:
    void socketDisconnected(int initDescriptor);
private:
    int socketDescriptor;
    QSslSocket *socket;
    QTimer *runTimer;
};

#endif // SERVERTHREAD_H

#include "serverthread.h"
#include "include/debug/debug.h"
#include "getsslrelateinfo.h"
#include "crccheck.h"
#include "datatype.h"
#include "datatransfer.h"
ServerThread::ServerThread(int socketDescriptor, QObject *parent) : QThread(parent), socketDescriptor(socketDescriptor)
{
    socket = new QSslSocket(this);
    runTimer = new QTimer(this);
    moveToThread(this);
}

ServerThread::~ServerThread()
{
}

int ServerThread::getSocketDescriptor() const
{
    if(socket)
        return socket->socketDescriptor();
    return -1;
}

int ServerThread::getInitSocketDescriptor() const
{
    return socketDescriptor;
}

void ServerThread::run()
{
    if (!socket->setSocketDescriptor(socketDescriptor)) {
        PrintMsg("socket->setSocketDescriptor %s",socket->errorString().toAscii().data());
        handleSocketDisconnected();
        return;
    } 

#if 1 /*如果不设定证书和私钥,连接会失败,并不像官方所说那样,可以按不加密方式使用*/
    socket->setPrivateKey("../certificates/server.pem");
    socket->setLocalCertificate("../certificates/server.crt");
#else //for test, self signed certificates
    socket->setPrivateKey("test_certificates/server.key");
    socket->setLocalCertificate("test_certificates/server.csr");

    //socket->setPrivateKey("test_certificates/serverlo.key");
    //socket->setLocalCertificate("test_certificates/serverlo.csr");
#endif

    socket->setPeerVerifyMode(QSslSocket::VerifyPeer);

    connect(socket,SIGNAL(readyRead()),this,SLOT(datareceive()));
    connect(socket,SIGNAL(encrypted()),this,SLOT(handleSocketEncrypted()));
    connect(socket,SIGNAL(modeChanged(QSslSocket::SslMode)),this,SLOT(handleSslModeChanged(QSslSocket::SslMode)));
    connect(socket,SIGNAL(connected()),this,SLOT(handleSocketConnected()));
    connect(socket,SIGNAL(disconnected()),this,SLOT(handleSocketDisconnected()));
    connect(socket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(handleSockStateChange(QAbstractSocket::SocketState)));
    connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(handleSocketError(QAbstractSocket::SocketError)));
    connect(socket,SIGNAL(sslErrors(QList)),this,SLOT(handleSslErrorList(QList)),Qt::DirectConnection);
    connect(socket,SIGNAL(peerVerifyError(QSslError)),this,SLOT(handlePeerVerifyError(QSslError)));
    socket->startServerEncryption();
    runTimer->setInterval(1000);
    runTimer->setSingleShot(true);
    connect(runTimer,SIGNAL(timeout()),this,SLOT(_run()));
    runTimer->start();
    PrintMsg("socket Mode: %d",socket->mode());
    exec();
}

void ServerThread::_run()
{

    if(socket->isEncrypted())
    {
        QString str = QString("hello I am Server threadId: %1").arg(QThread::currentThreadId());
        QByteArray block;
        block.resize(str.length() + 1);
        memcpy(block.data(),str.toLocal8Bit().data(),block.size());
        socket->write(block);
    }
    runTimer->start();
}

void ServerThread::datareceive()
{
    while(socket->bytesAvailable() > 0 )
    {
        QByteArray datagram;
        datagram.resize(socket->bytesAvailable());
        socket->read(datagram.data(),datagram.size());
        qDebug() << "thread:" << QThread::currentThreadId() << datagram;
    }
}

void ServerThread::handleSocketEncrypted()
{
    PrintMsg("handleSocketEncrypted.");
}

void ServerThread::handleSslModeChanged(QSslSocket::SslMode mode)
{
    PrintMsg("socket Mode Change: %d",mode);
}

void ServerThread::handleSocketConnected()
{
    PrintMsg("Socket Connected");
}

void ServerThread::handleSocketDisconnected()
{
    PrintMsg("Socket Disconnected");
    runTimer->stop();
    requestDestroyed();
}

void ServerThread::requestDestroyed()
{
    PrintMsg("requestDestroyed.bytesToWrite %lld",socket->bytesToWrite());
    if(socket->isOpen())
        socket->close();
    quit();
    emit socketDisconnected(socketDescriptor);
}



void ServerThread::handleSocketError(const QAbstractSocket::SocketError &socketError)
{
    PrintMsg("================socket error=================");
    switch(socketError)
    {
    case QAbstractSocket::ConnectionRefusedError:
        PrintMsg("The connection was refused by the peer (or timed out).");
        break;
    case QAbstractSocket::RemoteHostClosedError:
        PrintMsg("The remote host closed the connection. Note that the client socket (i.e., this socket) will be closed after the remote close notification has been sent.");
        break;
    case QAbstractSocket::HostNotFoundError:
        PrintMsg("The host address was not found.");
        break;
    case QAbstractSocket::SocketAccessError:
        PrintMsg("The socket operation failed because the application lacked the required privileges.");
        break;
    case QAbstractSocket::SocketResourceError:
        PrintMsg("The local system ran out of resources (e.g., too many sockets).");
        break;
    case QAbstractSocket::SocketTimeoutError:
        PrintMsg("The socket operation timed out.");
        break;
    case QAbstractSocket::DatagramTooLargeError:
        PrintMsg("The datagram was larger than the operating system's limit (which can be as low as 8192 bytes).");
        break;
    case QAbstractSocket::NetworkError:
        PrintMsg("An error occurred with the network (e.g., the network cable was accidentally plugged out).");
        break;
    case QAbstractSocket::AddressInUseError:
        PrintMsg("The address specified to bind() is already in use and was set to be exclusive.");
        break;
    case QAbstractSocket::SocketAddressNotAvailableError:
        PrintMsg("The address specified to bind() does not belong to the host.");
        break;
    case QAbstractSocket::UnsupportedSocketOperationError:
        PrintMsg("The requested socket operation is not supported by the local operating system (e.g., lack of IPv6 support).");
        break;
    case QAbstractSocket::ProxyAuthenticationRequiredError:
        PrintMsg("The socket is using a proxy, and the proxy requires authentication.");
        break;
    case QAbstractSocket::SslHandshakeFailedError:
        PrintMsg("The SSL/TLS handshake failed, so the connection was closed (only used in SslSocket)");
        break;
    case QAbstractSocket::UnfinishedSocketOperationError:
        PrintMsg("Used by AbstractSocketEngine only, The last operation attempted has not finished yet (still in progress in the background).");
        break;
    case QAbstractSocket::ProxyConnectionRefusedError:
        PrintMsg("Could not contact the proxy server because the connection to that server was denied");
        break;
    case QAbstractSocket::ProxyConnectionClosedError:
        PrintMsg("The connection to the proxy server was closed unexpectedly (before the connection to the final peer was established)");
        break;
    case QAbstractSocket::ProxyConnectionTimeoutError:
        PrintMsg("The connection to the proxy server timed out or the proxy server stopped responding in the authentication phase.");
        break;
    case QAbstractSocket::ProxyNotFoundError:
        PrintMsg("The proxy address set with setProxy() (or the application proxy) was not found.");
        break;
    case QAbstractSocket::ProxyProtocolError:
        PrintMsg("The connection negotiation with the proxy server because the response from the proxy server could not be understood.");
        break;
    case QAbstractSocket::UnknownSocketError:
        PrintMsg("An unidentified error occurred.");
        break;
    default:
        PrintMsg("Not match any errors.");
        break;
    }
}

void ServerThread::handleSslErrorList(const QList &errorList)
{
    PrintMsg("====================Ssl Error List==================");
    for(int i = 0 ;i < errorList.count(); i++)
    {
        QSslError SslError = errorList.at(i);
        handleSslError(SslError);
    }
}

void ServerThread::handlePeerVerifyError(const QSslError &error)
{
    handleSslError(error);
}

void ServerThread::handleSslError(const QSslError & error)
{
    PrintMsg("====================Ssl Error==================");
    PrintMsg("%s",error.errorString().toAscii().data());
    switch(error.error())
    {
    case QSslError::NoError:
        break;
    case QSslError::UnableToGetIssuerCertificate:
        break;
    case QSslError::UnableToDecryptCertificateSignature:
        break;
    case QSslError::UnableToDecodeIssuerPublicKey:
        break;
    case QSslError::CertificateSignatureFailed:
        break;
    case QSslError::CertificateNotYetValid:
        break;
    case QSslError::CertificateExpired:
        break;
    case QSslError::InvalidNotBeforeField:
        break;
    case QSslError::InvalidNotAfterField:
        break;
    case QSslError::SelfSignedCertificate:
        break;
    case QSslError::SelfSignedCertificateInChain:
        break;
    case QSslError::UnableToGetLocalIssuerCertificate:
        break;
    case QSslError::UnableToVerifyFirstCertificate:
        break;
    case QSslError::CertificateRevoked:
        break;
    case QSslError::InvalidCaCertificate:
        break;
    case QSslError::PathLengthExceeded:
        break;
    case QSslError::InvalidPurpose:
        break;
    case QSslError::CertificateUntrusted:
        break;
    case QSslError::CertificateRejected:
        break;
    case QSslError::SubjectIssuerMismatch:
        break;
    case QSslError::AuthorityIssuerSerialNumberMismatch:
        break;
    case QSslError::NoPeerCertificate:
        break;
    case QSslError::HostNameMismatch:
        break;
    case QSslError::UnspecifiedError:
        break;
    case QSslError::NoSslSupport:
        break;
    default:
        PrintMsg("no match any ssl error.");
        break;
    }
}

void ServerThread::handleSockStateChange(const QAbstractSocket::SocketState &socketState)
{
    PrintMsg("=============Socket State==============");
    switch(socketState)
    {
    case QAbstractSocket::UnconnectedState:
        PrintMsg("The socket is not connected");
        break;
    case QAbstractSocket::HostLookupState:
        PrintMsg("The socket is performing a host name lookup");
        break;
    case QAbstractSocket::ConnectingState:
        PrintMsg("The socket has started establishing a connection");
        break;
    case QAbstractSocket::BoundState:
        PrintMsg("The socket is bound to an address and port (for servers).");
        break;
    case QAbstractSocket::ClosingState:
        PrintMsg("The socket is about to close (data may still be waiting to be written).");
        break;
    case QAbstractSocket::ListeningState:
        PrintMsg("ListeningState .For internal use only.");
        break;
    default:
        PrintMsg("Not match any states.");
        break;
    }
}

服务器端main.cpp

#include "include/common/net_heads.h"
#include "include/debug/debug.h"
#include "include/exception/common_exception.h"

#include "server.h"
#include 
#include 
int main(int argc , char **argv)
{
    QApplication app(argc,argv);

    Server *server = new Server();
    server->startService();
    return app.exec();
}



客户端(为了简化例子,有一些汲及鄙人自己的东西没有贴上来,如果真有朋友用这个例子学习的话,要自行适配):

#ifndef NETWORKCLIENTTHREAD_H
#define NETWORKCLIENTTHREAD_H
#include "include/common/net_heads.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
class NetWorkClientThread : public QThread
{
    Q_OBJECT

public:
    NetWorkClientThread(QObject *parent = 0);
    ~NetWorkClientThread();
protected:
    void run();
public slots:
    void connectToServer();
    void discontToServer();
    void handleSocketConnected();
    void handleSocketDisconnected();
    void dataReceived();
    void sendData();

    void handleSockStateChange(const QAbstractSocket::SocketState &socketState);
    void handleSocketError(const QAbstractSocket::SocketError &socketError);
    void handleSocketEncrypted();
    void handleSslModeChanged(QSslSocket::SslMode mode);
    void handleSslErrorList(const QList &errorList);
    void handlePeerVerifyError(const QSslError &error);
    void handleSslError(const QSslError &error);
private:

private slots:
    void _run();
private:
    bool status;
    int port;
    QHostAddress *serverIP;
    QSslSocket *socket;
    QTimer *runTimer;
};
#endif // NETWORKCLIENTTHREAD_H



#include "networkclientthread.h"
#include "include/debug/debug.h"
#include "getsslrelateinfo.h"
#include "crccheck.h"
#include "datatransfer.h"
#include "datatype.h"
#include 
NetWorkClientThread::NetWorkClientThread(QObject *parent) : QThread(parent)
{
    status = false;
    port = TCP_PORT;
    serverIP = new QHostAddress();
    runTimer = new QTimer(this);
    connect(runTimer,SIGNAL(timeout()),this,SLOT(_run()));
    moveToThread(this);
}

NetWorkClientThread::~NetWorkClientThread()
{
    delete serverIP;
    delete socket;
}

void NetWorkClientThread::run()
{
    connectToServer();

    runTimer->setInterval(5000);
    runTimer->setSingleShot(true);
    runTimer->start();
    exec();
}

void NetWorkClientThread::_run()
{
    sendData();
    //QTimer::singleShot(1000,this,SLOT(discontToServer()));
    //runTimer->start();
}

//#define SERVERNAME "winkingzhu.xicp.net"
void NetWorkClientThread::connectToServer()
{
    if(!status)
    {
        QString ip;
#ifndef SERVERNAME
        ip.append("localhost.localdomain");
#else
        ip = QString(SERVERNAME);
#endif

        socket = new QSslSocket(this);
        if(!socket)
            PrintMsg("memory not enough");
#if 1
        /**在设置了ignoreSslErrors(...)后,sslerror 信号还是会被发射的,只是连接不会被drop掉**/
        QList expectedSslErrors;
        QList certList = QSslCertificate::fromPath("../certificates/server.crt");
        QSslError error(QSslError::HostNameMismatch,certList.at(0));
        expectedSslErrors.append(error);
        socket->ignoreSslErrors(expectedSslErrors);
        GetSslRelateInfo::getCertificateDetail(certList.at(0));

#endif
#if 0   /*查看ca证书数据库*/
        PrintMsg("=====================DEFAULT CA DATA BASE============================");
        QList caCrtList = QSslSocket::defaultCaCertificates();
        for(int i = 0; i < caCrtList.count(); i++)
        {
            GetSslRelateInfo::getCertificateDetail(caCrtList.at(i));
        }
        PrintMsg("=====================DEFAULT CA DATA BASE END============================");
#endif

#if 1  /*设置socket的ca数据库*/
        QList caCertList = QSslCertificate::fromPath("../certificates/cacert.pem");
        socket->setCaCertificates(caCertList);
        caCertList = socket->caCertificates();

        PrintMsg("=====================SOCKET CA DATA BASE============================");
        for(int i = 0; i < caCertList.count(); i++)
        {
            GetSslRelateInfo::getCertificateDetail(caCertList.at(i));
        }
        PrintMsg("=====================SOCKET CA DATA BASE END============================");
#endif

        socket->setPrivateKey("../certificates/client.pem");
        socket->setLocalCertificate("../certificates/client.crt");

        connect(socket,SIGNAL(encrypted()),this,SLOT(handleSocketEncrypted()));
        connect(socket,SIGNAL(modeChanged(QSslSocket::SslMode)),this,SLOT(handleSslModeChanged(QSslSocket::SslMode)));
        connect(socket,SIGNAL(sslErrors(QList)),this,SLOT(handleSslErrorList(QList)));
        connect(socket,SIGNAL(peerVerifyError(QSslError)),this,SLOT(handlePeerVerifyError(QSslError)));
        connect(socket,SIGNAL(connected()),this,SLOT(handleSocketConnected()));
        connect(socket,SIGNAL(disconnected()),this,SLOT(handleSocketDisconnected()));
        connect(socket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
        connect(socket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(handleSockStateChange(QAbstractSocket::SocketState)));
        connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(handleSocketError(QAbstractSocket::SocketError)));

#ifdef SERVERNAME
        //socket->connectToHost(ip,port);
        socket->connectToHostEncrypted(ip,port);
#else
        //socket->connectToHost(*serverIP,port);
        socket->connectToHostEncrypted(ip,port);
#endif

        PrintMsg("socket Mode: %d",socket->mode());
        status = true;
        PrintMsg("connectToServer\n");
    }

}

void NetWorkClientThread::discontToServer()
{
    if(socket)
        socket->disconnectFromHost();
    quit();
}

void NetWorkClientThread::handleSocketConnected()
{
    int length = 0;
    QString msg = QString("I am in!");
    if((length = socket->write(msg.toLatin1(),msg.length())) != msg.length())
    {
        return;
    }
}

void NetWorkClientThread::sendData()
{
    if(!socket->isEncrypted())
        return;
    QString msg = QString("hello I am client %1").arg(QThread::currentThreadId());
    socket->write(msg.toLatin1(),msg.length());
}


void NetWorkClientThread::handleSockStateChange(const QAbstractSocket::SocketState &socketState)
{
    PrintMsg("=============Socket State==============");
    switch(socketState)
    {
    case QAbstractSocket::UnconnectedState:
        PrintMsg("The socket is not connected");
        break;
    case QAbstractSocket::HostLookupState:
        PrintMsg("The socket is performing a host name lookup");
        break;
    case QAbstractSocket::ConnectingState:
        PrintMsg("The socket has started establishing a connection");
        break;
    case QAbstractSocket::BoundState:
        PrintMsg("The socket is bound to an address and port (for servers).");
        break;
    case QAbstractSocket::ClosingState:
        PrintMsg("The socket is about to close (data may still be waiting to be written).");
        break;
    case QAbstractSocket::ListeningState:
        PrintMsg("ListeningState .For internal use only.");
        break;
    default:
        PrintMsg("Not match any states.");
        break;
    }
    PrintMsg("===========================");
}

void NetWorkClientThread::handleSocketEncrypted()
{
    PrintMsg("handleSocketEncrypted.");
}

void NetWorkClientThread::handleSslModeChanged(QSslSocket::SslMode mode)
{
    PrintMsg("socket Mode Change: %d",mode);
}

void NetWorkClientThread::handleSslErrorList(const QList &errorList)
{
    PrintMsg("====================Ssl Error List==================count %d",errorList.count());
    for(int i = 0 ;i < errorList.count(); i++)
    {
        QSslError SslError = errorList.at(i);
        handleSslError(SslError);
    }
}

void NetWorkClientThread::handlePeerVerifyError(const QSslError &error)
{
#if 1
    handleSslError(error);
#endif
}

void NetWorkClientThread::handleSslError(const QSslError &error)
{
    PrintMsg("====================Ssl Error==================");
    PrintMsg("%s",error.errorString().toAscii().data());
    QSslCertificate cer = error.certificate();

    if(cer.isNull())
    {
        PrintMsg("Error certificate is NULL! error code: %d",error.error());
    }
    else
    {
        PrintMsg("=====Error certificate infomation========error code: %d",error.error());
        GetSslRelateInfo::getCertificateDetail(cer);
        if(cer.isValid())
        {
            PrintMsg("Certificates is Valid.");
        }
        else
        {
            PrintMsg("Certificates is Not Valid.");
        }
    }

    switch(error.error())
    {
    case QSslError::NoError:
        break;
    case QSslError::UnableToGetIssuerCertificate:
        break;
    case QSslError::UnableToDecryptCertificateSignature:
        break;
    case QSslError::UnableToDecodeIssuerPublicKey:
        break;
    case QSslError::CertificateSignatureFailed:
        break;
    case QSslError::CertificateNotYetValid:
        break;
    case QSslError::CertificateExpired:
        break;
    case QSslError::InvalidNotBeforeField:
        break;
    case QSslError::InvalidNotAfterField:
        break;
    case QSslError::SelfSignedCertificate:
        break;
    case QSslError::SelfSignedCertificateInChain:
        break;
    case QSslError::UnableToGetLocalIssuerCertificate:
        break;
    case QSslError::UnableToVerifyFirstCertificate:
        break;
    case QSslError::CertificateRevoked:
        break;
    case QSslError::InvalidCaCertificate:
        break;
    case QSslError::PathLengthExceeded:
        break;
    case QSslError::InvalidPurpose:
        break;
    case QSslError::CertificateUntrusted:
        break;
    case QSslError::CertificateRejected:
        break;
    case QSslError::SubjectIssuerMismatch:
        break;
    case QSslError::AuthorityIssuerSerialNumberMismatch:
        break;
    case QSslError::NoPeerCertificate:
        break;
    case QSslError::HostNameMismatch:
        if(cer.subjectInfo(QSslCertificate::CommonName) == QString("GameServer"))
        {
            //socket->ignoreSslErrors();
        }
        //socket->ignoreSslErrors();
        break;
    case QSslError::UnspecifiedError:
        break;
    case QSslError::NoSslSupport:
        break;
    default:
        PrintMsg("no match any ssl error.");
        break;
    }
}

void NetWorkClientThread::handleSocketError(const QAbstractSocket::SocketError &socketError)
{
    PrintMsg("================socket error=================");
    switch(socketError)
    {
    case QAbstractSocket::ConnectionRefusedError:
        PrintMsg("The connection was refused by the peer (or timed out).");
        break;
    case QAbstractSocket::RemoteHostClosedError:
        PrintMsg("The remote host closed the connection. Note that the client socket (i.e., this socket) will be closed after the remote close notification has been sent.");
        break;
    case QAbstractSocket::HostNotFoundError:
        PrintMsg("The host address was not found.");
        break;
    case QAbstractSocket::SocketAccessError:
        PrintMsg("The socket operation failed because the application lacked the required privileges.");
        break;
    case QAbstractSocket::SocketResourceError:
        PrintMsg("The local system ran out of resources (e.g., too many sockets).");
        break;
    case QAbstractSocket::SocketTimeoutError:
        PrintMsg("The socket operation timed out.");
        break;
    case QAbstractSocket::DatagramTooLargeError:
        PrintMsg("The datagram was larger than the operating system's limit (which can be as low as 8192 bytes).");
        break;
    case QAbstractSocket::NetworkError:
        PrintMsg("An error occurred with the network (e.g., the network cable was accidentally plugged out).");
        break;
    case QAbstractSocket::AddressInUseError:
        PrintMsg("The address specified to bind() is already in use and was set to be exclusive.");
        break;
    case QAbstractSocket::SocketAddressNotAvailableError:
        PrintMsg("The address specified to bind() does not belong to the host.");
        break;
    case QAbstractSocket::UnsupportedSocketOperationError:
        PrintMsg("The requested socket operation is not supported by the local operating system (e.g., lack of IPv6 support).");
        break;
    case QAbstractSocket::ProxyAuthenticationRequiredError:
        PrintMsg("The socket is using a proxy, and the proxy requires authentication.");
        break;
    case QAbstractSocket::SslHandshakeFailedError:
        PrintMsg("The SSL/TLS handshake failed, so the connection was closed (only used in SslSocket)");
        break;
    case QAbstractSocket::UnfinishedSocketOperationError:
        PrintMsg("Used by AbstractSocketEngine only, The last operation attempted has not finished yet (still in progress in the background).");
        break;
    case QAbstractSocket::ProxyConnectionRefusedError:
        PrintMsg("Could not contact the proxy server because the connection to that server was denied");
        break;
    case QAbstractSocket::ProxyConnectionClosedError:
        PrintMsg("The connection to the proxy server was closed unexpectedly (before the connection to the final peer was established)");
        break;
    case QAbstractSocket::ProxyConnectionTimeoutError:
        PrintMsg("The connection to the proxy server timed out or the proxy server stopped responding in the authentication phase.");
        break;
    case QAbstractSocket::ProxyNotFoundError:
        PrintMsg("The proxy address set with setProxy() (or the application proxy) was not found.");
        break;
    case QAbstractSocket::ProxyProtocolError:
        PrintMsg("The connection negotiation with the proxy server because the response from the proxy server could not be understood.");
        break;
    case QAbstractSocket::UnknownSocketError:
        PrintMsg("An unidentified error occurred.");
        break;
    default:
        PrintMsg("Not match any errors.");
        break;
    }
}


void NetWorkClientThread::handleSocketDisconnected()
{
    PrintMsg("disconnect");
}

void NetWorkClientThread::dataReceived()
{
    while(socket->bytesAvailable() > 0 )
    {
        QByteArray datagram;
        datagram.resize(socket->bytesAvailable());
        socket->read(datagram.data(),datagram.size());
        qDebug() << datagram;
    }
}


include/debug/debug.h

#ifndef __DEBUG_H
#define __DEBUG_H
/*这个头文件按下面的需要,包含必要的头文件,比如QString*/
#include "globalinclude.h"
#define DEBUG_PROG

#ifdef _WIN32
/************Windows*****************/
/*获取当前文件,当函数,当前行作为QString*/
#define POS_INF QString("File: %1 Func: %2 Line %3").arg(__FILE__).arg(__FUNCTION__).arg(__LINE__)

#ifdef DEBUG_PROG
#define PrintMsg(str,...) (qDebug("File: %s Func: %s Line: %d "str,__FILE__,__FUNCTION__,__LINE__,__VA_ARGS__))
#else
#define PrintMsg(str,...)
#endif //DEBUG_PROG
/************************************/
#else
/*************Linux*******************/
/*获取当前文件,当函数,当前行作为QString*/
#define POS_INF QString("File: %1 Func: %2 Line %3").arg(__FILE__).arg(__func__).arg(__LINE__)

#ifdef DEBUG_PROG
//#define PrintMsg(str,arg...) (printf("File: %s Func: %s Line: %d "str,__FILE__,__func__,__LINE__,##arg))
#define PrintMsg(str,arg...) (qDebug("File: %s Func: %s Line: %d "str,__FILE__,__func__,__LINE__,##arg))
#else
#define PrintMsg(str,arg...)
#endif //DEBUG_PROG
/**************************************/
#endif //_WIN32
 

#endif //__DEBUG_


客户端main.cpp

#include "include/common/connect_retry.h"
#include "include/debug/debug.h"
#include "include/exception/common_exception.h"
#include 
#include "networkclientthread.h"
#include 
int main(int argc , char **argv)
{
    QApplication app(argc,argv);

    QList  list;
    NetWorkClientThread *networkThread;
    do
    {
        for(int i = 0; i < 1; i++)
        {
            networkThread = new NetWorkClientThread;
            list.append(networkThread);
            networkThread->start();
        }
        for(int i = 0; i < list.count(); i++)
        {
            list.at(i)->wait();
        }
        for(int i = 0; i < list.count(); i++)
        {
            delete list.at(i);
        }
        list.clear();
    }
    while(0);
    return app.exec();
}



===============我是分割线,分割,分割=====================

以上例子证书生成过程(生成是在RHEL6.2上生成的):

.鄙人CA配置文件:/etc/pki/tls/openssl.cnf

####################################################################


#

# OpenSSL exampleconfiguration file.

# This is mostly beingused for generation of certificate requests.

#

# This definition stopsthe following lines choking if HOME isn't

# defined.

HOME = .

RANDFILE =$ENV::HOME/.rnd

# Extra OBJECTIDENTIFIER info:

#oid_file =$ENV::HOME/.oid

oid_section = new_oids

# To use thisconfiguration file with the "-extfile" option of the

# "openssl x509"utility, name here the section containing the

# X.509v3 extensions touse:

# extensions =

# (Alternatively, use aconfiguration file that has only

# X.509v3 extensions inits main [= default] section.)

[ new_oids ]

# We can add new OIDsin here for use by 'ca', 'req' and 'ts'.

# Add a simple OID likethis:

# testoid1=1.2.3.4

# Or use config filesubstitution like this:

#testoid2=${testoid1}.5.6

# Policies used by theTSA examples.

tsa_policy1 = 1.2.3.4.1

tsa_policy2 =1.2.3.4.5.6

tsa_policy3 =1.2.3.4.5.7

####################################################################

[ ca ]

default_ca = CA_default# The default ca section

####################################################################

[ CA_default ]

dir = /etc/pki/CA #Where everything is kept

certs = $dir/certs #Where the issued certs are kept

crl_dir = $dir/crl #Where the issued crl are kept

database =$dir/index.txt # database index file.

#unique_subject = no #Set to 'no' to allow creation of

# several ctificateswith same subject.

new_certs_dir =$dir/newcerts # default place for new certs.

certificate =$dir/cacert.pem # The CA certificate

serial = $dir/serial #The current serial number

crlnumber =$dir/crlnumber # the current crl number

# must be commented outto leave a V1 CRL

crl = $dir/crl.pem #The current CRL

private_key =$dir/private/cakey.pem# The private key

RANDFILE =$dir/private/.rand # private random number file

x509_extensions =usr_cert # The extentions to add to the cert

# Comment out thefollowing two lines for the "traditional"

# (and highly broken)format.

name_opt = ca_default #Subject Name options

cert_opt = ca_default #Certificate field options

# Extension copyingoption: use with caution.

# copy_extensions =copy

# Extensions to add toa CRL. Note: Netscape communicator chokes on V2 CRLs

# so this is commentedout by default to leave a V1 CRL.

# crlnumber must alsobe commented out to leave a V1 CRL.

# crl_extensions =crl_ext

default_days = 365 #how long to certify for修改这个值,可改变默认给签多少天(也可以在签证时,指出有效时长)

default_crl_days= 30 #how long before next CRL

default_md = default #use public key default MD

preserve = no # keeppassed DN ordering

# A few difference wayof specifying how similar the request should look

# For type CA, thelisted attributes must be the same, and the optional

# and supplied fieldsare just that :-)

policy = policy_match

# For the CA policy

[ policy_match ]

countryName = match

stateOrProvinceName =match

organizationName =match

organizationalUnitName= optional

commonName = supplied

emailAddress = optional

# For the 'anything'policy

# At this point intime, you must list all acceptable 'object'

# types.

[ policy_anything ]

countryName = optional

stateOrProvinceName =optional

localityName = optional

organizationName =optional

organizationalUnitName= optional

commonName = supplied

emailAddress = optional

####################################################################

[ req ]

default_bits = 2048

default_md = sha1

default_keyfile =privkey.pem

distinguished_name =req_distinguished_name

attributes =req_attributes

x509_extensions = v3_ca# The extentions to add to the self signed cert

# Passwords for privatekeys if not present they will be prompted for

# input_password =secret

# output_password =secret

# This sets a mask forpermitted string types. There are several options.

# default:PrintableString, T61String, BMPString.

# pkix :PrintableString, BMPString (PKIX recommendation before 2004)

# utf8only: onlyUTF8Strings (PKIX recommendation after 2004).

# nombstr :PrintableString, T61String (no BMPStrings or UTF8Strings).

# MASK:XXXX a literalmask value.

# WARNING: ancientversions of Netscape crash on BMPStrings or UTF8Strings.

string_mask = utf8only

# req_extensions =v3_req # The extensions to add to a certificate request

[req_distinguished_name ]

countryName = CountryName (2 letter code)

countryName_default =CN

countryName_min = 2

countryName_max = 2

stateOrProvinceName =State or Province Name (full name)

#stateOrProvinceName_default= GuangDong

localityName = LocalityName (eg, city)

localityName_default =GuangZhou

0.organizationName =Organization Name (eg, company)

0.organizationName_default= XXX, Inc.

# we can do this but itis not needed normally :-)

#1.organizationName =Second Organization Name (eg, company)

#1.organizationName_default= World Wide Web Pty Ltd

organizationalUnitName= Organizational Unit Name (eg, section)

#organizationalUnitName_default=

commonName = CommonName (eg, your name or your server\'s hostname)

commonName_max = 64

emailAddress = EmailAddress

emailAddress_max = 64

# SET-ex3 = SETextension number 3

[ req_attributes ]

challengePassword = Achallenge password

challengePassword_min =4

challengePassword_max =20

unstructuredName = Anoptional company name

[ usr_cert ]

# These extensions areadded when 'ca' signs a request.

# This goes againstPKIX guidelines but some CAs do it and some software

# requires this toavoid interpreting an end user certificate as a CA.

basicConstraints=CA:FALSE

# Here are someexamples of the usage of nsCertType. If it is omitted

# the certificate canbe used for anything *except* object signing.

# This is OK for an SSLserver.

# nsCertType = server

# For an object signingcertificate this would be used.

# nsCertType = objsign

# For normal client usethis is typical

# nsCertType = client,email

# and for everythingincluding object signing:

# nsCertType = client,email, objsign

# This is typical inkeyUsage for a client certificate.

# keyUsage =nonRepudiation, digitalSignature, keyEncipherment

# This will bedisplayed in Netscape's comment listbox.

nsComment = "OpenSSLGenerated Certificate"

# PKIX recommendationsharmless if included in all certificates.

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid,issuer

# This stuff is forsubjectAltName and issuerAltname.

# Import the emailaddress.

#subjectAltName=email:copy

# An alternative toproduce certificates that aren't

# deprecated accordingto PKIX.

#subjectAltName=email:move

# Copy subject details

#issuerAltName=issuer:copy

#nsCaRevocationUrl =http://www.domain.dom/ca-crl.pem

#nsBaseUrl

#nsRevocationUrl

#nsRenewalUrl

#nsCaPolicyUrl

#nsSslServerName

# This is required forTSA certificates.

# extendedKeyUsage =critical,timeStamping

[ v3_req ]

# Extensions to add toa certificate request

basicConstraints =CA:FALSE

keyUsage =nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]


# Extensions for atypical CA


# PKIX recommendation.

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid:always,issuer

# This is what PKIXrecommends but some broken software chokes on critical

# extensions.

#basicConstraints =critical,CA:true

# So we do thisinstead.

basicConstraints =CA:true

# Key usage: this istypical for a CA certificate. However since it will

# prevent it being usedas an test self-signed certificate it is best

# left out by default.

# keyUsage = cRLSign,keyCertSign

# Some might want thisalso

# nsCertType = sslCA,emailCA

# Include email addressin subject alt name: another PKIX recommendation

#subjectAltName=email:copy

# Copy issuer details

#issuerAltName=issuer:copy

# DER hex encoding ofan extension: beware experts only!

# obj=DER:02:03

# Where 'obj' is astandard or added object

# You can even overridea supported extension:

# basicConstraints=critical, DER:30:03:01:01:FF

[ crl_ext ]

# CRL extensions.

# Only issuerAltNameand authorityKeyIdentifier make any sense in a CRL.

#issuerAltName=issuer:copy

authorityKeyIdentifier=keyid:always

[ proxy_cert_ext ]

# These extensionsshould be added when creating a proxy certificate

# This goes againstPKIX guidelines but some CAs do it and some software

# requires this toavoid interpreting an end user certificate as a CA.

basicConstraints=CA:FALSE

# Here are someexamples of the usage of nsCertType. If it is omitted

# the certificate canbe used for anything *except* object signing.

# This is OK for an SSLserver.

# nsCertType = server

# For an object signingcertificate this would be used.

# nsCertType = objsign

# For normal client usethis is typical

# nsCertType = client,email

# and for everythingincluding object signing:

# nsCertType = client,email, objsign

# This is typical inkeyUsage for a client certificate.

# keyUsage =nonRepudiation, digitalSignature, keyEncipherment

# This will bedisplayed in Netscape's comment listbox.

nsComment = "OpenSSLGenerated Certificate"

# PKIX recommendationsharmless if included in all certificates.

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid,issuer

# This stuff is forsubjectAltName and issuerAltname.

# Import the emailaddress.

#subjectAltName=email:copy

# An alternative toproduce certificates that aren't

# deprecated accordingto PKIX.

#subjectAltName=email:move

# Copy subject details

#issuerAltName=issuer:copy

#nsCaRevocationUrl =http://www.domain.dom/ca-crl.pem

#nsBaseUrl

#nsRevocationUrl

#nsRenewalUrl

#nsCaPolicyUrl

#nsSslServerName

# This really needs tobe in place for it to be a proxy certificate.

proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo

####################################################################

[ tsa ]

default_tsa =tsa_config1 # the default TSA section

[ tsa_config1 ]

# These are used by theTSA reply generation only.

dir = ./demoCA # TSAroot directory

serial = $dir/tsaserial# The current serial number (mandatory)

crypto_device = builtin# OpenSSL engine to use for signing

signer_cert =$dir/tsacert.pem # The TSA signing certificate

# (optional)

certs = $dir/cacert.pem# Certificate chain to include in reply

# (optional)

signer_key =$dir/private/tsakey.pem # The TSA private key (optional)

default_policy =tsa_policy1 # Policy if request did not specify it

# (optional)

other_policies =tsa_policy2, tsa_policy3 # acceptable policies (optional)

digests = md5, sha1 #Acceptable message digests (mandatory)

accuracy = secs:1,millisecs:500, microsecs:100 # (optional)

clock_precision_digits= 0 # number of digits after dot. (optional)

ordering = yes # Isordering defined for timestamps?

# (optional, default:no)

tsa_name = yes # Mustthe TSA name be included in the reply?

# (optional, default:no)

ess_cert_id_chain = no# Must the ESS cert id chain be included?

# (optional, default:no)

####################################################################



.创建必须目录

[root@Winking-PC CA]#pwd

/etc/pki/CA

[root@Winking-PC CA]#mkdir {certs,newcerts,crl}

[root@Winking-PC CA]#ls

certs crl newcertsprivate /*如果没有这几个文件,可以自己创建*/

[root@Winking-PC CA]#echo 00 > serial; /*生成一个初始序列号*/

[root@Winking-PC CA]#touch index.txt /*创建index.txt文件*/

[root@Winking-PC CA]#echo 00 > crlnumber /*创建吊销证书号码文件*/

[root@Winking-PC CA]#ls

certs crl crlnumberindex.txt newcerts private serial

.CA中心生成自己的私钥

[root@Winking-PC CA]#openssl genrsa -out private/cakey.pem -des3 2048

Generating RSA privatekey, 2048 bit long modulus

.............................................................................+++

....................................................................+++

e is 65537 (0x10001)

Enter pass phrase forprivate/cakey.pem: /*输入使用这个私钥时所要输入的密码"你的密码"*/

Verifying - Enter passphrase for private/cakey.pem:

[root@Winking-PC CA]#ls private/

cakey.pem



.通过CA私钥生成CA根证书

[root@Winking-PC CA]#openssl req -new -x509 -key private/cakey.pem -days 14600 >cacert.pem

Enter pass phrase forprivate/cakey.pem:

You are about to beasked to enter information that will be incorporated

into your certificaterequest.

What you are about toenter is what is called a Distinguished Name or a DN.

There are quite a fewfields but you can leave some blank

For some fields therewill be a default value,

If you enter '.', thefield will be left blank.

-----

Country Name (2 lettercode) [CN]:CN

State or Province Name(full name) []:GuangDong

Locality Name (eg,city) [GuangZhou]:

Organization Name (eg,company) [XXX, Inc.]:

Organizational UnitName (eg, section) []:

Common Name (eg, yourname or your server's hostname) []:HOSTNAME

Email Address []:

[root@Winking-PC CA]#ls

cacert.pem certs crlcrlnumber index.txt newcerts private serial



.生成需要申请证书的用户私钥

/*这里在用户文件夹里,生成用户私钥时,也可以指定密码以提高安全性,就像生成CA私钥的步骤一样*/

[Winking@Winking-PCcertificates]$ openssl genrsa 1024 > server.pem

Generating RSA privatekey, 1024 bit long modulus

.......++++++

.....++++++

e is 65537 (0x10001)

[Winking@Winking-PCcertificates]$ ls

server.pem



.使用用户私钥生成一个证书请求文件

[Winking@Winking-PCcertificates]$ openssl req -new -key server.pem -out server.csr

You are about to beasked to enter information that will be incorporated

into your certificaterequest.

What you are about toenter is what is called a Distinguished Name or a DN.

There are quite a fewfields but you can leave some blank

For some fields therewill be a default value,

If you enter '.', thefield will be left blank.

-----

Country Name (2 lettercode) [CN]:CN

State or Province Name(full name) []:GuangDong

Locality Name (eg,city) [GuangZhou]:GuangZhou

Organization Name (eg,company) [XXX, Inc.]:XXX, Inc.

Organizational UnitName (eg, section) []: /*以上填写均要跟CA中心的一致*/

Common Name (eg, yourname or your server's hostname) []:USERHOSTNAME

Email Address []:

Please enter thefollowing 'extra' attributes

to be sent with yourcertificate request

A challenge password[]:

An optional companyname []:

[Winking@Winking-PCcertificates]$ ls

server.csr server.pem

.CA中心签名

由于是在本机操作,所以现在直接在本文件夹操作CA中心签名的步骤.

[Winking@Winking-PCcertificates]$ sudo openssl ca -in server.csr -days 12775 -outserver.crt /*12775= 35*/

Using configurationfrom /etc/pki/tls/openssl.cnf

Enter pass phrase for/etc/pki/CA/private/cakey.pem:    <<在这里输入创建CA私钥的那个密码

Check that the requestmatches the signature

Signature ok

Certificate Details:

Serial Number: 0 (0x0)

Validity

Not Before: Jan 806:11:04 2014 GMT

Not After : Dec 3006:11:04 2048 GMT

Subject:

countryName = CN

stateOrProvinceName =GuangDong

organizationName =XXX, Inc.

commonName = HOSTNAME

X509v3 extensions:

X509v3 BasicConstraints:

CA:FALSE

Netscape Comment:

OpenSSL GeneratedCertificate

X509v3 Subject KeyIdentifier:

1F:B2:9A:2F:AD:52:38:C7:BB:F1:09:37:BB:6F:F9:1B:A2:3C:08:06

X509v3 Authority KeyIdentifier:

keyid:42:02:2E:FA:BB:4C:FF:41:8B:15:05:EE:B2:51:C8:96:C2:4B:39:91

Certificate is to becertified until Dec 30 06:11:04 2048 GMT (12775 days)

Sign the certificate?[y/n]:y


1 out of 1 certificaterequests certified, commit? [y/n]y

Write out database with1 new entries

Data Base Updated

[Winking@Winking-PCcertificates]$ ls

server.crt server.csrserver.pem

.吊销用户证书

1.生成吊销证书

[root@Winking-PCnewcerts]# pwd

/etc/pki/CA/newcerts

[root@Winking-PCnewcerts]# ls

00.pem

[root@Winking-PCnewcerts]# openssl ca -revoke 00.pem

Using configurationfrom /etc/pki/tls/openssl.cnf

Enter pass phrase for/etc/pki/CA/private/cakey.pem:

Revoking Certificate00.

Data Base Updated

[root@Winking-PCnewcerts]# ls

00.pem

2.生成过期列表

[root@Winking-PC CA]#openssl ca -gencrl -out /etc/pki/CA/crl/crl.pem

Using configurationfrom /etc/pki/tls/openssl.cnf

Enter pass phrase for/etc/pki/CA/private/cakey.pem:

[root@Winking-PC CA]#ls crl/

crl.pem

然后crl.pem被连接服务器的客户端导入到过期列表就行了



参考:

http://dgraves.org/content/qt-notes-working-qsslsocket

http://doc.qt.digia.com/solutions/4/qtsslsocket/sslguide.html

.....

还参考了挺多国内前辈的博客,忘记了...,非常感谢

你可能感兴趣的:(Qt)