一次IOS通知推送问题排查全过程
原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。
发现问题
在上周一个将要下班的夜晚,测试突然和我打招呼,说IOS推送的修复更新上线后存在问题,后台报错。
连忙跑到测试那里看报错详情,报错如下:

重现问题
看到这个报错后,在网上搜索了一下,这种错误一般都是因为客户端不信任服务端SSL证书导致的,回想工作以来,好像遇到这种问题好多次了,只要将证书导入一下就好了。
由于不能冒然在线上修改解决问题,于是获取推送相关信息(如:设备token)后,到自己电脑上去测试,看是否能重现问题。
啪啦啪啦,代码修改完毕,点运行坐等错误出现。
5秒钟过后,发现推送消息发送成功了,没有出现报错,有点懵逼!心里想,代码都是完全一样的啊,怎么线上报错,我这却是好的呢???
纠结了一会,于是开始静下心来分析:
- 代码肯定是一样的,应该不是表面上的代码原因。
- 其次推送设备也是一样的,应该也不是手机问题。
- 那么。。。
就在没有头绪之际,我又扫了一眼工程目录,发现了jdk8,但我们线上系统使用的是jdk7啊。

于是我将自己工程的jdk8换成jdk7试一下,同样的报错终于出现了!
那为什么jdk8没问题,jdk7却报错呢???
寻找原因
围绕着网上的说法和之前的经验,这hand_failure错误应该是SSL/TLS握手过程中不信任服务端证书导致的,那办法很简单,去苹果官网找到苹果服务端提供的证书,导入到java的证书信任库cacert文件中即可。

于是,下载了证书文件GeoTrust_Global__CA.cer,并用jdk自带的keytool工具将证书导入到cacert文件中,如下:

如上,导入过程中,发现提示已经有这个证书了,当时没想那么多,再导一次试试吧!
导入后,再次运行代码,结果还是报错!
心里又慌乱起来,没招了,于是又百度/google去了,但搜索出来的结论几乎都是证书信任问题,和自己的招式一样!

同时,也了解了一下SSL/TLS协议相关知识,大致握手过程如下:

详细如下:
客户端发送clientHello消息,告诉服务端我使用的TLS版本与加密套件等。
服务器返回serverHello消息,告诉自己选择哪个TLS协议版本与加密套件等。
服务器发送Certification消息,将自己的数字证书(包括服务器名称、CA和公钥)作为消息内容发给客户端。
客户端Certificate verify校验服务器的数字证书的有效性。
客户端Change cipher spec选择加密套件并生成会话密钥(客户端与服务器之间后续的数据传输将使用此会话密钥)。
客户端、服务器之间发送加密数据。
另外,jvm有一个-Djavax.net.debug=SSL的参数,可以把SSL/TLS的握手过程都在控制台通过日志显示出来,如下:

Ok,既然知道了握手过程,那就把SSL/TLS的日志显示出来看一下吧,看看是什么阶段出现的问题,如下:

这说明GeoTrust_Global.cer证书确实已经添加到信任库中了,而详细的SSL日志如下:

如上图,报错发生在clientHello发送之后,在读服务端返回的serverHello消息时,却读到的是Alert:handshake_failure警告信息,然后SSL握手就中断了,就好像你在和服务器打招呼,然后服务器回复了一个滚蛋一样!
百思不得其解?
准备放弃
一会,我看到测试走了过来。
测试:“问题解决的怎么样了,啥情况啊”
我:“还不知道,本来以为很简单,实际上并没有,jdk8可以,7不行”
测试:“好吧”
我:”要不升级jdk8吧“
测试:”能行吗“
我:”......能行,jdk兼容性都很好,而且其它系统都已经升级过了,这个系统本来也计划要升,问题不大“
测试:”......好的“
于是,测试去升级jdk8了,本来我应该站在测试身后等待问题被解决,但我还是想在这期间查一查根本原因,于是又琢磨了起来。
发现真相
回想起,这个系统以前本来是jdk6,就是因为苹果强制开发者必须使用Https,且需要TLSv1.2版本,而jdk7才开始支持TLSv1.2,所以这个系统才升级到jdk7,那现在这个报错会不会...
于是,我赶紧使用TLSv1.2试试,添加jvm参数-Dhttps.protocols=TLSv1.2即可强制https使用TLSv1.2版本协议,如下:

加完后再次运行,报错依旧,那还有那里可能会有问题呢?
我突然灵光一闪,使用jdk7运行一次,再使用jdk8运行一次,将两次的SSL日志对比一下,看看哪里不一样,说不定能找到线索!
于是我马上试运行并对比两次SSL日志,发现两次SSL日志中TLS协议使用的加密套件不同,如下图:
jdk7的调试信息如下 :

jdk8的调试信息如下:

jdk8的TLS握手在服务端ServerHello之后,选择的加密套件是TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,而jdk7中可供选择的加密套件中没有TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384。
问题差不多清晰了,应该是jdk7中的加密套件,苹果服务器觉得太老不安全,都不支持了,那么如果我使用jdk8,并将加密套件强制为jdk7中的加密套件呢,应该也是会报错的!
于是,我还是使用jdk8,并添加了jvm参数-Dhttps.cipherSuites=SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,SSL_RSA_WITH_RC4_128_SHA,SSL_RSA_WITH_RC4_128_MD5,SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,SSL_RSA_WITH_3DES_EDE_CBC_SHA,以强制jdk8使用jdk7的加密套件,如下:

