1. 程式人生 > >使用libevhtp搭建HTTPS SERVER(單向驗證身份)

使用libevhtp搭建HTTPS SERVER(單向驗證身份)

本文主要介紹使用libevhtp搭建一個HTTPS SERVER的方法,非加密的HTTP SERVER搭建方法,請點選此處 (本文的構建環境繼承自該部落格)。

本文將針對“單向驗證身份”場景,介紹HTTPS SERVER的搭建方法。“雙向驗證身份”的場景,請點選此處

1. 單向驗證身份

一般的HTTPS服務都是隻需要客戶端驗證伺服器的身份就可以了。

例如我們想訪問銀行的網站,我們首先需要確認要訪問的這個網站確實是真實的銀行網站,而不是一個介面類似的、用來誘騙我們輸入銀行賬號和密碼的釣魚網站,這就是我們作為客戶端對銀行進行身份驗證的過程;而銀行網站並不需要通過SSL/TLS來驗證我們的身份,因為我們會通過在網頁裡輸入銀行賬號和密碼向伺服器展示我們的身份。

我們可以通過以下步驟搭建單向驗證身份的HTTPS SERVER。

1.1 生成伺服器的證書檔案

要搭建HTTPS伺服器,需要使用HTTPS伺服器的祕鑰檔案和證書檔案。

生成HTTPS伺服器的祕鑰檔案和證書檔案的步驟如下:

1. 生成祕鑰對(RSA)檔案:

openssl genrsa -out server-key.pem 2048

2. 生成身份證申請(CSR)檔案:

openssl req -new -key server-key.pem -out server-csr.pem -subj "/CN=192.168.213.133"

3. 簽署身份證(CRT)檔案:

openssl x509 -req -sha256 -days 365 -in server-csr.pem -signkey server-key.pem -out server-crt.pem

經過上面的3步,我們得到了HTTPS伺服器的祕鑰檔案server-key.pem和證書檔案server-crt.pem。

說明:

  • 在上面的例子中,用來簽署CSR檔案的私鑰和CSR裡的公鑰是一對兒,也就是說這是一個自簽名(self-sign)的例子。通常情況下,我們會用一個CA的私鑰來簽署一個CSR,在雙向驗證身份的博文中會有相關介紹。
  • 上述步驟中的引數需要根據實際情況進行調整,如HTTPS伺服器的訪問域名“/CN=192.168.213.133”。

1.2 呼叫libevhtp介面,構建HTTPS伺服器

呼叫libevhtp介面,編寫HTTPS伺服器,程式碼(https_server.cpp)如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <getopt.h>

#include "../log.h"
#include "internal.h"
#include "evhtp/evhtp.h"

#ifndef EVHTP_DISABLE_SSL
// 回撥方法的具體實現
static void http__callback_(evhtp_request_t* req, void* arg)
{
    evbuffer_add_reference(req->buffer_out, "foobar\n", 7, NULL, NULL);
    return evhtp_send_reply(req, EVHTP_RES_OK);
}

// 認證客戶端的相關資訊
static int ssl__x509_verify_(int ok, X509_STORE_CTX* store)
{
    char                 buf[256];
    X509               * err_cert;
    int                  err;
    int                  depth;
    SSL                * ssl;
    evhtp_connection_t * connection;
    evhtp_ssl_cfg_t    * ssl_cfg;

    err_cert   = X509_STORE_CTX_get_current_cert(store);
    err        = X509_STORE_CTX_get_error(store);
    depth      = X509_STORE_CTX_get_error_depth(store);
    ssl        = (SSL*)X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx());

    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);

    connection = (evhtp_connection_t*)SSL_get_app_data(ssl);
    ssl_cfg    = connection->htp->ssl_cfg;

    if (depth > ssl_cfg->verify_depth)
    {
        ok  = 0;
        err = X509_V_ERR_CERT_CHAIN_TOO_LONG;

        X509_STORE_CTX_set_error(store, err);
    }

    if (!ok)
    {
        log_error("SSL: verify error:num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf);
    }

    return ok;
}

