基于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
- 基于C++ 的苹果apns消息推送实现(2)
- 基于boost 的苹果apns消息推送实现(1)
- ZPush--基于netty4实现的苹果通知推送服务(APNs)Java客户端
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- iOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- IOS 基于APNS消息推送原理与实现
- IOS 基于APNS消息推送原理与实现(JAVA后台)
- Largest Number
- iPhone手机通讯录恢复方法
- 使用draggablegridview开源库添加自定义控件可以显示,添加自定义的layout文件可以显示背景色但是无法显示其控件
- 项目2-长方体类
- 【JAVA】【NIO】10、Java NIO ServerSocketChannel
- 基于C++ 的苹果apns消息推送实现(2)
- 堆
- 二叉树 nyoj-63 小猴子下落
- 组合数对素数取模 Lucas定理
- Java——安装配置
- 总结eclipse中安装maven插件
- php绘图应用之验证码
- 分享一篇很好的sprite介绍文章:模拟物理
- 内部类