Libcurl的编译_HTTP/HTTPS客户端源码示例

2023-05-16

HTTP/HTTPS客户端源码示例

环境:  zlib-1.2.8  openssl-1.0.1g  curl-7.36

Author:  Kagula

LastUpdateDate: 2016-05-09

阅读前提:CMake工具的基本使用、配置openssl-1.0.1g 开发环境

 

编译zlib库

         下载zlib-1.2.8.tar.gz并解压缩到" D:\SDK\zlib-1.2.8",使用CMake工具生成zlib.sln,在Visual Studio2013中打开并编译即可。

编译curl-7.36.0

    假设Open SSL已经安装到“D:\SDK\openssl-1.0.1g”,先设置下面的环境变量

OPENSSL_LIBRARIES=D:\SDK\openssl-1.0.1g\out32

OPENSSL_ROOT_DIR=D:\SDK\openssl-1.0.1g

    从http://curl.haxx.se/下载curl-7.36.0.zip并解压缩到“D:\SDK\curl-7.36.0”启动CMake工具Configure,分别设置LIB_EAY_RELEASESSL_EAY_RELEASE变量为“D:\SDK\openssl-1.0.1g\out32\libeay32.lib”,“D:\SDK\openssl-1.0.1g\out32\ssleay32.lib”,产生sln文件后打开,为里面的curl工程项目添加“USE_MANUAL”宏定义,然后build里面的4个项目成功。

   为项目添加链接库libcurl_imp.lib  , 把libcurl.dll文件复制到C++项目路径下,否则程序运行会提示找不到动态链接库。

下面是HTTP/HTTPS客户端示例


如何使用

#include <iostream>
#include <string>

using namespace std;

#include "httpclient.h"

int main(int argc, char *argv[])
{
    string response;

    //login
    kagula::network::CHttpClient client;
    int nR = client.Post("https://lijun:8443/escortcashbox/main/login.do",
                         "data={\"version\":\"1.0.0.0\",\"user\":\"admin\",\"password\":\"123\"}",                         
                         response,true,
                         "d:\cookie.txt",
                         "D:/workspace_qt/build-escortcashbox_c-Mingw32-Debug/debug/tomcat7.pem",
                         "D:/workspace_qt/build-escortcashbox_c-Mingw32-Debug/debug/client_all.pem",
                         "123456");
    cout << nR << endl <<response << endl;

    //upload png file
    /*
    std::map<std::string,std::string> mapFields;
    std::map<std::string,std::vector<std::string>> mapFiles;
    mapFields["data"] = "{\"version\":\"1.0.0.0\",\"socialid\":\"1012\",\"realname\":\"realnamevalue\",\"portrait\":\"protraitvalue\",\"fingerid\":\"fingeridvalue\",\"groupid\":\"123\"}";
    std::vector<std::string> vecFile(2);
    vecFile[0] = "d:\\b.png";
    vecFile[1] = "image/png";
    mapFiles["portraitFile"] = vecFile;
    nR = client.MultipartFormdata("https://lijun:8443/escortcashbox/main/escortStaffAdd.do",
                                  mapFields,mapFiles,
                                  response,
                                  "d:\cookie.txt",
                                  "D:/workspace_qt/build-escortcashbox_c-Mingw32-Debug/debug/tomcat7.pem",
                                  "D:/workspace_qt/build-escortcashbox_c-Mingw32-Debug/debug/client_all.pem",
                                  "123456");

    cout << nR << endl << response << endl;
*/
    //download png file
    nR = client.GetFile("https://lijun:8443/escortcashbox/upload/img/20160527_110514_679_426.png",
                        "d:/ee.png",
                        "d:\cookie.txt",
                        "D:/workspace_qt/build-escortcashbox_c-Mingw32-Debug/debug/tomcat7.pem",
                        "D:/workspace_qt/build-escortcashbox_c-Mingw32-Debug/debug/client_all.pem",
                        "123456");
    cout << nR << endl;

    return 0;
}


HttpClient.h封装好的头文件

//HttpClient.h源代码清单
#ifndef _HTTPCLIENT_H_
#define _HTTPCLIENT_H_

#include <string>
#include <map>
#include <vector>

/*
Title: Get Response from Web Server by HTTP/HTTPS method.
Environment:
Windows 7SP1, Windows 8.1, Windows 10
QT Creator 3.5.1, Visual Studio 2013 Update1, Visual Studio 2013 Update5
libcurl 7.36.0, libcurl 7.46.0, Qt 5.6, MSYS2 64bits gcc 5.3.0
Last Update: 2016-05-27
Remark:
[1]如果要在多线程方式下同时调用多个CHttpClient实例,
 需要在App初始化的时候调用kagula::network::Init();
 在App结束的时候调用kagula::network::Cleanup();
[2]编译libcurl必须打开zlib标志,并且把OpenSSL也链进去。

Reference:
curl_eay_setopt manual
http://www.helplib.net/s/linux.die/65_2740/man-3-curl-easy-setopt.shtml
C++ cout format
http://www.cnblogs.com/devymex/archive/2010/09/06/1818754.html
*/

