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

时间:2023-02-15 23:15:20

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;

}

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

基于C++ 苹果apns消息推送实现(2)的更多相关文章

  1. oc学习之路----APNS消息推送从证书到代码&lpar;2015年4月26号亲试可用&rpar;

    前言:看这篇博客之前要准备:首先的有一个99刀的个人开发者账号或者199刀的企业开发者账号,其次你用的是apns消息推送,(本人之前四处打听有没有其他消息推送的方法:收获如下:首先如果想做到apns的 ...

  2. IOS 基于APNS消息推送原理与实现&lpar;JAVA后台&rpar;

    Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Pu ...

  3. 转:IOS 基于APNS消息推送原理与实现&lpar;JAVA后台&rpar;

    Push的原理: Push 的工作机制可以简单的概括为下图   图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...

  4. IOS 基于APNS消息推送原理与实现&lpar;JAVA后台&rpar;--转

    Push的原理: Push 的工作机制可以简单的概括为下图   图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...

  5. iOS 基于APNS消息推送原理与实现&lpar;包括JAVA后台代码&rpar;

    Push的原理: Push 的工作机制可以简单的概括为下图   图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...

  6. 【转】APNs消息推送完整讲解

    https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificat ...

  7. APNs消息推送完整讲解

    在项目的AppDelegate中的didFinishLaunchingWithOptions方法中加入下面的代码: [[UIApplication sharedApplication] registe ...

  8. APNS消息推送实现

    转自:http://blog.csdn.net/biaobiaoqi/article/details/8058503 一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图1-1: 1 ...

  9. 基于APNs最新HTTP&sol;2接口实现iOS的高性能消息推送&lpar;服务端篇&rpar;

    1.前言 本文要分享的消息推送指的是当iOS端APP被关闭或者处于后台时,还能收到消息/信息/指令的能力. 这种在APP处于后台或关闭情况下的消息推送能力,通常在以下场景下非常有用: 1)IM即时通讯 ...

随机推荐

  1. bug&lowbar; &lowbar;小心android-support-v4&period;jar版本混乱造成的NoClassDefFoundError

    当你的项目出现以下红色提示的时候,要小心了, 因为很可能因为这个错误而导致解释不通的异常出现. Found 2 versions of android-support-v4.jar in the de ...

  2. HBuilder的几个常用快捷键

    Alt + [ 匹配括号 Alt + ↓跳转到下一个可编辑区 Ctrl + Alt + j 合并下一行 Ctrl + Alt + ←选择助手 Shift + 回车 Shift + 空格   Ctrl ...

  3. hdu3811(状态压缩dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3811 题目大意:给定1~N个数,求出至少满足一个条件的排列总数.M个条件如下:Ai位置的数为Bi 分析 ...

  4. Angular - - ngClass、ngClassEven、ngClassOdd、ngStyle

    这几个都关于样式及类名修改的,所以先把样式代码贴上吧. .red{color:red} .blue{color:blue} 写案例用到的样式就这么简单的两个,下面进入正题. ngClass ngCla ...

  5. kafka 集群搭建

    环境:ubuntu14.04 版本:jdk1.8,zookeeper 3.4.10,kafka 2.11 搭建步骤: 1. 搭建zookeeper集群 参考链接:zookeeper集群搭建 2. 下载 ...

  6. SQLServer 2014 内存优化表

    内存优化表是 SQLServer 2014 的新功能,它是可以将表放在内存中,这会明显提升DML性能.关于内存优化表,更多可参考两位大侠的文章:SQL Server 2014新特性探秘(1)-内存数据 ...

  7. 清除冗余的css

    下载旧版的火狐浏览器,如Firefox 48.0.exe, 下载地址:https://ftp.mozilla.org/pub/firefox/releases/48.0/win32/zh-CN 关闭更 ...

  8. ES5-ES6-ES7&lowbar;解构赋值

    解构赋值的概念 ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring) 传统对变量赋值的方式,以前,为变量赋值,只能直接指定值. var a = 1; ...

  9. Axis2发布服务,支持Tomcat和Weblogic的SSHWeb项目部署

    先说下遇到的问题,在SSHWeb项目中使用JDK自带的jar发布WebService(Endpoint.publish),在tomcat下可以正常发布,但是在Weblogic报奇葩错误,如Struts ...

  10. 获取客户端真实ip地址&lpar;无视代理&rpar;

    /// <summary> /// 获取客户端IP地址(无视代理) /// </summary> /// <returns>若失败则返回回送地址</retur ...