乐趣区

关于ios:一次IOS通知推送问题排查全过程

原创:打码日记(微信公众号 ID:codelogs),欢送分享,转载请保留出处。

发现问题

在上周一个将要上班的夜晚,测试忽然和我打招呼,说 IOS 推送的修复更新上线后存在问题,后盾报错。

连忙跑到测试那里看报错详情,报错如下:

重现问题

看到这个报错后,在网上搜寻了一下,这种谬误个别都是因为客户端不信赖服务端 SSL 证书导致的,回忆工作以来,如同遇到这种问题好屡次了,只有将证书导入一下就好了。

因为不能冒然在线上批改解决问题,于是获取推送相干信息 (如:设施 token) 后,到本人电脑下来测试,看是否能重现问题。

啪啦啪啦,代码批改结束,点运行坐等谬误呈现。

5 秒钟过后,发现推送音讯发送胜利了,没有呈现报错,有点懵逼!心里想,代码都是齐全一样的啊,怎么线上报错,我这却是好的呢???

纠结了一会,于是开始静下心来剖析:

  1. 代码必定是一样的,应该不是外表上的代码起因。
  2. 其次推送设施也是一样的,应该也不是手机问题。
  3. 那么。。。

就在没有脉络之际,我又扫了一眼工程目录,发现了 jdk8,但咱们线上零碎应用的是 jdk7 啊。

于是我将本人工程的 jdk8 换成 jdk7 试一下,同样的报错终于呈现了!

那为什么 jdk8 没问题,jdk7 却报错呢???

寻找起因

围绕着网上的说法和之前的教训,这 hand_failure 谬误应该是 SSL/TLS 握手过程中不信赖服务端证书导致的,那方法很简略,去苹果官网找到苹果服务端提供的证书,导入到 java 的证书信赖库 cacert 文件中即可。

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

如上,导入过程中,发现提醒曾经有这个证书了,过后没想那么多,再导一次试试吧!

导入后,再次运行代码,后果还是报错!

心里又慌乱起来,没招了,于是又百度 /google 去了,但搜寻进去的论断简直都是证书信赖问题,和本人的招式一样!

同时,也理解了一下 SSL/TLS 协定相干常识,大抵握手过程如下:

具体如下:

  1. 客户端发送 clientHello 音讯,通知服务端我应用的 TLS 版本与加密套件 等。
  2. 服务器返回 serverHello 音讯,通知本人抉择哪个 TLS 协定版本与加密套件 等。
  3. 服务器发送 Certification 音讯,将本人的 数字证书(包含服务器名称、CA 和公钥)作为音讯内容发给客户端。
  4. 客户端 Certificate verify 校验服务器的 数字证书 的有效性。
  5. 客户端 Change cipher spec 抉择加密套件并生成 会话密钥(客户端与服务器之间后续的数据传输将应用此会话密钥)。
  6. 客户端、服务器之间发送加密数据。

另外,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 网络异样场景剖析
字符编码解惑

退出移动版