namespace kagula
{
    namespace network
    {
        void Init();
        void Cleanup();

        class CHttpClient
        {
        public:
            CHttpClient(void);
            ~CHttpClient(void);

        public:
            /**
            * @brief HTTP/HTTPS POST/GET请求
            * @param strUrl 输入参数,请求的Url地址,如:https://www.alipay.com
            * @param strPost 输入参数,使用如下格式para1=val1¶2=val2&…
            * @param strCookie 输入参数,Cookie文件名,例如 d:\temp\cookie.txt
            *                          如果为空,不启用Cookie.
            * @param strResponse 输出参数,返回的内容
            * @param bPost 是否Post方式发送请求,默认Post方式发送请求。
            * @param pCaPath 输入参数,为CA证书的路径.如果输入为NULL,则不验证服务器端证书的有效性.
            * @param pClientCalPath 输入参数,为客户端证书的路径.如果输入为NULL,则不验证客户端证书的有效性.
            * @param pClientCalPassword 输入参数,为客户端证书的存取密码.
            * @return 返回是否Post成功
            * 0  成功
            * 7  无法连接
            * 28 超时
            * 58 服务端验证客户端证书失败。
            * 60 客户端验证服务端证书失败。
            */
            int Post(const char* pUrl,
                     const char* pPost,
                     std::string &strResponse,
                     bool bPost,
                     const char* pCookie,
                     const char* pCaPath = NULL,
                     const char* pClientCalPath = NULL,
                     const char* pClientCalPassword = NULL);

            int MultipartFormdata(const char *pUrl,
                                  const std::map<std::string,std::string> & mapFields,
                                  const std::map<std::string,std::vector<std::string>> & mapFiles,
                                  std::string & strResponse,
                                  const char *pCookie,
                                  const char * pCaPath = NULL,
                                  const char * pClientCalPath = NULL,
                                  const char * pClientCalPassword = NULL);

            int GetFile(const char* pUrl,
                        const char* pLocalFullPath,
                        const char* pCookie,
                        const char* pCaPath = NULL,
                        const char* pClientCalPath = NULL,
                        const char* pClientCalPassword = NULL);
        public:
            void SetDebug(bool bDebug);
            std::string getMsgInChinese(int code);

        private:
            bool m_bDebug;

            bool PrintCookies(void* curl, std::string& strOut);
        };
    }
}
#endif


源文件清单

//HttpClient.cpp源代码清单
#include "HttpClient.h"
#include <iostream>
#include <curl/curl.h>
#include <iomanip>
#include <sstream>

#ifdef WIN32
#pragma comment(lib,"libcurl_imp.lib")
#endif

namespace kagula
{
    namespace network
    {
        CHttpClient::CHttpClient(void) :
            m_bDebug(false)
        {

        }

        CHttpClient::~CHttpClient(void)
        {

        }

        bool CHttpClient::PrintCookies(void* curl, std::string& strOut)
        {
            std::ostringstream ostr;

            CURLcode res;
            struct curl_slist *cookies;

            res = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies);
            if (res != CURLE_OK) {

                ostr << "Curl curl_easy_getinfo failed:" << curl_easy_strerror(res) << std::endl;
                strOut = ostr.str();
                return false;
            }
            const struct curl_slist *nc = cookies;
            int i = 1;
            ostr << "Cookies, curl knows:" << std::endl;
            while (nc) {
                ostr << "[" << i++ << "]: " << nc->data << std::endl;
                nc = nc->next;
            }

            return true;
        }

        static int OnDebug(CURL *, curl_infotype itype, char * pData, size_t size, void *)
        {
            if (itype == CURLINFO_TEXT)
            {
                //printf("[TEXT]%s\n", pData);
            }
            else if (itype == CURLINFO_HEADER_IN)
            {
                printf("[HEADER_IN]%s\n", pData);
            }
            else if (itype == CURLINFO_HEADER_OUT)
            {
                printf("[HEADER_OUT]%s\n", pData);
            }
            else if (itype == CURLINFO_DATA_IN)
            {
                printf("[DATA_IN]%s\n", pData);
            }
            else if (itype == CURLINFO_DATA_OUT)
            {
                printf("[DATA_OUT]%s\n", pData);
            }
            return 0;
        }

        size_t OnWriteData_Post(void* buffer, size_t size, size_t nmemb, void* lpVoid)
        {
            std::string* str = reinterpret_cast<std::string*>(lpVoid);
            if (NULL == str || NULL == buffer)
            {
                return -1;
            }

            char* pData = reinterpret_cast<char*>(buffer);
            str->append(pData, size * nmemb);
            return nmemb;
        }

