基于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即时通讯 ...
随机推荐
- 又一次认识HTML,CSS,Javascript 之node-webkit 初探
今天我们来系统的.全面的 了解一下前端的一些技术,将有助于我们写出 更优秀的 产品 出来. 什么是HTML? HTML 是用来描写叙述网页的一种语言. HTML 包括一些根节点.子节点,文本节点.属性 ...
- c++学籍管理系统v1.10
//////////////新增添加学生和成绩录入系统 #include<iostream> #include <string> #include<conio.h> ...
- 二维码简单Demo
二维码简单Demo 一.视图 @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name=&qu ...
- 简单实现Android平台多语言
这里,我们认识到两种语言.中国简体和繁体中国. 在res文件建议两个文件夹 values-zh-rCN values-zh-rTW 两个目录下都有一个strings.xml文件. 两个同名文件的字符串 ...
- 渲染优化 之fixed与返回顶部 以及开启GPU Hack
fixed元素,常见网站右侧出现一个返回顶部的按钮,滚动的时候,会发现返回顶部这个区域在不停的进行重绘,而返回顶部是position:fixed定位的.这也解释了为什么fixed定位是最耗性能的属性之 ...
- uva 10817 - Headmaster's Headache ( 状态压缩dp)
本文出自 http://blog.csdn.net/shuangde800 题目链接: 点击打开链接 题目大意 某校有n个教师和m个求职者,已知每人的工资和能教的课程集合,要求支付最少的工资使得每 ...
- 理解git经常使用命令原理
git不同于类似SVN这样的版本号管理系统,尽管熟悉经常使用的操作就能够满足大部分需求,但为了在遇到麻烦时不至于靠蛮力去尝试,了解git的原理还是非常有必要. 文件 通过git管理的文件版本号信息所有 ...
- Net并行编程高级教程--Parallel
Net并行编程高级教程--Parallel 一直觉得自己对并发了解不够深入,特别是看了<代码整洁之道>觉得自己有必要好好学学并发编程,因为性能也是衡量代码整洁的一大标准.而且在<失控 ...
- zoj 3659 并检查集合
http://acm.zju.edu.cn/onlinejudge/showProblem.do? problemId=4882 现在在牡丹江,明天regional直播比赛,我会在一个月内退休.求祝福 ...
- DevExpress XtraReports 入门五 创建交叉表报表
原文:DevExpress XtraReports 入门五 创建交叉表报表 本文只是为了帮助初次接触或是需要DevExpress XtraReports报表的人群使用的,为了帮助更多的人不会像我这样浪 ...