// https server引數列舉值
enum {
    OPTARG_CERT = 1000,
    OPTARG_KEY,
    OPTARG_CA,
    OPTARG_CAPATH,
    OPTARG_CIPHERS,
    OPTARG_VERIFY_PEER,
    OPTARG_ENFORCE_PEER_CERT,
    OPTARG_VERIFY_DEPTH,
    OPTARG_ENABLE_CACHE,
    OPTARG_CACHE_TIMEOUT,
    OPTARG_CACHE_SIZE,
    OPTARG_CTX_TIMEOUT,
    OPTARG_ENABLE_PROTOCOL,
    OPTARG_DISABLE_PROTOCOL
};

// https server幫助資訊
static const char* help =
    "Usage %s [opts] <host>:<port>\n"
    "  -cert          <file> : Server PEM-encoded X.509 Certificate file\n"
    "  -key           <file> : Server PEM-encoded Private Key file\n"
    "  -ca            <file> : File of PEM-encoded Server CA Certificates\n"
    "  -capath        <path> : Directory of PEM-encoded CA Certificates for Client Auth\n"
    "  -ciphers        <str> : Accepted SSL Ciphers\n"
    "  -verify-peer          : Enable SSL client verification\n"
    "  -enforce-peer-cert    : Reject clients without a cert\n"
    "  -verify-depth     <n> : Maximum depth of CA Certificates in Client Certificate verification\n"
    "  -enable-protocol  <p> : Enable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n"
    "  -disable-protocol <p> : Disable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n"
    "  -ctx-timeout      <n> : SSL Session Timeout (SSL >= 1.0)\n";