        size_t OnWriteData_MultipartFormdata( void *inBuffer, size_t size, size_t nmemb, void *outBuffer )
        {
            int len = size * nmemb;
            char *temp = new char[len+1];
            memcpy(temp,inBuffer,len);
            temp[len]=0;
            reinterpret_cast<std::string *>(outBuffer)->append(temp);
            delete temp;
            return len;
        }

        size_t OnWriteData_GetFile(void *inBuffer, int size, int nmemb, std::string &content)
        {
            long len = size * nmemb;
            std::string temp((char *)inBuffer, len);
            content += temp;
            return len;
        }

        std::string CHttpClient::getMsgInChinese(int code)
        {
            switch(code)
            {
            case 0:
                return "通讯成功";
            case 7:
                return "服务器连接失败。";
            case 28:
                return "连接超时。";
            case 58:
                return "服务端验证客户端证书失败。";
            case 60:
                return "客户端验证服务端证书失败。";
            default:
                return "";
            }
        }

        int CHttpClient::Post(const char* pUrl,
                              const char* pPost,
                              std::string & strResponse,
                              bool bPost,
                              const char* pCookie,
                              const char* pCaPath,
                              const char* pClientCalPath,
                              const char* pClientCalPassword)
        {
            strResponse = "";


            CURLcode res;
            CURL* curl = curl_easy_init();
            if (NULL == curl)
            {
                return CURLE_FAILED_INIT;
            }
            if (m_bDebug)
            {
                curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
                curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);
            }
            curl_easy_setopt(curl, CURLOPT_URL, pUrl);
            if(bPost)
            {
                curl_easy_setopt(curl, CURLOPT_POST, 1);
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, pPost);
            }
            curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData_Post);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
            curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);

            if (pCookie!=0)
            {
                curl_easy_setopt(curl, CURLOPT_COOKIEFILE, (void *)pCookie);
                curl_easy_setopt(curl, CURLOPT_COOKIEJAR, (void *)pCookie);
            }

            if (NULL == pCaPath)
            {
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);

                //需要在编译curl的时候,一同编译zlib标志。要不然找不到这个标志.
                //当然前提是你已经编译完成zlib.
                //发出接受gzip压缩内容的请求,如果服务器支持gzip内容,会返回压缩后的数据。
                //如果Http服务器不支持gzip encoding也不影响libcurl正常工作。
                //接受数据的时候,如果返回的是压缩数据,libcurl会自动解压数据。
                curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip");
            }
            else
            {
                //缺省情况就是PEM,所以无需设置,另外支持DER
                //curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE,"PEM");
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
                curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);

                //服务端需要认证客户端的真实性,即双向认证。
                if(pClientCalPath!=NULL)
                {
                    curl_easy_setopt(curl,CURLOPT_SSLCERT, pClientCalPath);
                    curl_easy_setopt(curl,CURLOPT_SSLCERTPASSWD, pClientCalPassword);
                    curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE, "PEM");
                    curl_easy_setopt(curl,CURLOPT_SSLKEY, pClientCalPath);
                    curl_easy_setopt(curl,CURLOPT_SSLKEYPASSWD, pClientCalPassword);
                    curl_easy_setopt(curl,CURLOPT_SSLKEYTYPE, "PEM");
                }
            }            
            //
            curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
            curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);

            //Web服务器一般会重定向链接,比如访问http:/xxx/x1.do自动转到http:/xxx/x2.do
            //所以一定要设置CURLOPT_FOLLOWLOCATION为1,否则重定向后的数据不会返回。
            curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,1);

            res = curl_easy_perform(curl);
            curl_easy_cleanup(curl);
            return res;
        }


        int CHttpClient::MultipartFormdata(const char *pUrl,
                              const std::map<std::string,std::string> & mapFields,
                              const std::map<std::string,std::vector<std::string>> & mapFiles,
                              std::string & strResponse,
                              const char *pCookie,const char * pCaPath,
                              const char * pClientCalPath,const char * pClientCalPassword)
        {
            CURL *curl;
            CURLcode res;

            curl = curl_easy_init();
            strResponse ="";

            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData_MultipartFormdata);//write_data
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strResponse);

            struct curl_httppost *formpost = 0;
            struct curl_httppost *lastptr  = 0;

            //
            std::map<std::string,std::string>::const_iterator iterFields = mapFields.begin();
            while(iterFields!=mapFields.end())
            {
                //curl_formadd(&formpost, &lastptr,CURLFORM_COPYNAME,"data",CURLFORM_COPYCONTENTS, pData,CURLFORM_END);
                curl_formadd(&formpost, &lastptr,CURLFORM_COPYNAME,iterFields->first.c_str(),
                             CURLFORM_COPYCONTENTS, iterFields->second.c_str(),CURLFORM_END);
                iterFields++;
            }

            std::map<std::string,std::vector<std::string>>::const_iterator iterFiles = mapFiles.begin();
            while(iterFiles!=mapFiles.end())
            {
                //"image/jpeg","image/png"
                //curl_formadd(&formpost, &lastptr,CURLFORM_PTRNAME, "portraitFile", CURLFORM_FILE, pImageFileName,CURLFORM_CONTENTTYPE, "image/png", CURLFORM_END);
                curl_formadd(&formpost, &lastptr,CURLFORM_PTRNAME,
                             iterFiles->first.c_str(), CURLFORM_FILE,
                             iterFiles->second[0].c_str(),CURLFORM_CONTENTTYPE,
                             iterFiles->second[1].c_str(), CURLFORM_END);
                iterFiles++;
            }


            //
            curl_easy_setopt(curl, CURLOPT_URL, pUrl);
            curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);

            if (pCookie!=0)
            {
                curl_easy_setopt(curl, CURLOPT_COOKIEFILE, (void *)pCookie);
                curl_easy_setopt(curl, CURLOPT_COOKIEJAR, (void *)pCookie);
            }

            //单向认证用
            if(pCaPath!=0)
            {
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
                curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
            }

            //服务端需要认证客户端的真实性,即双向认证。
            if(pClientCalPath!=0 && pClientCalPassword!=0)
            {
                curl_easy_setopt(curl,CURLOPT_SSLCERT, pClientCalPath);
                curl_easy_setopt(curl,CURLOPT_SSLCERTPASSWD, pClientCalPassword);
                curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE, "PEM");
                curl_easy_setopt(curl,CURLOPT_SSLKEY, pClientCalPath);
                curl_easy_setopt(curl,CURLOPT_SSLKEYPASSWD, pClientCalPassword);
                curl_easy_setopt(curl,CURLOPT_SSLKEYTYPE, "PEM");
            }

            res = curl_easy_perform(curl);
            curl_easy_cleanup(curl);

            curl_formfree(formpost);
            return res;
        }

        int CHttpClient::GetFile(const char* pUrl,
                    const char* pLocalFullPath,
                    const char* pCookie,
                    const char* pCaPath,
                    const char* pClientCalPath,
                    const char* pClientCalPassword)
        {
            CURL *curl = NULL;
            CURLcode code;
            char bufError[CURL_ERROR_SIZE];
            std::string content;
            long retcode = 0;

            code = curl_global_init(CURL_GLOBAL_DEFAULT);
            if (code != CURLE_OK)
            {
                //printf("Failed to global init default [%d]\n", code);
                return -100;
            }

            curl = curl_easy_init();
            if (curl == NULL)
            {
                //printf("Failed to create CURL connection\n");
                return -200;
            }
            code = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, bufError);
            if (code != CURLE_OK)
            {
                //printf("Failed to set error buffer [%d]\n", code);
                return code;
            }
            //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
            code = curl_easy_setopt(curl, CURLOPT_URL, pUrl);
            if (code != CURLE_OK)
            {
                //printf("Failed to set URL [%s]\n", error);
                goto _END;
            }
            code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
            if (code != CURLE_OK)
            {
                //printf("Failed to set redirect option [%s]\n", error);
                goto _END;
            }
            code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData_GetFile);
            if (code != CURLE_OK)
            {
                //printf("Failed to set writer [%s]\n", error);
                goto _END;
            }
            code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &content);
            if (code != CURLE_OK)
            {
                //printf("Failed to set write data [%s]\n", error);
                goto _END;
            }

            if (pCookie!=0)
            {
                curl_easy_setopt(curl, CURLOPT_COOKIEFILE, (void *)pCookie);
                curl_easy_setopt(curl, CURLOPT_COOKIEJAR, (void *)pCookie);
            }

            //单向认证用
            if(pCaPath!=0)
            {
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
                curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
            }

            //服务端需要认证客户端的真实性,即双向认证。
            if(pClientCalPath!=0 && pClientCalPassword!=0)
            {
                curl_easy_setopt(curl,CURLOPT_SSLCERT, pClientCalPath);
                curl_easy_setopt(curl,CURLOPT_SSLCERTPASSWD, pClientCalPassword);
                curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE, "PEM");
                curl_easy_setopt(curl,CURLOPT_SSLKEY, pClientCalPath);
                curl_easy_setopt(curl,CURLOPT_SSLKEYPASSWD, pClientCalPassword);
                curl_easy_setopt(curl,CURLOPT_SSLKEYTYPE, "PEM");
            }

            code = curl_easy_perform(curl);
            if (code != CURLE_OK)
            {
                //printf("Failed to get '%s' [%s]\n", URL, error);
                goto _END;
            }

            code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &retcode);
            if ((code == CURLE_OK) && retcode == 200)
            {
                double length = 0;
                code = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length);
                //printf("%d", retcode);
                FILE * file = fopen(pLocalFullPath, "wb");
                fseek(file, 0, SEEK_SET);
                fwrite(content.c_str(), 1, (size_t)length, file);
                fclose(file);

                code =  CURLE_OK;
                goto _END;
            }
        _END:
            curl_easy_cleanup(curl);

            return code;
        }

        ///
        void CHttpClient::SetDebug(bool bDebug)
        {
            m_bDebug = bDebug;
        }

        void Init()
        {
            //the function is not thread safe.
            curl_global_init(CURL_GLOBAL_ALL);
        }

        void Cleanup()
        {
            curl_global_cleanup();
        }
    }
}

