admin 管理员组

文章数量: 887018

文章目录

  • 前言
  • 一、安装openssl
  • 二、创建证书目录和必要文件
    • 1、创建sslcertTest文件夹
    • 2、创建opensslf文件
    • 3、创建必要文件
  • 三、创建密钥和证书
    • 1、创建根证书私钥ca.key
    • 2、创建根证书请求文件ca.csr
    • 3、创建自签根证书ca.crt
    • 4、创建服务端私钥server.key
    • 5、创建服务端证书请求文件server.csr
    • 6、创建自签服务端证书server.crt
  • 四、Qt使用QSslSocket实现的单向认证
    • 1、客户端
    • 2、服务端


前言

最近工作中要完成ssl加密啊通信,使用到了openssl,本文主要介绍win10系统下其安装和配置及对应的密钥和证书的生成步骤.

一、安装openssl

下载Win64OpenSSL-1_1_1,双击安装;配置环境变量,如下:

验证是否安装成功,输入如下命令:

openssl version

二、创建证书目录和必要文件

1、创建sslcertTest文件夹

我是在C盘下创建的该文件夹.

2、创建opensslf文件

在sslcertTest文件夹下,创建opensslf文件,设置系统变量:

3、创建必要文件

在sslcertTest文件夹下,创建demoCA文件夹;在该文件夹下,创建index.txt、database.txt、serial文件和newcerts文件夹;其中,serial文件创建后,打开该文件并在第一行编辑输入01

三、创建密钥和证书

1、创建根证书私钥ca.key

在C:\sslcertTest目录下,进入命令行,命令如下:

openssl genrsa -out ca.key 2048

此命令采用无密码模式,genrsa 表示采用RSA算法生成根证书私钥,2048表示根证书私钥长度,越长越安全.

2、创建根证书请求文件ca.csr

命令如下:

openssl req -new -key ca.key -out ca.csr

以下为demo:

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:TJ
Locality Name (eg, city) []:TJ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:TJ
Organizational Unit Name (eg, section) []:TJ
Common Name (e.g. server FQDN or YOUR name) []:192.168.10.254
Email Address []:

其中,Country Name 必须填写CN,Common Name 填写服务器IP或域名,否则监测器使用过程中会报SSL错误,尽管忽略错误后可以完成通信。Email Address可以不填;

3、创建自签根证书ca.crt

命令如下:

openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt -days 3650

此处是使用openssl命令完成自签名,证书格式x509,证书有效期10年。

4、创建服务端私钥server.key

命令如下:

openssl genrsa -out server.key 2048

5、创建服务端证书请求文件server.csr

命令如下:

openssl req -new -key server.key -out server.csr

输入如下:

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:TJ
Locality Name (eg, city) []:TJ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:TJ
Organizational Unit Name (eg, section) []:TJ
Common Name (e.g. server FQDN or YOUR name) []:192.168.10.254
Email Address []:

注意此处,与CA证书请求文件输入一致。

6、创建自签服务端证书server.crt

命令如下:

openssl ca -in server.csr -cert ca.crt -keyfile ca.key -out server.crt -days 3650

此处是自签名,即使用上述自己创建的ca.crt和ca.key,对server.csr进行签名,最终生成一个带有签名的server.crt。
如果你的服务端证书需要别的CA签名机构来签名,则将server.csr请求文件发送给签名机构,签名过程中需要CA的公钥证书和私钥参与,机构签名完成后,返回一个带有CA签名的server.crt文件。

四、Qt使用QSslSocket实现的单向认证

这里仅展示主要代码:

1、客户端

#include "certificateinfo.h"
#include "sslclient.h"

SslClient::SslClient(QWidget *parent)
    : QWidget(parent)
{
    setupUi();
    setupSecureSocket();
}

SslClient::~SslClient()
{

}

void SslClient::updateEnabledState()
{
    /* 不为空或socket处于未连接状态,则unconnected为真 */
    const bool unconnected = socket->state() == QAbstractSocket::UnconnectedState;

    /* 更新界面各控件的状态 */
    hostNameEdit->setReadOnly(!unconnected);
    hostNameEdit->setFocusPolicy(unconnected ? Qt::StrongFocus : Qt::NoFocus);
    hostNameLabel->setEnabled(unconnected);
    portBox->setEnabled(unconnected);
    portLabel->setEnabled(unconnected);
    connectButton->setEnabled(unconnected && !hostNameEdit->text().isEmpty());

    const bool connected = socket->state() == QAbstractSocket::ConnectedState;
    sessionOutput->setEnabled(connected);
    sessionInput->setEnabled(connected);
    sessionInputLabel->setEnabled(connected);
    sendButton->setEnabled(connected);
}