再次运行后,果然报错了!所以应该是jdk8支持了一些新的加密套件,而苹果服务端只认这些新的加密套件导致的。
于是,我去jdk官网,查了一下jdk8的新增特性,发现TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384中AES加密算法的GCM模式在jdk8中才支持,如下:

至此,问题原因全部搞清楚了,测试升级jdk8后也报告推送正常,此时已是10点多,两人写写日报赶紧回家去了......
往期内容
密码学入门
时区的坑,不想再踩了!
常见的Socket网络异常场景分析
字符编码解惑
一次IOS通知推送问题排查全过程的更多相关文章
- iOS 通知推送APNS
结合网上各个资料,再简单整理的一份. 一.APNS推送说明 1.你的IOS应用需要去注册APNS消息推送功能. 2.当苹果APNS推送服收到来自你应用的注册消息就会返回一串device token给你 ...
- 【转】iOS消息推送实现过程记录
客户端代码:链接地址 服务器代码:链接地址 链接地址 这里记录下iOS消息推送实现的全过程 首先,申请秘钥. 之后进入链接地址开发者,当然你得有啊!!!!! 点击这里 如图: 下面实现创建推送证书( ...
- IOS之推送通知(本地推送和远程推送)
推送通知和NSNotification是有区别的: NSNotification:是看不到的 推送通知:是可以看到的 IOS中提供了两种推送通知 本地推送通知:(Local Notification) ...
- iOS 10推送通知开发
原文地址:Developing Push Notifications for iOS 10,译者:李剑飞 虽然通知经常被过度使用,但是通知确实是一种获得用户关注和通知他们需要更新或行动的有效方式.iO ...
- (七十三)iOS本地推送通知的实现
iOS的推送通知分为本地推送和网络推送两种,如果App处于挂起状态,是可以发送本地通知的,如果已经被杀掉,则只有定时通知可以被执行,而类似于QQ的那种网络消息推送就无法实现了,因为App的网络模块在被 ...
- xamarin.ios 本地通知推送
由于ios10版本以后UILocalNotification被标为弃用了,所以要添加新的本地通知推送功能,下面提供一些代码参考. 一.先在AppDelegate.cs上注册本地通知推送功能. publ ...
- IOS远程推送
IOS远程推送 一.关于推送通知 推送通知,也被叫做远程通知,是在iOS 3.0以后被引入的功能.是当程序没有启动或不在前台运行时,告诉用户有新消息的一种途径,是从外部服务器发送到应用程序上的.一般说 ...
- iOS 消息推送(APNs) 傻瓜式教程
也可以去我的简书页面查看这篇文章 首先: 1.做iOS消息推送需要真机测试 2.做iOS消息推送需要有付费的开发者账号 是否继续看帖? 先学习一下相关的知识吧! 因为中途可能会遇到一些问题,这篇文章或 ...
- (转载)iOS 极光推送SDK 集成指南
iOS SDK 集成指南 使用提示 本文匹配的 SDK版本:r1.2.5 以后. 查看最近更新了解最新的SDK更新情况. 产品功能说明 极光推送(JPush)是一个端到端的推送服务,使得服务器端消息能 ...
随机推荐
- 什么是 CAS?
CAS 是 compare and swap 的缩写,即我们所说的比较交换. cas 是一种基于锁的操作,而且是乐观锁.在 java 中锁分为乐观锁和悲观锁.悲观锁是将资源锁住,等一个之前获得锁的线程 ...
- 解释 MySQL 外连接、内连接与自连接的区别 ?
先说什么是交叉连接: 交叉连接又叫笛卡尔积,它是指不使用任何条件,直接将一 个表的所有记录和另一个表中的所有记录一一匹配. 内连接 则是只有条件的交叉连接,根据某个条件筛选出符合条件的记录,不符合 条 ...
- Java 中 LinkedHashMap 和 PriorityQueue 的区别是 什么?
PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序.当遍历一个 PriorityQueue 时,没有任何顺序保证,但 ...
- C++ | 虚函数初探
虚函数 虚函数 是在基类中使用关键字 virtual 声明的函数.在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数. 我们想要的是在程序中任意点可以根据所调用的对象类型来选择调 ...
- 【静态页面架构】CSS之链接和图像
CSS架构 一.链接: 链接元素:通过使用a元素的href属性设置跳转到指定页面地址 <style> a{ color: blue; text-decoration: none; } a: ...
- 基于腾讯开源的msec来进行php开发模块
msecphp 毫秒服务引擎(Mass Service Engine in Cluster)是一个开源框架,适用于在廉价机器组成的集群上开发和运营分布式后台服务. 毫秒服务引擎集RPC.名字发现服务. ...
- 单例模式的实现Singleton和MonoSingleton
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> / ...
- PAT B1061判断题
题目描述: 判断题的评判很简单,本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分. 输入格式: 输入在第一行给出两个不超过 100 的正整数 N 和 M,分别是学生人数和判断题数量.第二 ...
- eclipse/myeclipse注释模板的修改
本文转自:http://kaminlee.iteye.com/blog/1101938 Window --> Java --> Code Style --> Code Templat ...
- 利用HTML5和css3 选中图片上传到服务器,插件地址如下
https://yusi123.com/3349.html 分三步,需要将js文件和css文件拷贝到项目目录下,在需要选择的图片的文件中引入,然后将HTML代码复制 <!--选择图片模块star ...