注意:

[1]MSys64 Mingw32 QtCreator 32位C++程序 配置libcurl
假设Msys64是安装在C盘默认路径上
第一步:在Msys64中查询可用的curl包
pacman -Ss curl


第二步:安装
pacman -S mingw-w64-i686-curl


第三步:在.pro文件下加入下面的代码
LIBS += C:\msys64\mingw32\lib\libcurl.dll.a
INCLUDEPATH += C:\msys64\mingw32\include
最后:编译链接依赖libcurl的项目成功。


[2]如何产生cer文件

https原理及tomcat配置https方法
http://jingyan.baidu.com/article/a948d6515d3e850a2dcd2ee6.html


[3]单向认证,让客户端信任服务端证书

第一步:
双击从服务端keystore中导出的cer文件可以导入证书。
windows下“certmgr.msc”命令可以进入证书管理。
自己制作的证书导入到Win7后默认在“中级证书颁发机构”->“证书”节点里。


第二步:
需要把你做的证书拖到“受信任的根证书颁发机构”->“证书”节点中去,否则
浏览器会提醒“此网站出具的安全证书不是由受信任的证书颁发机构颁发的”等类似错误。
chrome需要重启,IE直接刷新页面,就不会出现警告了。


注意:
数字证书转换cer---pem 
在Msys2 Shell中确保安装好openssl.
在Msys2 Shell中使用openssl命令后进入openssl提示符,输入下面的命令
x509 -inform der -in d:/MyServerSecurity/tomcat7.cer -out d:/MyServerSecurity/tomcat7.pem


