基于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;
}
版权声明:本文博主原创文章,博客,未经同意不得转载。
基于C++ 苹果apns消息推送实现(2)的更多相关文章
- oc学习之路----APNS消息推送从证书到代码(2015年4月26号亲试可用)
前言:看这篇博客之前要准备:首先的有一个99刀的个人开发者账号或者199刀的企业开发者账号,其次你用的是apns消息推送,(本人之前四处打听有没有其他消息推送的方法:收获如下:首先如果想做到apns的 ...
- IOS 基于APNS消息推送原理与实现(JAVA后台)
Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Pu ...
- 转:IOS 基于APNS消息推送原理与实现(JAVA后台)
Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...
- IOS 基于APNS消息推送原理与实现(JAVA后台)--转
Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...
- iOS 基于APNS消息推送原理与实现(包括JAVA后台代码)
Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple ...
- 【转】APNs消息推送完整讲解
https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificat ...
- APNs消息推送完整讲解
在项目的AppDelegate中的didFinishLaunchingWithOptions方法中加入下面的代码: [[UIApplication sharedApplication] registe ...
- APNS消息推送实现
转自:http://blog.csdn.net/biaobiaoqi/article/details/8058503 一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图1-1: 1 ...
- 基于APNs最新HTTP/2接口实现iOS的高性能消息推送(服务端篇)
1.前言 本文要分享的消息推送指的是当iOS端APP被关闭或者处于后台时,还能收到消息/信息/指令的能力. 这种在APP处于后台或关闭情况下的消息推送能力,通常在以下场景下非常有用: 1)IM即时通讯 ...
随机推荐
- redis源代码解读之内存管理————zmalloc文件
本文章主要记录本人在看redis源代码的一些理解和想法.由于功力有限,肯定会出现故障,所以.希望高手给出指正. 第一篇就是内存相关的介绍.由于我喜欢先看一些组件的东西,再看总体的流程. 先上一下代码吧 ...
- CCProgressTo 和CCProgressTimer
在cocos2d中相同提供了非常多表现图片和精灵的方式,上一篇其中提到的切换场景的方式之中的一个是顺或逆时针切入的方法,在图片上也能够使用,test里有一个样例介绍CCProgressTimer能够实 ...
- 探索Windows Azure 监控和自动伸缩系列1 - 连接中国区Azure
最近准备基于Microsoft Azure Management Libraries 实现虚拟机的监控.主要的需求就是获取虚拟机内置的性能计数器数据,基于性能计数器实现后续的监控和自动伸缩. 作为这一 ...
- Twitter 新一代流处理工具——Heron 该纸币Storm Limitations
Twitter 新一代流处理工具--Heron 该纸币Storm Limitations (空格分隔): Streaming-Processing Storm Problems scalability ...
- 使用JMX实现的内存监控(转)
public final class MemoryWarningSystem { private static MemoryWarningSystem m_instance = null; /** * ...
- 数据库管理——安全管理——识别SQLServer中空密码或者弱密码的登录名
原文:数据库管理--安全管理--识别SQLServer中空密码或者弱密码的登录名 原文译自: http://www.mssqltips.com/sqlservertip/2775/identify-b ...
- div+js 弹出层
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <t ...
- Android处理延迟加载的方法
在项目开发,通过延时载入来实现满足我们的项目要求.那究竟如何来实现延时.以下结合java与android的相关方法来实现延时问题. 一.利用线程的Sleep方法 <span style=&quo ...
- eclipse android ndk 提示Type 'JNIEnv' could not be resolved 等信息解决办法
新配置完eclipse c++ android ndk 环境后,导入项目提示以下信息 是由于没有将jni.h导入的缘故,而这个文件在ndk的目录下面.所以,参照以下步骤:Project Propert ...
- Android EventBus源代码解析 带你深入理解EventBus
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...