zoukankan      html  css  js  c++  java
  • 基于C++ 苹果apns消息推送实现(2)

    1.该模块的用途C++ 和 Openssl 代码 它实现了一个简单的apns顾客
    2.配套文件:基于boost 的苹果apns消息推送实现(1)
    3.最初使用的sslv23/sslv2/sslv3仅仅能和apple 建立连接,但一直是handshake失败。
    最后换tls连接,握手成功!

    original_ssl_client.h

    #ifndef original_ssl_client_h
    #define original_ssl_client_h
    
    #pragma once
    #include <iostream>
    using namespace std;
    
    int myssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
    
    class original_ssl_client
    {
    public:
        original_ssl_client()   
        {
            m_pctx          = NULL;
            m_sockfd        = -1;
            m_phost_info    = NULL;
            m_pssl          = NULL;
            memset(m_recv_buffer,0,MAX_BUFFER_RECEIVE);
    
        }
        ~original_ssl_client() 
        {
    
        }
    
    private:
        //SSL_METHOD*       m_pmeth;
        SSL_CTX *       m_pctx;
        SOCKET          m_sockfd;
        sockaddr_in     m_server_addr;
        struct hostent* m_phost_info;
        SSL*            m_pssl;
        enum
        {
            MAX_BUFFER_RECEIVE = 1024,
        };
    
        char            m_recv_buffer[MAX_BUFFER_RECEIVE];
    
    public:
        //
        void close()
        {
            // 关闭SSL套接字
            SSL_shutdown(m_pssl);
    
            // 释放SSL套接字
            SSL_free(m_pssl);
    
            // 释放SSL会话环境
            SSL_CTX_free(m_pctx);
    
            // 关闭tcp 套接字
            closesocket(m_sockfd);
    
        }
    
        // 初始化ssl库,Windows下初始化WinSock
        void init_openssl() 
        { 
    
        #ifdef _WIN32 WSADATA wsaData;
            WSADATA wsaData;
            WSAStartup(MAKEWORD(2, 2), &wsaData);
        #endif
    
            SSL_library_init(); 
            ERR_load_BIO_strings(); 
            SSL_load_error_strings(); 
            OpenSSL_add_all_algorithms();
        }
    
        //
        bool init_tcp_connect(const char* host, int port) 
        { 
            if ( !host )
                return false;
    
            struct hostent *hp; 
            //struct sockaddr_in m_server_addr; 
            if (!(hp = gethostbyname(host)))        // 解析域名 
                return false;
    
            memset(&m_server_addr, 0, sizeof(m_server_addr));
            m_server_addr.sin_addr  = *(struct in_addr*)hp->h_addr_list[0];
            m_server_addr.sin_family = AF_INET;
            m_server_addr.sin_port  = htons(port);
            if ((m_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
            {
                cout<<"Could not get Socket"<<endl;
                return false;
            } 
    
            if (connect(m_sockfd, (struct sockaddr*)&m_server_addr, sizeof(m_server_addr)) != 0) 
            { 
                return false;
            } 
    
            return true;
        }
    
        // 创建SSL Context 
        SSL_CTX* init_ssl_context( const char* clientcert, /* 客户端的证书 */ const char* clientkey, /* 客户端的Key */ const char* keypwd, /* 客户端Key的密码, 假设有的话 */ const char* cacert) /* 服务器CA证书 假设有的话 */ 
        { 
            // set up the ssl context 
            m_pctx = SSL_CTX_new((SSL_METHOD*)TLSv1_client_method()); 
            if (!m_pctx) { return NULL; } 
    
            // 要求校验对方证书
            //SSL_CTX_set_verify(m_pctx,SSL_VERIFY_PEER |SSL_VERIFY_CLIENT_ONCE , myssl_verify_callback);
    
            // certificate 
            if (clientcert && SSL_CTX_use_certificate_file(m_pctx, clientcert, SSL_FILETYPE_PEM) <= 0) 
                { return NULL; } 
    
            // key
            if ( clientkey )
            {
                SSL_CTX_set_default_passwd_cb_userdata(m_pctx,(void*)keypwd);
    
                if (SSL_CTX_use_PrivateKey_file(m_pctx, clientkey, SSL_FILETYPE_PEM) <= 0) 
                { return NULL; } 
    
                // make sure the key and certificate file match 
                if (SSL_CTX_check_private_key(m_pctx) == 0) 
                { return NULL; } 
            }
    
    
            // load ca if exist
            if ( cacert ) 
            { 
                if (!SSL_CTX_load_verify_locations(m_pctx, cacert, NULL)) 
                    { return NULL; } 
            } 
    
            return m_pctx; 
        }
    
        // 实现SSL握手,建立SSL连接 
        SSL* ssl_connect( ) 
        { 
            m_pssl = SSL_new(m_pctx); 
            //BIO *bio = BIO_new_socket(m_sockfd, BIO_NOCLOSE);
            //SSL_set_bio(m_pssl, bio, bio);
            SSL_set_fd(m_pssl,m_sockfd);
            int ret = SSL_connect(m_pssl);
            if ( ret <= 0)
            {
                int nErr = SSL_get_error(m_pssl,ret);   // SSL_ERROR_SSL 1,  SSL_ERROR_SYSCALL 5
    
                char err_msg[1024];
                ERR_error_string_n(ERR_get_error(), err_msg, sizeof(err_msg));
                printf("%s
    ", err_msg);
    
                ERR_print_errors_fp(stderr);
                std::cout<<ssl_error_string().c_str()<<endl;
                return NULL; 
            } 
    
            return m_pssl; 
        }
    
        // 验证服务器证书
        // 首先要验证服务器的证书有效。其次要验证服务器证书的CommonName(CN)与我们 
        // 实际要连接的服务器域名一致 
        bool verify_connection(const char* peername) 
        { 
            // 获取校验结果
            int result = SSL_get_verify_result(m_pssl);
            if (result != X509_V_OK && result != X509_V_ERR_CERT_UNTRUSTED && result != X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) 
            { 
                fprintf(stderr, "WARNING! ssl verify failed: %d 
    ", result);
                std::cout<<ssl_error_string().c_str()<<endl;
                return false;
            } 
    
    //      X509 *peer; 
    //      char peer_CN[256] = {0}; 
    //      peer = SSL_get_peer_certificate(m_pssl); 
    //      X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, peer_CN, 255); 
    //      if (strcmp(peer_CN, peername) != 0) 
    //      { 
    //          fprintf(stderr, "WARNING! Server Name Doesn't match, got: %s, required: %s", peer_CN, peername);
    //      } 
    
            return true;
        }
    
        std::string ssl_error_string( )
        {
            //SSL_get_error();
            unsigned long ulErr = ERR_get_error();  // 获取错误号
            char szErrMsg[1024] = {0};
            char *pTmp = NULL;
            pTmp = ERR_error_string(ulErr,szErrMsg); // 格式:error:errId:库:函数:原因
            return szErrMsg;
        }
    
        void ssl_send_keyinput_msg( )
        {
            while ( true)
            {
                Sleep(100);
    
                if( false )
                {
                    char szInput[100] = {};
                    cout<<"commond: "<<endl;
                    cin.getline(szInput,sizeof(szInput),'
    ');
                    if ( strcmp(szInput,"exit") == 0 )
                        break;
    
                    char token[]        = "d2eb47674417c05c5a6f474bddef0391242e1c4d9ea3385e8f55c427d3c7d2ed";
                    char format[]       = "{"aps":{"alert":"%s","badge":1}}";
                    char payload[256]   = {};
                    sprintf(payload,format,szInput);
                    int ret = pushMessage(token, payload);
                    cout<<"push ret["<<ret<<"]"<<endl;
                }
    
                recv_message();
            }
        }
    
        int initializeSSL(  )
        {
            /*/
            char host[]     = "gateway.sandbox.push.apple.com"; 
            int port        = 2195;
            char password[] = "hc123";
            #const char*     CERTFILE_PATH =          "boost/PushChatCert.pem";
            #const char*     CERTKEY_PATH  =          "boost/PushChatKey.pem";
            #const char*     CACERT_PATH   =          "boost/sandbox.pem";
            /*/
            const char*     CERTFILE_PATH =          NULL;
            const char*     CERTKEY_PATH  =          NULL;
            const char*     CACERT_PATH   =          "boost/ca.pem";
            char host[]     = "localhost";
            int port        = 13;
            char password[] = "test";
            //*/
            char token[]    = "adc97f91 fbd886bd cd052c3b 89c9bf95 1b5be2eb b31bdd56 16d3165c 9d0569c4";
            char payload[]  = "{"aps":{"alert":"kkkkkkk","badge":1,"sound":"default"},}";
    
            int err;
            SSL_library_init();
            SSL_load_error_strings();
            OpenSSL_add_all_algorithms();       // 支持全部算法
    
            m_pctx = SSL_CTX_new((SSL_METHOD*)SSLv3_client_method());
            if( !m_pctx )   {
                cout<<"Could not get SSL Context"<<endl;
                return false;
            }
            // 要求校验对方证书
            SSL_CTX_set_verify(m_pctx,SSL_VERIFY_PEER/*|SSL_VERIFY_CLIENT_ONCE*/, myssl_verify_callback);
    
            if(SSL_CTX_load_verify_locations(m_pctx, NULL, CACERT_PATH) <= 0)
            {
                cout<<"Failed to set CA location"<<endl;
                ERR_print_errors_fp(stderr);
                return false;
            }
    
    
            if(CERTFILE_PATH && SSL_CTX_use_certificate_file(m_pctx,CERTFILE_PATH,SSL_FILETYPE_PEM) <= 0)
            {
                cout<<"Cannot use Certificate File"<<endl;
                ERR_print_errors_fp(stderr);
                return false;
            }
    
            if ( CERTKEY_PATH )
            {
                SSL_CTX_set_default_passwd_cb_userdata(m_pctx,password);
    
                if (SSL_CTX_use_PrivateKey_file(m_pctx, CERTKEY_PATH, SSL_FILETYPE_PEM) <= 0)
                {
                    cout<<"Cannot use Private Key"<<endl;
                    ERR_print_errors_fp(stderr);
                    return false;
                }
    
                if (!SSL_CTX_check_private_key(m_pctx))
                {
                    cout<<"Private key does not match the certificate public key"<<endl;
                    return false;
                }
            }
    
            WSADATA wsaData;
            WSAStartup(MAKEWORD(2, 2), &wsaData);
            if ((m_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)     {
                cout<<"Could not get Socket"<<endl;
                return false;
            } 
    
            memset (&m_server_addr, '', sizeof(m_server_addr));
            m_server_addr.sin_family      = AF_INET;
            m_server_addr.sin_port        = htons(port);
            m_phost_info                  = gethostbyname(host);
            if( m_phost_info )
            {
                struct in_addr *address = (struct in_addr*)m_phost_info->h_addr_list[0];
                m_server_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*address));
            }
            else
            {
                cout<<"Could not resolve hostname = "<<host<<endl;
                return false;
            }
    
            err = connect(m_sockfd, (struct sockaddr*)&m_server_addr, sizeof(m_server_addr));
            if(err == -1)
            {
                cout<<"Could not connect"<<endl;
                return false;
            }
    
            m_pssl = SSL_new(m_pctx);
            if( !m_pssl )   {
                cout<<"Could not get SSL Socket"<<endl;
                return false;
            }
    
            if( SSL_set_fd(m_pssl, m_sockfd) == -1 )
                return false;
    
            err = SSL_connect(m_pssl);
            if(err <= 0  )  {
                //ERR_print_errors_fp(stderr);
                cout<<ssl_error_string().c_str()<<endl;
                cout<<"Could not connect to SSL Server"<<endl;
                return false;
            }
    
            // 获取证书验证结果
            int result = SSL_get_verify_result(m_pssl);
            if (result != X509_V_OK && result != X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) 
            { 
                fprintf(stderr, "WARNING! ssl verify failed: %d 
    ", result);
                std::cout<<ssl_error_string().c_str()<<endl;
                return false;
            }
    
    
            return true;
        }
    
        int pushMessage(const char * token, const char * payload)
        {
            char tokenBytes[32];
            char message[293];
            unsigned long msgLength;
    
            token2bytes( token, tokenBytes );
            unsigned short payloadLength    = strlen( payload );
            char * pointer = message;
            unsigned short networkTokenLength   = htons( (unsigned short)32 );
            unsigned short networkPayloadLength = htons( (unsigned short)payloadLength );
            // command
            //*/
            unsigned char command           = 0;
            memcpy(pointer, &command, sizeof(unsigned char));
            pointer += sizeof(unsigned char);
            /*/
            unsigned char command           = 1;
            memcpy(pointer, &command, sizeof(unsigned char));
            pointer += sizeof(unsigned char);
            // identityfer
            boost::uint32_t identityfer = 1;
            memcpy(pointer, &identityfer, 4);  
            pointer += 4;
            // expiry
            boost::uint32_t tExpiry = time(NULL) + 24*3600;
            memcpy(pointer, &tExpiry, 4);  
            pointer += 4;
            //*/
    
            // token len
            memcpy(pointer, &networkTokenLength, sizeof(unsigned short));
            pointer += sizeof(unsigned short);
            // token
            memcpy(pointer, tokenBytes, sizeof(tokenBytes));
            pointer += 32;
            // payload len
            memcpy(pointer, &networkPayloadLength, sizeof(unsigned short));
            pointer += sizeof(unsigned short);
            // payload
            memcpy(pointer, payload, payloadLength);
            pointer += payloadLength;
            // clac len
            msgLength = pointer - message;
            return SSL_write( m_pssl, message, (int)msgLength );
        }
    
        void recv_message( )
        {
            int nRealRead = SSL_read(m_pssl,m_recv_buffer,MAX_BUFFER_RECEIVE);
            if ( nRealRead <= 0 )
            {
                int nErr = SSL_get_error(m_pssl, nRealRead);    // SSL_ERROR_SSL 1,  SSL_ERROR_SYSCALL 5
    
                char err_msg[1024];
                ERR_error_string_n(ERR_get_error(), err_msg, sizeof(err_msg));
                printf("%s
    ", err_msg);
            }
            else
            {
                std::cout<<m_recv_buffer<<endl;
                memset(m_recv_buffer,0,MAX_BUFFER_RECEIVE);
            }
        }
    
        void token2bytes(const char *token, char *bytes)
        {
            int val;  
            while (*token) 
            {  
                sscanf_s(token, "%2x", &val);  
                *(bytes++) = (char)val;  
                token += 2;  
                while (*token == ' ') {  
                    ++token;                // skip space
                }  
            }  
        } 
    
    };
    
    #endif

    original_ssl_client.cpp

    #include "stdafx.h"
    
    #include <boost/asio.hpp>
    #include <boost/asio/ssl.hpp>
    
    #include "original_ssl_client.h"
    
    struct myssl_data
    {
        int verbose_mode;
        int verify_depth;
        int always_continue;
    };
    
    
    
    int myssl_verify_callback( int preverify_ok, X509_STORE_CTX *ctx )
    {
        char    buf[256];
        X509   *err_cert;
        int     err, depth;
        SSL    *ssl;
        myssl_data *mydata;
        int     mydata_index = 0;
        err_cert = X509_STORE_CTX_get_current_cert(ctx);
        err = X509_STORE_CTX_get_error(ctx);
        depth = X509_STORE_CTX_get_error_depth(ctx);
        /*
        * Retrieve the pointer to the SSL of the connection currently treated
        * and the application specific data stored into the SSL object.
        */
        ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
        mydata = (myssl_data*)SSL_get_ex_data(ssl, mydata_index);
        X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
        /*
        * Catch a too long certificate chain. The depth limit set using
        * SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so
        * that whenever the "depth>verify_depth" condition is met, we
        * have violated the limit and want to log this error condition.
        * We must do it here, because the CHAIN_TOO_LONG error would not
        * be found explicitly; only errors introduced by cutting off the
        * additional certificates would be logged.
        */
        if (mydata && depth > mydata->verify_depth) {
            preverify_ok = 0;
            err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
            X509_STORE_CTX_set_error(ctx, err);
        } 
        if (!preverify_ok) {
            printf("verify error:num=%d:%s:depth=%d:%s
    ", err,
                X509_verify_cert_error_string(err), depth, buf);
        }
        else if (mydata && mydata->verbose_mode)
        {
            printf("depth=%d:%s
    ", depth, buf);
        }
        /*
        * At this point, err contains the last verification error. We can use
        * it for something special
        */
        if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT))
        {
            X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
            printf("issuer= %s
    ", buf);
        }
        if (mydata && mydata->always_continue)
            return 1;
        else if ( err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || err == X509_V_ERR_CERT_UNTRUSTED )
            return 1;
        else
            return preverify_ok;
    }
    
    

    使用:
    int _tmain(int argc, _TCHAR* argv[])
    {
    original_ssl_client test_ssl_client;
    test_ssl_client.init_openssl();
    test_ssl_client.init_tcp_connect(“gateway.sandbox.push.apple.com”,2195);
    test_ssl_client.init_ssl_context(“boost/PushChatCert.pem”,”boost/PushChatKey.pem”,”hc123”,”boost/sandbox.pem”);
    test_ssl_client.ssl_connect();
    test_ssl_client.verify_connection(NULL);
    test_ssl_client.ssl_send_keyinput_msg();
    test_ssl_client.close();
    return 1;
    }

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    结对项目进度1
    学期总结之数学建模软件——LINGO和R
    四则运算计算程序(完成)
    学期总结之数学建模软件——编译原理
    图形学算法之NichollLeeNicholl算法
    学期总结之3D游戏开发
    《构建之法》读后感
    四则运算计算程序(初步)
    结对项目进度2
    Spring IOC原理解析
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4807797.html
Copyright © 2011-2022 走看看