[4]双向认证,让服务端信任客户端的证书

相当于“Https单向认证”,添加了“服务端验证客户端身份真实性”的动作。
需要注意的是:服务端的密钥库参数“CN”必须与服务端的IP地址相同,否则会报错,客户端的任意。
如果服务端的CN为lijun,则客户端不能为lijun,即这两个不能是同一个名字。


第一步:客户端生成证书(用于服务端验证客户端)
keytool -validity 365 -genkeypair -v -alias kagula -keyalg RSA -storetype PKCS12 -keystore D:\MyClientSecurity\kagulakey.p12 -storepass 123456 -keypass 123456
kagula为证书的名称,D:\MyClientSecurity\kagulakey.p12证书的存放位置。


第二步:证书格式转为cer文件。
keytool -export -v -alias kagula -keystore D:\MyClientSecurity\kagulakey.p12 -storetype PKCS12 -storepass 123456 -rfc -file D:\MyClientSecurity\kagulakey.cer
kagula为证书的名称,D:\MyClientSecurity\kagulakey.p12证书的存放位置,123456证书密码,D:\MyClientSecurity\kagulakey.cer导出的文件。


第三步:添加到(或新建)一个keystore文件
keytool -import -v -alias kagula -file D:\MyClientSecurity\kagulakey.cer -keystore D:\MyServerSecurity\tomcat7_client.keystore -storepass 123456
tomcat7_client.keystore如果不存在就会新建一个keystore,如果存在会添加到已经存在的keystore中。


第四步:查看keystore文件中的内容
keytool -list -keystore D:\MyServerSecurity\tomcat7_client.keystore


第五步:修改tomcat7中的server.xml文件
原单向认证的配置如下
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" 
               keystoreFile="D:\\MyServerSecurity\\tomcat7.keystore" keystorePass="123456"/>
               
               
现在修改后,如下
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="true" sslProtocol="TLS"
               keystoreFile="D:\\MyServerSecurity\\tomcat7.keystore" keystorePass="123456"
  truststoreFile="D:\\MyServerSecurity\\tomcat7_client.keystore" truststorePass="123456"/>           
添加了最后一行属性使它指向客户端证书,并把clientAuth属性从false改为true。


第六步(可选):
Step6-1:
测试Windows下的Chrome是否还能访问服务器,果然刷新浏览器后
“https://lijun:8443/escortcashbox/main/aboutUs.do”返回错误信息
Step6-2:
测试libcurl是否还能访问服务器,现在libcurl返回7(无法连接服务器)的错误信息。


最后一步-让Windows下的Chrome和IE能访问服务端:
双击D:\MyClientSecurity\kagulakey.p12文件,“不需要添加到受信任的根证书机构”结点,直接导入证书即可。
默认在certmgr.msc命令,“个人”->“证书”节点下。