// 獲取並解析使用者輸入的https server引數,配置evhtp的ssl資訊
evhtp_ssl_cfg_t *parse__ssl_opts_(int argc, char** argv)
{
    int opt = 0;
    int long_index = 0;
    int ssl_verify_mode = 0;
    struct stat f_stat;
    evhtp_ssl_cfg_t* ssl_config = (evhtp_ssl_cfg_t*)calloc(1, sizeof(evhtp_ssl_cfg_t));

    ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;

    static struct option long_options[] = {
        { "cert",              required_argument, 0, OPTARG_CERT              },
        { "key",               required_argument, 0, OPTARG_KEY               },
        { "ca",                required_argument, 0, OPTARG_CA                },
        { "capath",            required_argument, 0, OPTARG_CAPATH            },
        { "ciphers",           required_argument, 0, OPTARG_CIPHERS           },
        { "verify-peer",       no_argument,       0, OPTARG_VERIFY_PEER       },
        { "enforce-peer-cert", no_argument,       0, OPTARG_ENFORCE_PEER_CERT },
        { "verify-depth",      required_argument, 0, OPTARG_VERIFY_DEPTH      },
        { "enable-cache",      no_argument,       0, OPTARG_ENABLE_CACHE      },
        { "cache-timeout",     required_argument, 0, OPTARG_CACHE_TIMEOUT     },
        { "cache-size",        required_argument, 0, OPTARG_CACHE_SIZE        },
        { "enable-protocol",   required_argument, 0, OPTARG_ENABLE_PROTOCOL   },
        { "disable-protocol",  required_argument, 0, OPTARG_DISABLE_PROTOCOL  },
        { "ctx-timeout",       required_argument, 0, OPTARG_CTX_TIMEOUT       },
        { "help",              no_argument,       0, 'h'                      },
        { NULL,                0,                 0, 0                        }
    };

    // 獲取使用者輸入的https server引數
    while ((opt = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) {
        switch (opt) {
            case 'h':
                printf(help, argv[0]);
                exit(EXIT_FAILURE);
            // 服務端證書檔案
            case OPTARG_CERT:
                ssl_config->pemfile         = strdup(optarg);
                break;
            // 服務端祕鑰檔案
            case OPTARG_KEY:
                ssl_config->privfile        = strdup(optarg);
                break;
            // 簽署伺服器證書檔案的CA證書檔案
            case OPTARG_CA:
                ssl_config->cafile          = strdup(optarg);
                break;
            case OPTARG_CAPATH:
                ssl_config->capath          = strdup(optarg);
                break;
            case OPTARG_CIPHERS:
                ssl_config->ciphers         = strdup(optarg);
                break;
            // 簽署客戶端證書檔案的CA證書檔案的最大深度
            case OPTARG_VERIFY_DEPTH:
                ssl_config->verify_depth = atoi(optarg);
                break;
            // 啟用對於客戶端的SSL認證
            case OPTARG_VERIFY_PEER:
                ssl_verify_mode            |= SSL_VERIFY_PEER;
                break;
            // 拒絕沒有證書檔案的客戶端的連線
            case OPTARG_ENFORCE_PEER_CERT:
                ssl_verify_mode            |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
                break;
            case OPTARG_ENABLE_CACHE:
                ssl_config->scache_type     = evhtp_ssl_scache_type_internal;
                break;
            case OPTARG_CACHE_TIMEOUT:
                ssl_config->scache_timeout  = atoi(optarg);
                break;
            case OPTARG_CACHE_SIZE:
                ssl_config->scache_size     = atoi(optarg);
                break;
            case OPTARG_CTX_TIMEOUT:
                ssl_config->ssl_ctx_timeout = atoi(optarg);
                break;
            case OPTARG_ENABLE_PROTOCOL:
                if (!strcasecmp(optarg, "SSLv2"))
                {
                    ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv2;
                }
                else if (!strcasecmp(optarg, "SSLv3"))
                {
                    ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv3;
                }
                else if (!strcasecmp(optarg, "TLSv1"))
                {
                    ssl_config->ssl_opts &= ~SSL_OP_NO_TLSv1;
                }
                else if (!strcasecmp(optarg, "ALL"))
                {
                    ssl_config->ssl_opts = 0;
                }

                break;
            case OPTARG_DISABLE_PROTOCOL:
                if (!strcasecmp(optarg, "SSLv2"))
                {
                    ssl_config->ssl_opts |= SSL_OP_NO_SSLv2;
                }
                else if (!strcasecmp(optarg, "SSLv3"))
                {
                    ssl_config->ssl_opts |= SSL_OP_NO_SSLv3;
                }
                else if (!strcasecmp(optarg, "TLSv1"))
                {
                    ssl_config->ssl_opts |= SSL_OP_NO_TLSv1;
                }
                else if (!strcasecmp(optarg, "ALL"))
                {
                    ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
                }
                break;

            default:
                break;
        } /* switch */
    }

    // 需要對客戶端進行認證的情況
    if (ssl_verify_mode != 0)
    {
        ssl_config->verify_peer    = ssl_verify_mode;
        ssl_config->x509_verify_cb = ssl__x509_verify_;
    }

    // 檢查服務端證書檔案
    if (ssl_config->pemfile)
    {
        if (stat(ssl_config->pemfile, &f_stat) != 0)
        {
            log_error("Cannot load SSL cert '%s' (%s)", ssl_config->pemfile, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    // 檢查服務端祕鑰檔案
    if (ssl_config->privfile)
    {
        if (stat(ssl_config->privfile, &f_stat) != 0)
        {
            log_error("Cannot load SSL key '%s' (%s)", ssl_config->privfile, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    // 檢查簽署伺服器證書檔案的CA證書檔案
    if (ssl_config->cafile)
    {
        if (stat(ssl_config->cafile, &f_stat) != 0)
        {
            log_error("Cannot find SSL CA File '%s' (%s)", ssl_config->cafile, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    if (ssl_config->capath) {
        if (stat(ssl_config->capath, &f_stat) != 0) {
            log_error("Cannot find SSL CA PATH '%s' (%s)", ssl_config->capath, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    return ssl_config;
}
#endif

int main(int argc, char **argv)
{
#ifndef EVHTP_DISABLE_SSL
    struct event_base *evbase = event_base_new();
    evhtp_alloc_assert(evbase);
    evhtp_t *htp    = evhtp_new(evbase, NULL);
    evhtp_alloc_assert(htp);

    // 根據使用者輸入的https server引數,初始化evhtp的ssl相關配置
    evhtp_ssl_init(htp, parse__ssl_opts_(argc, argv));
    // 設定回撥函式
    evhtp_set_gencb(htp, http__callback_, NULL);
    // 監聽本機IP"192.168.213.133"的4443埠, backlog為128
    evhtp_bind_socket(htp, "192.168.213.133", 4443, 128);
    log_info("[https server info] https://192.168.213.133:4443/");

    /* 進入迴圈、監聽連線,http server開始工作 */
    event_base_loop(evbase, 0);
    return 0;
#else
    log_error("Not compiled with SSL support, go away");
    return EXIT_FAILURE;
#endif
}

編譯生成HTTPS伺服器程式,命令如下:

g++ -o https_server https_server.cpp -I/opt/liitdar/libevhtp/libevhtp-1.2.15/include/ -L/opt/liitdar/libevhtp/libevhtp-1.2.15/build/  -levhtp -levent -lpthread -levent_openssl -lssl -lcrypto

說明:

  • 需要根據實際情況設定標頭檔案路徑,並在標頭檔案路徑中新增需要的標頭檔案;
  • 需要根據實際情況設定靜態庫 libevhtp.a 的地址;
  • 因為libevhtp的靜態庫 libevhtp.a 中包含了一些ssl的內容,所以在我們使用該靜態庫時,也需要連線一下ssl的相關庫。

1.3 測試HTTPS伺服器

開啟一個終端,執行剛剛生成的HTTPS伺服器程式,如下:

./https_server -cert ssl/server-crt.pem -key ssl/server-key.pem

在客戶端機器上,開啟一個終端,使用curl命令測試HTTPS伺服器,如下:

curl https://192.168.213.133:4443/ --cacert ssl/server-crt.pem

說明:上述命令使用的HTTPS伺服器的證書和祕鑰是在本文前面生成的。

上面的curl命令的執行結果如下:


如果客戶端的終端中出現上述資訊,說明單向身份驗證的HTTPS伺服器部署成功了。

相關推薦

使用libevhtp搭建HTTPS SERVER單向驗證身份

本文主要介紹使用libevhtp搭建一個HTTPS SERVER的方法,非加密的HTTP SERVER搭建方法,請點選此處 (本文的構建環境繼承自該部落格)。本文將針對“單向驗證身份”場景,介紹HTTPS SERVER的搭建方法。“雙向驗證身份”的場景,請點選此處。1. 單向

使用libevhtp搭建HTTPS SERVER雙向驗證身份

本文主要介紹使用libevhtp搭建一個HTTPS SERVER的方法,非加密的HTTP SERVER搭建方法,請點選此處 (本文的構建環境繼承自該部落格)。本文將針對“雙向驗證身份”場景,介紹HTTPS SERVER的搭建方法。“單向驗證身份”的場景,請點選此處。1. 雙向

團隊合作---使用Gitblit在Windows下搭建Git ServerHTTP

用過Git之後再使用Svn是很頭疼的事情 於是就讓向老大要了一塊Windows伺服器的地址空間,來搭建Git Server 一、下載Gitblit 二、下載完解壓 (1)解壓之後我的gitblit檔案的路徑: D:\gitblitserver\gitb

【Pytorch-入門】windows下的環境搭建驗證成功~

自動 檢查 create span -- nump .cn otl type 前言 實驗需要,之前使的tensorflow【因為自己手邊的服務器都是windows環境TT...】,但身邊的師兄們用的都是pytorch,自己查了查現在做科研基本上都是用的pytorch,而且

小程式server- 搭建HTTPS server

一.使用 Node 和 Express 搭建一個 HTTP 伺服器 1.在app.js修改小程式通訊域名 App({ config: { host: '' // 這個地方填寫你的域名 }, onLaunch () {

golang https server

之前的blog介紹了https的單向認證流程,這裡再介紹一下雙向認證的過程。人多人沒有理解雙向認證的過程,這裡先介紹一下認證流程 這裡的ca證書其實是可以不一樣的,這是很多人的誤區,服務端證書用服務端的ca簽名過後,客戶端應該用服務的ca去認證,而不是客戶

移動端搭建Http Server—— 實現URL路由模組

在前面幾篇文章中已經實現了移動端Server的關鍵前兩步:監聽遠端連線和解析HTTP Headers中的資料,本文將要設計一下路由規則 1.URL路由規則 簡單來講就是客戶端請求一個URL,伺服器分發給哪個服務來處理 移動端Server要實現兩個功能:

使用虛擬機器VM從零搭建Linux serverJdk1.6+tomcat7+MySQL5.5

筆者是在win7系統上安裝了一臺虛擬機器VMware6,然後再在虛擬機器上用映象檔案安裝了Linux redhat-5-server-i386,最後在此Linux系統上 完成了 Jdk,tomcat,MySQL的安裝。 - 1、虛擬機器VMware6的安裝

Nodejs 搭建https伺服器

$ cd /Users/51testing/Desktop/https   $ express HttpsService ....    install dependencies:       $ cd HttpsService && npm insta

php通過curl擴展進行模擬登錄驗證

程序 valid 做的 .cn ica ews fclose har user 以下為本人工作中遇到的需要做的事情,之前也沒怎麽用過curl,查了好多資料,才稍微弄明白一點;本文所有內容只是自己平日工作的記錄,僅供大家參考:<?php/*** 模擬登錄*/head

圖像識別練習flash驗證

探討 野比 2012由於破解可能給他人帶來困擾,所以我只說大概的思路,不會提供源碼。-----------------本次目標是www.iboling.com的flash驗證碼。這個網站很新穎,用的是flash動畫,隨機給出大小球,然後讓用戶用鼠標點擊相應的球,實現驗證碼輸入。像這樣本來這是個很不錯的思路,跳

50行python代碼實現個代理server你懂的

try sel -m 轉發 size sso ddr bin input 之前遇到一個場景是這種: 我在自己的電腦上須要用mongodb圖形client,可是mongodb的server地址沒有對外網開放,僅僅能通過先登錄主機A,然後再從A連接mongodbserver

新手玩個人server阿裏雲續二

ria pause 管理 debug timeout yum directory eth http 小二班一番廝殺:那英四強誕生:大家閨秀,小家碧玉。窈窕淑女,妍姿俊俏 。不解釋!?不行!陳冰,李嘉格,劉明湘。張碧晨。大多數的時候,僅僅要

【Quartz】Quartz的搭建、應用單獨使用Quartz

文章 sgd aca guide mfc uci strong div guid 原文:http://www.cnblogs.com/nick-huang/p/4848843.html 目錄 1. > 參考的優秀資料 2. > 版本說明 3. > 簡單的

ZooKeeper動態增加Server動態增加節點的研究待實踐

leader linux nod 部署 them user ase 通過 增加節點 說明:是動態增加Server,不是動態增加連接到ZK Server的Client。 場景如下(轉自外文): 1、在t=t_1->[peer-1(Leader),peer-2],pe

在centos上搭建JavaWeb環境jdk+mysql+tomcat

pass .tar.gz wget https ins img min span ice 1.安裝OpenJDK yum list java* yum install java-1.7.0-openjdk* -y java -version 2.安裝Tomcat cd

SMM框架的搭建和測試Spring MVC+MyBatis

meta true model spa const -i ssp pan div Spring MVC:MVC框架,通過Model-View-Controller模式很好的將數據,業務與展現進行分離。 MyBatis:數據持久層框架 我這裏使用的是MyEclipse 2

SSH框架的搭建和測試Spring + Struts2 + Hibernate

conf work fault 項目 -i extends struts2 scrip map SSH框架實現了視圖、控制器、和模型的徹底分離,同時還實現了業務邏輯層與持久層的分離。 Spring實現了MVC中的 Controller的功能,Struts實現Web視圖的功能

java利用myeclipse自帶三大框架搭建三大框架Hibernate+Struts2+Spring過程詳解

sun 過程 9.png att alt 分享圖片 struts apach sch 搭建過程因人而異,我的搭建過程大致是這樣的:   1.創建一個javaweb項目;   2.導入Spring框架,上圖:     2.1:     2.2:     2.3:   3.

Tomcat集群搭建超詳細apache+mod_jk+tomcat

detect orm 這一 parallel fault nsh 軟件包 .so 信息 TOMCAT集群 目錄 TOMCAT集群 1 1 集群 1 1.1 什麽是集群 1 1.2 集群的特性 1 1.3 集群的分類 1 1.4 TOMCAT集群配置的優缺點 2 1.5 AP