基于C++ 的苹果apns消息推送实现(2)

来源:互联网 发布:linux close函数 编辑:程序博客网 时间:2024/06/05 02:02

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;
}

2 0
原创粉丝点击