最后一步-让libcurl能访问服务端:
使用msys64打开openssl命令行工具
#客户端个人证书的公钥
openssl>pkcs12 -in D:\MyClientSecurity\myclientkey.p12 -out D:\MyClientSecurity\client_publickey.pem -nokeys
也许如果在当前command shell下能找到openssl.exe也可以用下面的命令
“openssl pkcs12 -in D:\MyClientSecurity\myclientkey.p12 -out D:\MyClientSecurity\client_publickey.pem -nokeys”


#客户端个人证书的私钥
openssl pkcs12 -in D:\MyClientSecurity\myclientkey.p12 -out D:\MyClientSecurity\client_privatekey.pem -nocerts -nodes


#也可以转换为公钥与私钥合二为一的文件;     客户端公钥与私钥,一起存在all.pem中
openssl>pkcs12 -in D:\MyClientSecurity\kagulakey.p12 -out D:\MyClientSecurity\client_all.pem -nodes




1、使用client_publickey.pem + client_privatekey.pem  (未在win console shell下测试)
curl -k --cert client_publickey.pem --key D:\MyClientSecurity\client_privatekey.pem https://lijun:8443/escortcashbox/main/aboutUs.do


2、可以在Msys64 Shell中执行curl命令(如果已经安装了curl)...使用all.pem
curl -k --cert /d/MyClientSecurity/client_all.pem https://lijun:8443/escortcashbox/main/aboutUs.do


下面是双向认证的参考资料
SSL——Secure Sockets Layer
http://www.blogjava.net/icewee/archive/2012/06/04/379947.html

补充阅读资料

使用libcurl实现上传文件到FTP服务器

http://blog.csdn.net/lee353086/article/details/5823145

 

使用libcurl下载http://www.baidu.com/img/baidu.gif示例

http://www.cppblog.com/qiujian5628/archive/2008/06/28/54873.html

 

libcurl的使用总结(一)

http://www.vimer.cn/2010/03/libcurl%E7%9A%84%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93%EF%BC%88%E4%B8%80%EF%BC%89.html

 

PHP的curl实现get,post 和 cookie

