基于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\n", 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 \n", 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),‘\n‘);
                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, ‘\0‘, 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 \n", 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\n", 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\n", err,
            X509_verify_cert_error_string(err), depth, buf);
    }
    else if (mydata && mydata->verbose_mode)
    {
        printf("depth=%d:%s\n", 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\n", 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;
}

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。