/* 点击connect按钮后会执行该函数 */
void SslClient::secureConnect()
{
    /* 设置SSL协议版本,和服务器相同 */
    socket->setProtocol(QSsl::TlsV1_2);

    /* 由于是自签证,所以要设置根证书,用于验证服务器证书,否则客户端不信任服务器 */
    QList<QSslCertificate> certs = QSslCertificate::fromPath("C:/sslcertTest/ca.crt");
    socket->setCaCertificates(certs);

    /* 客户端链接服务器进行SSL握手,当进入加密模式后会发出Encrypted()信号 */
    socket->connectToHostEncrypted(hostNameEdit->text(), portBox->value());

    updateEnabledState();
}

void SslClient::socketStateChanged(QAbstractSocket::SocketState state)
{
    if (executingDialog)
        return;

    updateEnabledState();

    if (state == QAbstractSocket::UnconnectedState) {
        sessionInput->clear();
        hostNameEdit->setPalette(QPalette());
        hostNameEdit->setFocus();
        cipherLabel->setText(tr("<无>"));
        padLock->hide();
    }
}

void SslClient::socketEncrypted()
{
    sessionOutput->clear();
    sessionInput->setFocus();

    /* 设置lineEdit的背景色 */
    QPalette palette;
    palette.setColor(QPalette::Base, QColor(255, 255, 192));
    hostNameEdit->setPalette(palette);

    /* 显示加密算法种类 */
    const QSslCipher cipher = socket->sessionCipher();
    const QString cipherInfo = QString("%1, %2 (%3/%4)").arg(cipher.authenticationMethod())
                                       .arg(cipher.name()).arg(cipher.usedBits())
                                       .arg(cipher.supportedBits());
    cipherLabel->setText(cipherInfo);
    padLock->show();
}

/* 读取数据 */
void SslClient::socketReadyRead()
{
    appendString(QString::fromUtf8(socket->readAll()));
}

/* 发送数据 */
void SslClient::sendData()
{
    const QString input = sessionInput->text();
    appendString(input + '\n');
    socket->write(input.toUtf8() + "\r\n");
    sessionInput->clear();
}

void SslClient::socketError(QAbstractSocket::SocketError)
{
    if (handlingSocketError)
        return;

    handlingSocketError = true;
    QMessageBox::critical(this, tr("连接错误"), socket->errorString());
    handlingSocketError = false;
}

void SslClient::sslErrors(const QList<QSslError> &errors)
{

    sslerror->clearError();

    for (const auto &error : errors)
        sslerror->showError(error.errorString());

    executingDialog = true;
    if (sslerror->exec() == QDialog::Accepted)
        socket->ignoreSslErrors();
    executingDialog = false;

    // did the socket state change?
    if (socket->state() != QAbstractSocket::ConnectedState)
        socketStateChanged(socket->state());
}

/* 弹出证书内容显示窗口 */
void SslClient::displayCertificateInfo()
{
    CertificateInfo info;
    info.setCertificateChain(socket->peerCertificateChain());
    info.exec();
}

void SslClient::setupUi()
{

    gridLayout = new QGridLayout;
    hostNameEdit = new QLineEdit;
    hostNameLabel = new QLabel;
    portBox = new QSpinBox;
    portLabel = new QLabel;

    sessionLayout = new QHBoxLayout;
    label = new QLabel;
    connectButton = new QPushButton;

    labelLayout = new QHBoxLayout;
    cipherLabel = new QLabel;
    cipherText = new QLabel;

    sessionOutput = new QTextEdit;

    inputLayout = new QHBoxLayout;
    sendButton = new QPushButton;
    sessionInput = new QLineEdit;
    sessionInputLabel = new QLabel;

    mainLayout = new QVBoxLayout;


    hostNameLabel->setText("主机名称:");
    portLabel->setText("端口:");
    hostNameEdit->setText("192.168.3.140");

    portBox->setRange(0,65535);
    portBox->setValue(5001);

    gridLayout->addWidget(hostNameLabel,0,0);
    gridLayout->addWidget(hostNameEdit,0,1);
    gridLayout->addWidget(portLabel,1,0);
    gridLayout->addWidget(portBox,1,1);


    label->setText("激活会话");
    connectButton->setText("连接到主机");

    sessionLayout->addWidget(label);
    sessionLayout->addWidget(connectButton);

    cipherLabel->setText("<加密算法>");
    cipherText->setText("<无>");
    labelLayout->addWidget(cipherLabel);
    labelLayout->addStretch();
    labelLayout->addWidget(cipherText);


    sessionInputLabel->setText("输入:");
    sendButton->setText("发送");
    inputLayout->addWidget(sessionInputLabel);
    inputLayout->addWidget(sessionInput);
    inputLayout->addWidget(sendButton);

    hostNameEdit->setSelection(0, hostNameEdit->text().size());
    sessionOutput->setHtml(tr("&lt;无连接&gt;"));

    mainLayout->addLayout(gridLayout);
    mainLayout->addLayout(sessionLayout);
    mainLayout->addLayout(labelLayout);
    mainLayout->addWidget(sessionOutput);
    mainLayout->addLayout(inputLayout);

    setLayout(mainLayout);

    connect(hostNameEdit, SIGNAL(textChanged(QString)),
            this, SLOT(updateEnabledState()));
    connect(connectButton, SIGNAL(clicked()),
            this, SLOT(secureConnect()));
    connect(sendButton, SIGNAL(clicked()),
            this, SLOT(sendData()));

    sslerror = new SslErrors;
    connect(sslerror,&SslErrors::sigDisplayCertificateInfo,this,&SslClient::displayCertificateInfo);

    setWindowTitle("SSL通信客户端");

    padLock = new QToolButton;
    /* 设置按钮图标 */
    padLock->setIcon(QIcon(":/encrypted.png"));
    connect(padLock, SIGNAL(clicked()), this, SLOT(displayCertificateInfo()));

#if QT_CONFIG(cursor)
    padLock->setCursor(Qt::ArrowCursor);
#endif
    /* 设置提示信息 */
    padLock->setToolTip(tr("显示详细加密信息."));

    /* 调整按钮大小和焦点 */
    const int extent = hostNameEdit->height() - 2;
    padLock->resize(extent, extent);
    padLock->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);

    /* 界面布局 */
    QHBoxLayout *layout = new QHBoxLayout(hostNameEdit);
    layout->setMargin(hostNameEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth));
    layout->setSpacing(0);
    layout->addStretch();
    layout->addWidget(padLock);

    hostNameEdit->setLayout(layout);
    padLock->hide();


    resize(300,300);
}