http://www.tsingpost.com/articles/201403/525.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Libcurl的编译_HTTP/HTTPS客户端源码示例 的相关文章

  • Gstreamer学习(二)——播放一个视频

    1 参考范例 官方文档 include lt gst gst h gt int main int argc char argv GstElement pipeline GstBus bus GstMessage msg Initialize
  • 菜鸟笔记之计算机网络(3)

    万维网 了解万维网概念相关概念 声明 xff1a 以下是看的视频并结合网上资料所记的笔记 xff0c 侵权请联系删除 可能会有一些错误 xff0c 发现了会修改 了解万维网 概念 万维网 xff08 World Wide Web xff0c
  • STM32串口中断接收实验

    STM32串口中断接收实验的详细说明 准备代码实现总结 准备 材料 xff1a STM32F407ZGT6最小系统板 xff0c 串口1通过跳线帽连接到了CH340上 需求 xff1a 从电脑向板子的串口1发送一个字符串 xff08 以回车
  • 使用C++将网络字节流转为数字(大端与小端区别)

    首先需要了解下大端和小端存储的区别 xff1a 大端方式 xff1a 用存储器的低字节地址单元来存放数据的最高字节 小端存放 xff1a 用存储器的低字节地址单元来存放数据的最低字节 如下图所示 xff1a 网络上都是以字节流的方式传输数据
  • 响应式移动Web测试题

    第一题 下列选项中对bootstrap中的能让元素只在小屏设备隐藏的类是 B A xff1a hidden xs B xff1a hidden sm C xff1a hidden md D xff1a hidden lg 解析 xff1a
  • ROS path [0]=/opt/ros/melodic/share/ros这种错误所有的可能性

    1 没有在ros workspace目录下source devel setup bash 2 roslauch启动节点时 xff0c launch文件包名打错了也会出现这个错误提示 ERROR cannot launch node of t
  • 【curl】 Linux上用curl 查看请求头和响应头

    curl xff0c 全称CommandLine URL 或 CommandLine Uniform Resource Locator xff0c 顾名思义 xff0c curl命令是在命令行方式下工作 xff0c 利用URL的语法进行数据
  • 【开启新阶段】进入本科末段学习的计划

    简单总结 xff1a 经过本科四年的学习 xff0c 博主只能说取得了一个差强人意的结果 xff0c 但生活总是这样 xff0c 难以尽善尽美 在进入大四下半阶段后 xff0c 准备开始新的学习阶段 xff0c 不再像之前一样有着需要自己不
  • 基于Matlab的Robotics Toolbox工具箱的机器人仿真函数介绍(运动学)

    前言 随着我们了解到机器人如何建立运动学模型和动力学模型之后 xff0c 我们可以使用Matlab中的仿真工具箱内来对模型的准确性进行验证 xff0c 并且可以通过内置的函数进行简单的轨迹规划和可视化观察 xff0c 本节涉及到的工具箱是M
  • 基于Matlab的Robotics Toolbox工具箱的机器人仿真函数介绍(空间位姿表示与动力学)

    文章目录 前言一 空间位姿描述1 二维空间2 三维空间3 旋转的不同表示方法1 xff09 欧拉角2 xff09 RPY角3 xff09 双向量表示4 xff09 轴与旋转角5 xff09 四元数表示 二 动力学1 动力学参数2 正动力学函
  • 平面2R机器人的运动学/动力学建模实例

    文章目录 前言平面2R机器人1 问题假设2 建立运动学模型1 xff09 DH参数法2 xff09 指数积方法3 xff09 雅可比矩阵 xff08 微分变换法 xff09 4 xff09 逆运动学求解 xff08 几何法 xff09 3
  • 2023最新jmeter接口测试入门到精通实战讲解,手把手教学

    一 线程组 线程组元件是任何一个测试计划的开始点 在一个测试计划中的所有元件都必须在某个线程组下 所有的任务都是基于线程组 xff1a 通俗理解 xff1a 线程组 xff1a 就是一个线程组 xff0c 里面有若干个请求 xff1b 线程
  • 2023最新全方面了解接口自动化,看完还不会你锤我

    一 自动化分类 现在流行的是金字塔状的分层测试 xff0c 将测试从上到下分为UI测试层 接口测试层 单元测试层三层 在传统的UI自动化的基础之上更多实施基于代码的低级别自动化测试 xff0c 而不仅仅通过用户界面进行端到端的测试 按照测试
  • pytest接口自动化测试框架搭建

    fixture 特点 xff1a 命令灵活 xff1a 对于setup xff0c teardown可以省略 数据共享 xff1a 在conftest py配置里写方法可以实现数据共享 xff0c 不需要import导入 xff0c 可以跨
  • App自动化测试怎么做?实战分享App自动化测试全流程

    一 什么是app测试 xff1f 什么是app自动化测试 xff1f 概念 xff1a 所谓app测试也称之为移动测试 xff0c 通俗易懂的理解就是测试我们平时手机使用的程序 那什么是app自动化测试呢 xff1f 通常情况下是随app产
  • 究极版-计算机四级错题集【操作系统单选题】

    计算机四级究极版错题集 1 解析 按照资源管理的观点 xff0c 操作系统的功能主要可以分为进程线程管理 xff08 处理器管理 xff09 存储管理 文件管理 作业管理和设备管理 2 若用户编程需要打印输出 xff0c 需要系统调用wri
  • 【干货分享】2023美团软件测试面试题汇总

    前言 本篇分享的软件测试面试题内容主要包括 xff1a 测试总体 需求分析 测试计划 测试策略 测试用例 缺陷报告 测试总结报告 白盒测试 单元测试 集成测试 系统测试 验收测试等等26个模块 https www bilibili com
  • 三面百度软件测试岗,都被问到自闭

    1 HR已读不回问题分析以及如何解决 哔哩哔哩 bilibili 1 HR已读不回问题分析以及如何解决是2023最新软件测试面试大全看完offer拿到手软的第1集视频 xff0c 该合集共计21集 xff0c 视频收藏或关注UP主 xff0
  • 抖音软件测试三面,21个面试题复盘

    2023最新软件测试面试大全看完offer拿到手软 哔哩哔哩 bilibili 2023最新软件测试面试大全看完offer拿到手软共计21条视频 xff0c 包括 xff1a 1 HR已读不回问题分析以及如何解决 2 HR已读不回之针对性进
  • 大环境不好难找工作?三面阿里,幸好做足了准备,已拿offer

    这边推荐你去看一下这套专门讲解面试和简历的视频 xff0c 主打面试题 xff0c 接口 web app全套视频面试题 xff0c 还有配套的笔记 xff01 这个视频可以说是B站百万播放全网第一的面试教程 xff0c 同时在线人数到达10

