• 69.Qt 实现Https ssl证书与内网服务器ip进行双向认证


    参考https://blog.csdn.net/qq_38502914/article/details/124630727
     

    1.安装OpenSSL

    下载地址: Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

    安装到第二个选项时,选择安装到指定的/bin目录下。

     下载好后将两个dll拷贝到:

    然后在qt pro里导入:

    LIBS += -LD:\OpenSSL-Win64\lib -llibcrypto -llibssl

    INCLUDEPATH += D:\OpenSSL-Win64\include

    如果在公司项目中,则需要自己将dll和libs还有include拷贝到工程里,指定生成exe路径

    2.SSL证书介绍

    常见的SSL证书文件后缀扩展名说明:

    *.DER或*.CER文件: 带有这类后缀扩展名的SSL证书文件是二进制格式,只含有SSL证书信息,不包含私钥。

    *.CRT文件: 这样的证书文件可以是二进制格式,也可以是文本格式,一般均为文本格式,功能与 *.DER及*.CER证书文件相同。

    *.PEM文件: 这样的证书文件一般是文本格式,可以存放证书或私钥,或者两者都包含。 *.PEM 文件如果只包含私钥,一般用*.KEY文件代替。

    *.PFX或*.P12文件: 这样的证书文件是二进制格式,同时包含证书和私钥,且一般有密码保护。

    之所以使用证书,是确保服务器地址有效性,主要是通过CA证书与服务器证书进行对比,也可以实现数据传输的加密处理,避免被劫持和篡改

    3.创建SSL证书

    *.csr文件:证书签名请求文件

    *.key文件:私钥文件

    *.crt文件:证书文件

    生成私钥和证书

    1. // CN非常很重要,必须和请求url的地址保持一致
    2. openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 3650 -out server.crt -subj "/C=CN/ST=Shanghai/L=Shanghai/O=xxx/OU=xxx/CN=10.0.1.160/emailAddress=xxx@xxxx.com"
    3. req: 配置参数-x509指定使用 X.509证书签名请求管理(certificate signing request (CSR))."X.509" 是一个公钥代表that SSL and TLS adheres to for its key and certificate management.
    4. -nodes: 告诉OpenSSL生产证书时忽略密码环节.(因为我们需要Nginx自动读取这个文件,而不是以用户交互的形式)。
    5. -days 36500: 证书有效期,100年
    6. -newkey rsa: 2048: 同时产生一个新证书和一个新的SSL key(加密强度为RSA 2048)
    7. -keyout: SSL输出文件名
    8. -out: 证书生成文件名
    9. C : 地区,如中国为 CN,美国为 US
    10. CT: 省份
    11. L: 所在地的市/县/区
    12. O: 填单位/机构/企业合法的名称,比如baidu
    13. OU: 填部门名称
    14. CN: 填域名,由于我们是ip访问,所以填ip,否则填域名,我们在验证服务器证书时,会验证Common Name与http请求的url域名是否匹配,如果不匹配,ssl会返回错误。
    15. emailAddress: 邮件地址

    生成了server.crt  server.key
    nginx开启ssl模块

    由于我这里开启的,所以跳过

    生成客户端证书

    执行命令:

    1. openssl genrsa -out client.key 2048
    2. openssl req -new -key client.key -out client.csr
    3. Country Name (2 letter code) [AU]:CN // 如中国为 CN,美国为 US
    4. State or Province Name (full name) [Some-State]:Shanghai
    5. Locality Name (eg, city) []:Shanghai // 填您所在地的市/县/区
    6. Organization Name (eg, company) [Internet Widgits Pty Ltd]:puniu // 填单位/机构/企业合法的名称
    7. Organizational Unit Name (eg, section) []:xxxClient // 填部门名称
    8. Common Name (e.g. server FQDN or YOUR name) []:xxxClient // 填域名,客户端随便填
    9. Email Address []: email@puniu.com // 邮件地址,可以不必输入,按回车跳过
    10. A challenge password []: # 私钥保护密码,可直接回车
    11. An optional company name []: # 一个可选公司名称,可直接回车
    12. openssl x509 -req -days 3650 -in client.csr -signkey client.key -out client.crt

    然后得到: client.key client.csr client.crt
     

    配置nginx

    服务器只需要服务器证书和私钥以及客户端证书即可
    打开配置文件 nano /etc/nginx/nginx.conf:
    修改如下所示:

    1. server {
    2.         listen       8081;
    3.         listen       443 ssl;            # 监听443端口, 开启ssl(必须)
    4.         listen       [::]:8081;
    5.         server_name  10.0.1.160;  # 必须和服务器证书CN值保持一致,否则会返回ssl错误
    6.         root         /usr/share/nginx/html;
    7.                 
    8.         ssl on;
    9.         # 引用ssl证书(必须,如果放在nginx/conf/ssl下可以用相对路径,其他位置必须用绝对路径)
    10.        ssl_certificate  /etc/pki/CA/lisense/server.crt;
    11.        ssl_certificate_key  /etc/pki/CA/lisense/server.key;
    12.         ssl_client_certificate /etc/pki/CA/lisense/client.crt;
    13.         ssl_verify_client on;                  # 开启客户端传输
    14.         # 协议优化(可选,优化https协议,增强安全性)
    15.         ssl_protocols        TLSv1 TLSv1.1 TLSv1.2;
    16.         ssl_ciphers          HIGH:!aNULL:!MD5;
    17.         ssl_prefer_server_ciphers  on;
    18.         ssl_session_cache    shared:SSL:10m;
    19.         ssl_session_timeout  10m;
    20.         
    21.         //...其它配置
    22.       }

    更新服务器配置
    nginx -t      // 查看配置,返回ok,则可以更新配置
    nginx -s reload  // 更新配置
    注:记得开启防火墙的443端口 firewall-cmd --zone=public --add_port=443/tcp permanent

    4.QT客户端使用https访问服务器验证

    1. qDebug() << "支持OpenSSL: " << QSslSocket::supportsSsl();
    2. // 客户端证书
    3. QFile crtFile("D:/OpenSSL-Win64/bin/client.crt");
    4. crtFile.open(QIODevice::ReadOnly);
    5. const QSslCertificate certificate(&crtFile, QSsl::Pem);
    6. crtFile.close();
    7. // 客户端私钥
    8. QFile keyFile("D:/OpenSSL-Win64/bin/client.key");
    9. keyFile.open(QIODevice::ReadOnly);
    10. const QSslKey prvateKey(&keyFile, QSsl::Rsa);
    11. keyFile.close();
    12. // 通过QSslConfiguration 类进行SSL连接配置
    13. QSslConfiguration config ;
    14. //设置SSL验证模式 客户端与服务器进行双向校验
    15. config.setPeerVerifyMode(QSslSocket::VerifyPeer);
    16. //使用TLS 1.2 协议版本 和服务器保持一致
    17. config.setProtocol(QSsl::TlsV1_2);
    18. config.setPrivateKey(prvateKey);
    19. config.setLocalCertificate(certificate);
    20. //由于是自签名的服务器端证书,我们还得将服务器端的证书叫入到CA证书数据库中,否则服务器端证书将验证失败。
    21. QList<QSslCertificate> caCerList;
    22. QFile fileServCrt("D:/OpenSSL-Win64/bin/server.crt");
    23. fileServCrt.open(QIODevice::ReadOnly);
    24. const QSslCertificate cACertificate(&fileServCrt, QSsl::Pem);
    25. //将服务证书加入到CA列表中
    26. caCerList << cACertificate;
    27. config.setCaCertificates(caCerList);
    28. //网络连接管路
    29. QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    30. QUrl url(QUrl("https://10.0.1.160:443/static/SSD/20220621/123.txt"));
    31. QNetworkRequest request(url);
    32. //加入ssl配置信息
    33. request.setSslConfiguration(config);
    34. //设置rest api 数据内容为json格式
    35. request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    36. connect(manager, &QNetworkAccessManager::finished, this, [](QNetworkReply* reply){
    37. QByteArray byteArray = reply->readAll();
    38. qDebug()<<"finished:"<<reply->error()<<byteArray.length()<<byteArray;
    39. QSslCertificate cert = reply->sslConfiguration().peerCertificate();
    40. qDebug()<<"打印服务器证书:";
    41. qDebug()<<cert.isNull();
    42. qDebug()<<cert.issuerDisplayName();
    43. qDebug()<<cert.subjectDisplayName();
    44. qDebug()<<cert.version();
    45. qDebug()<<"serialNumber"<<cert.serialNumber();
    46. qDebug()<<cert.issuerInfo(QSslCertificate::Organization);
    47. qDebug()<<"CommonName: "<<cert.issuerInfo(QSslCertificate::CommonName);
    48. qDebug()<<cert.issuerInfo(QSslCertificate::LocalityName);
    49. qDebug()<<cert.issuerInfo(QSslCertificate::OrganizationalUnitName);
    50. qDebug()<<cert.issuerInfo(QSslCertificate::CountryName);
    51. qDebug()<<cert.issuerInfo(QSslCertificate::StateOrProvinceName);
    52. qDebug()<<cert.issuerInfo(QSslCertificate::DistinguishedNameQualifier);
    53. qDebug()<<cert.issuerInfo(QSslCertificate::SerialNumber);
    54. qDebug()<<cert.issuerInfo(QSslCertificate::EmailAddress);
    55. qDebug()<<cert.subjectInfo(QSslCertificate::Organization);
    56. qDebug()<<"CommonName: "<<cert.subjectInfo(QSslCertificate::CommonName);
    57. qDebug()<<cert.subjectInfo(QSslCertificate::LocalityName);
    58. qDebug()<<cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
    59. qDebug()<<cert.subjectInfo(QSslCertificate::CountryName);
    60. qDebug()<<cert.subjectInfo(QSslCertificate::StateOrProvinceName);
    61. qDebug()<<cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier);
    62. qDebug()<<cert.subjectInfo(QSslCertificate::SerialNumber);
    63. qDebug()<<cert.subjectInfo(QSslCertificate::EmailAddress);
    64. qDebug()<<cert.effectiveDate();
    65. qDebug()<<cert.expiryDate();
    66. });
    67. connect(manager, &QNetworkAccessManager::sslErrors, this, [](QNetworkReply*reply,const QList<QSslError>& errors){
    68. qDebug()<<"sslErrors:"<<errors;
    69. });
    70. manager->get(request);

    总结

    如果CA证书和服务器证书不匹配,则报错:
    sslErrors: ("The certificate is self-signed, and untrusted")


    如果客户端证书与服务器绑定的客户端证书不匹配,则报错:
    QNetworkReply::UnknownNetworkError

  • 相关阅读:
    做自媒体如何获得更多的流量?
    「UI开发」DevExpress WPF Pivot Grid组件可轻松实现多维数据分析!(一)
    RK主机 CPU、NPU使用频率查看和设置
    spi个人笔记
    volatile使用方法
    (C++版)ROS2发布者节点终极版--面向过程设计款式
    全网最全Java快捷键~
    跟我学时序分析之基础时序参数
    绝地求生大吃鸡攻略,让你成为顶级战士!
    区分axios在开发环境和生产环境的请求基础地址
  • 原文地址:https://blog.csdn.net/qq_37997682/article/details/125472654