void SslClient::setupSecureSocket()
{
    /* socket为空则创建 */
    if (socket)
        return;

    socket = new QSslSocket(this);

    connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
    connect(socket, SIGNAL(encrypted()),
            this, SLOT(socketEncrypted()));
    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(socketError(QAbstractSocket::SocketError)));
    connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
            this, SLOT(sslErrors(QList<QSslError>)));
    connect(socket, SIGNAL(readyRead()),
            this, SLOT(socketReadyRead()));
}

void SslClient::appendString(const QString &line)
{
    /* 设置光标 */
    QTextCursor cursor(sessionOutput->textCursor());
    cursor.movePosition(QTextCursor::End);
    cursor.insertText(line);

    /* 将滚动条设置到最后一行 */
    sessionOutput->verticalScrollBar()->setValue(sessionOutput->verticalScrollBar()->maximum());
}

2、服务端

#include "sslserver.h"
#include <QDebug>
#include <QThread>
#include <QMessageBox>

sslserver::sslserver(QObject *parent) : QTcpServer(parent)
{
}

sslserver::~sslserver()
{
}

/* 当有客户端连接上服务器时会调用该函数 */
void sslserver::incomingConnection(qintptr socketDescriptor)
{
    sock = new QSslSocket;

    connect(sock,SIGNAL(readyRead()),this,SLOT(readyRead()));
    connect(sock,SIGNAL(encrypted()),this,SLOT(encrypted()));

    /* 设置根证书,握手时客户端会接收到根证书 
    QList<QSslCertificate> certs = QSslCertificate::fromPath("C:/sslcertTest/ca.crt");
    sock->setCaCertificates(certs);*/

    /* 设置本地的服务器证书,证书中的Common Name要写服务器地址 */
    sock->setLocalCertificate("C:/sslcertTest/server.crt");

    /* 当服务器秘钥有密码时 */
    //QString passwd = "1234";
    //sock->setPrivateKey("C:/sslcertTest/server.key",QSsl::Rsa,QSsl::Pem,passwd.toLocal8Bit());
    /* 如果服务器秘钥没有密码 */
    sock->setPrivateKey("C:/sslcertTest/server.key");

    /* 设置ssl通信协议版本 */
    sock->setProtocol(QSsl::TlsV1_2);

    /* 使用的加密算法 */
    /* 默认就是这个"DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA" */
    sock->setCiphers(QSslSocket::supportedCiphers());
    //sock->setCiphers("RC4-SHA");

    if(sock->setSocketDescriptor(socketDescriptor))
    {
        /* SSL握手 */
        sock->startServerEncryption();
    }
    else
    {
        delete sock;
    }
}

void sslserver::startssl()
{
    /* 允许所有地址,监听端口5001,设置这个端口为了排除其他的干扰*/
    listen(QHostAddress::Any,5001);  //
}

void sslserver::readyRead()
{
    if(display)
    {
        display->append(sock->readAll());
    }
}

void sslserver::encrypted()
{
    if(display)
    {
        display->append("进入加密模式");
    }
}

void sslserver::setDisplayContrl(QTextEdit *disp)
{
    display = disp;
}

本文标签: 证书 系统 openssl