随机推荐

  • 接口自动化测试面试题大全(合适各级软件测试人员)

    这边推荐你去看一下这套专门讲解面试和简历的视频 xff0c 主打面试题 xff0c 接口 web app全套视频面试题 xff0c 还有配套的笔记 xff01 这个视频可以说是B站百万播放全网第一的面试教程 xff0c 同时在线人数到达10
  • 阿里90道常问面试题(软件测试岗位)

    这边推荐你去看一下这套专门讲解面试和简历的视频 xff0c 主打面试题 xff0c 接口 web app全套视频面试题 xff0c 还有配套的笔记 xff01 这个视频可以说是B站百万播放全网第一的面试教程 xff0c 同时在线人数到达10
  • curl 发送json格式数据 请求

    curl H 34 Content Type application json 34 X POST data 39 34 userID 34 10001 39 http localhost 8085 GetUserInfo
  • 在keil中使用头文件实现多文件编程

    如上图所示 xff0c 在这里 xff0c MAX7219driver c为将被包含的源文件 xff0c max7219 h为对应MAX7219driver c的头文件 xff0c 而 xff08 驱动测试 xff09 显示PZ 12234
  • PHP中使用CURL之php curl详细解析和常见大坑

    这篇文章主要介绍了PHP中使用CURL之php curl详细解析和常见大坑 xff0c 现在分享给大家 xff0c 也给大家做个参考 一起跟随小编过来看看吧 七夕啦 xff0c 作为开发 xff0c 妹子没得撩就 撩 下服务器吧 xff0c
  • KMP字符串

    给定一个字符串 S xff0c 以及一个模式串 P xff0c 所有字符串中只包含大小写英文字母以及阿拉伯数字 模式串 P 在字符串 S 中多次作为子串出现 求出模式串P在字符串S中所有出现的位置的起始下标 输入格式 第一行输入整数 N x
  • 一个简单实用的分离器件锂电池充电电路

    下面推荐一个由分离器件搭建的锂电池充电电路 xff0c 如下图 简单说明一下各器件的功能及电路原理 xff1a 简单说明一下各器件的功能及电路原理 F 43 为充电器的正极 xff0c BT 43 为电池正极 xff0c CH与单片机的一个
  • Android自定义节点进度条NodeProgressBar

    NodeProgressBar 一 简介 Android日常开发中我们可能会遇到开发一个带节点的进度条的需求 xff0c 这个需求看似简单 xff0c 实际上可以挖掘出不少东西 做的好的话也可以做成相对通用的自定义组件 二 自定义属性 sp
  • Http工具类 HttpUtils

    import java io IOException import java nio charset Charset import java security KeyManagementException import java secur
  • 【STM32基础】第二篇、STM32串口的使用

    目录 第一部分 如何取出串口接收到的数据 xff1f 第二部分 如何将串口接收的数据与目标数据进行匹配 xff1f 第三部分 串口常用的发送数据的函数 1 发送一个字符 xff08 8位 xff09 2 发送一个16位数据 xff08 16
  • 使用FlyMCU往STM32中烧写程序

    0 软硬件环境 1 操作系统 xff1a Windows 2 软件 xff1a KeilFlyMcu 3 硬件 xff1a PCSTM32最小系统开发板USB转TTL CH340G 1 生成hex文件 编写好要烧写的程序后 xff0c 点击
  • LVI-SAM安装与测试

    1 介绍 就在昨天 xff0c LVI SAM开源了 xff0c 它是一个lidar visual inertial里程计和建图系统 xff0c 在系统级别结合了LIO SAM和Vins Mono的优势 作者之前还开源了LeGO LOAM和
  • 在Ubuntu 18.04上安装Apollo 6.0

    文章目录 1 前期准备1 1 安装Ubuntu1 2 安装NVIDIA GPU驱动1 3 安装Docker Engine1 4 安装NVIDIA Container Toolkit 2 下载Apollo源文件3 启动Docker容器4 进入
  • R2LIVE安装与测试

    文章目录 1 R2LIVE2 安装依赖2 1 Ubuntu和ROS2 2 Ceres Solver2 3 livox ros driver 3 编译R2LIVE4 运行示例 1 R2LIVE R2LIVE 是一个强大的 实时的 紧密耦合的多
  • 图像标注工具labelme在WIndows系统上的安装和使用

    1 前言 labelme可对图像进行标注 xff0c 包括多边形 xff0c 矩形 xff0c 线 xff0c 点和图像级标注 它是用Python编写的 xff0c 并使用Qt作为其图形界面 详细内容见 xff1a https github
  • Windows环境使用和编译CMake记录

    以下为两种使用方式 xff0c 第一种较为简单 xff0c 第二种需提前安装vs软件 1 二进制方式安装 下载win平台的安装包 xff0c 安装解压后将bin目录添加到环境变量即可 打开命令窗口 xff0c 查看当前版本 百度云下载链接
  • 激光雷达和相机的联合标定(Camera-LiDAR Calibration)之Autoware

    1 前言 单一传感器不可避免的存在局限性 xff0c 为了提高系统的稳健性 xff0c 多采取多传感器融合的方案 xff0c 融合又包含不同传感器的时间同步和空间同步 这里要讲的激光雷达和相机的联合标定就属于空间同步范畴 另外 xff0c
  • 如何使用Keras fit和fit_generator(动手教程)

    写在前面 被Adrian Rosebrock圈粉后 xff0c 就一直期待他的更新 xff0c 作者每周一更新 xff0c 考虑到时差问题 xff08 作者在美国 xff09 xff0c 一般北京时间周二才能看到 作者根据读者留言中的问题写
  • CMake 的常用命令

    目录 0 CMake常用的命令或函数 xff1a 1 定义项目 project 2 多个目录 add subdirectory 3 常用命令 add executable add library 4 常用命令 改变最终目标文件输出位置 5
  • Libcurl的编译_HTTP/HTTPS客户端源码示例

    HTTP HTTPS客户端源码示例 环境 zlib 1 2 8 openssl 1 0 1g curl 7 36 Author Kagula LastUpdateDate 2016 05 09 阅读前提 xff1a CMake工具的基本使用