cpprestsdk https双向认证小测

概述

因项目需要在系统中引入https双向认证,由于程序使用C/C++和cpprestsdk库编写,从网上经过一顿检索折腾,总算测试通过,故而博文记录用以备忘。

系统环境

Ubuntu 22.04.3 LTS
libcpprest-dev(jammy,now 2.10.18-1build2 amd64)

步骤

  1. 参考自签名根证书、中间证书、服务器证书生成流程详解,生成根证书、服务器证书以及客户端证书,然后用根证书对服务器、客户端的证书进行签名
  2. 编写代码,具体代码看后面
  3. 测试
    测试可以使用curl
    curl命令
curl -v -k --cacert root_cert.pem --cert client_cert.pem --key client_key.pem https://ip:port/

注意:
使用自签名证书的时候,服务器端的证书Common Name字段一定要填写对应的IP,不然会出现SSL握手失败的情况。

服务端代码

#include <cpprest/http_listener.h>
#include <cpprest/json.h>
#include <string>

using namespace std;
using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;
namespace net = boost::asio;
namespace ssl = net::ssl;

bool verify_callback(bool preverified, boost::asio::ssl::verify_context& ctx)
{
	char subject_name[256];
	X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
	X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
	std::cout << "subject_name:" << subject_name << std::endl;
	return true;
}

int main(int argc, char *argv[]) {
    string host = "192.168.25.112";
    int port = 6000;

    string url = "https://" + host + ":" + to_string(port);
    http_listener_config conf;
    string cert = "./certs/server_cert.pem";
    string privkey = "./certs/server_key.pem";
    string rootcert = "./certs/root_cert.pem";
    conf.set_ssl_context_callback([&cert, &privkey, &rootcert](ssl::context &ctx) {
    try {
        ctx.set_options(ssl::context::default_workarounds |
                        ssl::context::no_sslv2 | ssl::context::no_tlsv1 |
                        ssl::context::no_tlsv1_1 | ssl::context::single_dh_use);

        //设置自签名的根证书
        ctx.load_verify_file(rootcert);
        //设置根证书签发的服务器证书
        ctx.use_certificate_chain_file(cert);
        //设置服务器证书的私钥
        ctx.use_private_key_file(privkey, ssl::context::pem);
        //设置验证模式 - 验证对端证书
        // boost::asio::ssl::verify_fail_if_no_peer_cert 这个必须设置,不然对端不发送证书的时候,服务器是默认通过
        ctx.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
        //设置认证回调,具体作用,没太搞懂
        ctx.set_verify_callback(verify_callback);
        //私钥有密码的情况,通过该回调返回密码
        //ctx.set_password_callback([]() { return "PASSWORD";});
    } catch (exception const &e) {
        clog << "ERROR: " << e.what() << endl;
    }
    });
    http_listener listener = http_listener(utility::conversions::to_string_t(url), conf);
    listener.support(methods::GET, [](web::http::http_request request) {
        request.reply(status_codes::OK,U("hello world"));
    });
    listener.open().wait();

    while(true) {
        sleep(100);
    }
    cout << "Listening for requests at: " << host << ":" << port << endl;
}

客户端代码

#define _TURN_OFF_PLATFORM_STRING
#include <cpprest/http_client.h>
#include <cpprest/json.h>
#include <string>

using namespace std;
using namespace web;
using namespace web::http;
using namespace web::http::client;
namespace net = boost::asio;
namespace ssl = net::ssl;

int main(int argc, char *argv[])
{
    string host = "192.168.25.112";
    int port = 6000;
    string url = "https://" + host + ":" + (to_string(port));
    string rootcert = "./certs/root_cert.pem";
    string privkey = "./certs/client_key.pem";
    string cert = "./certs/client_cert.pem";
    http_client_config conf;

    try
    {
        conf.set_ssl_context_callback([&cert, &privkey, &rootcert](boost::asio::ssl::context &ctx)
                                      {
                                          // 加载根证书
                                          ctx.load_verify_file(rootcert);
                                          // 加载根证书签发的客户端证书
                                          ctx.use_certificate_chain_file(cert);
                                          // 加载客户端证书的私钥
                                          ctx.use_private_key_file(privkey, ssl::context::pem);
                                          // 设置验证模式 - 验证对端
                                          ctx.set_verify_mode(boost::asio::ssl::verify_peer);
                                          // 私钥有密码的情况,通过该回调返回密码
                                          // ctx.set_password_callback([]() { return "PASSWORD";});
                                      });

        http_client client(uri_builder(url).to_uri(), conf);

        http_response response = client.request(methods::GET, "/").get();
        // Check the status code.
        if (response.status_code() != 200)
        {
            throw std::runtime_error("Returned " + std::to_string(response.status_code()));
        }

        // Read the response body as a string.
        auto response_body = response.extract_string().get();

        // Output the response body for debugging.
        std::cout << "Response Body: " << response_body << std::endl;
    }
    catch (const std::runtime_error &e)
    {
        std::cerr << "Exception caught: " << e.what() << __FILE__ << __FUNCTION__ << __LINE__ << std::endl;
    }
    catch (std::exception const &e)
    {
        clog << "ERROR: " << e.what() << endl;
    }
}

注意:
以上代码只是在自签名证书下验证过,对于正式环境签发的证书,请自行尝试。

Q&A

Q: SSL握手返回,alert number 80
A: 我不知道咋出现的,只有服务器证书的Common Name不一致的情况,该问题会出现


Q: SSL握手返回, Unknown CA
A: 通过ctx.load_verify_file(rootcert);加载根证书即可

参考链接

自签名根证书、中间证书、服务器证书生成流程详解
HTTPS client server in unix
联盟链系列 - Https双向验证
基于boost和QT 实现客户端/服务端TCP双向证书认证与SSL加密
https双向认证

相关推荐

  1. cpprestsdk https双向认证

    2024-06-07 23:20:07       11 阅读
  2. HTTPS双向认证

    2024-06-07 23:20:07       45 阅读
  3. kingbase配置SSL双向认证

    2024-06-07 23:20:07       29 阅读
  4. 基于openssl实现TCP双向认证

    2024-06-07 23:20:07       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-07 23:20:07       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-07 23:20:07       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-07 23:20:07       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-07 23:20:07       18 阅读

热门阅读

  1. Qt 中QList、QListIterator 、QMutableListIterator、QMap用法

    2024-06-07 23:20:07       10 阅读
  2. gitleb详细的搭建步骤

    2024-06-07 23:20:07       10 阅读
  3. Tomcat 启动闪退问题解决方法

    2024-06-07 23:20:07       8 阅读
  4. 负载均衡加权轮询算法

    2024-06-07 23:20:07       9 阅读
  5. Nginx 实战-03-nginx 负载均衡

    2024-06-07 23:20:07       7 阅读
  6. Spark大数据 Spark运行架构与原理

    2024-06-07 23:20:07       10 阅读
  7. cesium 之 flyTo、setView、lookat

    2024-06-07 23:20:07       8 阅读
  8. Python基础总结之functools.partial

    2024-06-07 23:20:07       11 阅读
  9. LeetCode hot100-64-Y

    2024-06-07 23:20:07       6 阅读
  10. Flink mongo & Kafka

    2024-06-07 23:20:07       6 阅读