苹果服务端通知v2处理(AppStore Server Notifications V2)

news2024/12/27 20:48:31

苹果服务端通知v2处理

关键词: App Store Server Notifications V2、Python源码、苹果订阅、JWS、x5c、JSON WEB TOKEN

背景

最近要接入苹果订阅功能,调研后发现订阅生命周期内的状态变更是通过苹果服务端通知返回的(什么时候普通内购也能加上减少掉单的概率),
其回调的正确性验证是依靠回调内容里的几个证书,捣鼓这块耗费了好几天时间,所以在此记录一下。

JWS

苹果服务端返回的数据格式为JWS,之前没处理过这种类型的数据,其实本质上还是JWT那一套,关于JWS的介绍
参考IETF RFC 7515-JSON Web Signature

JWS格式与验证

jws格式为 header.payload.signature
其中每部分都是基于urlbase64加密过的,需要使用urlbase64解密得到内容。
其中,header最为关键,其内容为:

{
  "alg": "ES256",
  "x5c": [
    "服务器证书",
    "中间证书",
    "根证书"
  ]
}

alg为本次回调的签名算法,ES256代表为 ECDSA using SHA-256 hash algorithm
x5c则为证书链,其内部的第一个证书为验证本次回调签名所使用,需要先将证书转为X509格式,再从其中解析出公钥,使用公钥对数据进行验签

payload部分就是本次通知的业务数据,此处不做过多描述,参考官方文档

signature则是本次签名的结果。

简单的来说就是该格式包含了详细数据证书链签名算法签名结果
需要我们在本地完成数据的正确性和合法性:

  • 合法性:验证证书链是可信的。
  • 正确性:使用证书链中的服务器证书内的公钥验证数据和签名是正确未经过篡改的。

验证思路

x5c证书链的验证

这块参考了下图
在这里插入图片描述

也就是说,证书链内有三个证书,分别是服务器证书、中间证书、根证书。 其验证顺序是

  • 使用中间证书验证服务器证书
  • 使用根证书验证中间证书
  • 使用根证书验证中间证书

那么根证书本身呢? 则需要用苹果官方的提供的根证书进行验证。如果整个验证流程下来都验证成功了,那么整个证书链就是可信的了。
其中苹果官方提供的根证书为AppleRootCA-G3.cer,需要自己从官网下载,下载地址

Python对证书的验证主要使用了openssl.crypto包下面的X509StoreX509StoreContext
其基本思路为

  • 将可信证书(一般为root证书)先加载至X509Store实例内,然后使用X509StoreContext对待验证证书进行verify_certificate验证
  • 单个证书验证如此,对于一个证书链,优先将待验证的证书验证完毕后加入到X509Store实例内,然后再继续验证后一个即可,以此类推。

python实现代码如下:

from OpenSSL import crypto
def verify_apple_jws_cert_chain(x5c):
    """
    验证苹果server notify的证书链
    'x5c':['服务器证书','中间证书','根证书']
    我们验证顺序: 苹果根证书->x5c根证书, x5c根证书->中间证书, 中间证书->服务器证书
    :param x5c:
    :return:
    """
    if not x5c or not isinstance(x5c, list):
        return "x5c type error"
    # 加载x5c证书,转为X509证书格式
    x5c_cert = []
    try:
        for each in x5c:
            cert = "-----BEGIN CERTIFICATE-----\n" + each + "\n-----END CERTIFICATE-----"
            new_cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
            x5c_cert.append(new_cert)
    except Exception as e:
        return "x5c certification load exception {}".format(e)
    # 加载苹果根证书
    cert_file = open("./AppleRootCA-G3.cer", "rb")
    apple_root_cert = crypto.load_certificate(crypto.FILETYPE_ASN1, cert_file.read())
    cert_file.close()

    # 接下来验证证书链,验证失败会报错:OpenSSL.crypto.X509StoreContextError: unable to get local issuer certificate
    # 首先验证x5c内的根证书
    store = crypto.X509Store()
    store.add_cert(apple_root_cert)
    try:
        store_ctx = crypto.X509StoreContext(store, x5c_cert[2])
        store_ctx.verify_certificate()
    except Exception as e:
        return "verify root certification exception {}".format(e)

    # 接下来验证x5c内的中间证书
    store.add_cert(x5c_cert[2])
    try:
        store_ctx = crypto.X509StoreContext(store, x5c_cert[1])
        store_ctx.verify_certificate()
    except Exception as e:
        return "verify mid certification exception {}".format(e)

    # 最后验证服务器证书
    store.add_cert(x5c_cert[1])
    try:
        store_ctx = crypto.X509StoreContext(store, x5c_cert[0])
        store_ctx.verify_certificate()
    except Exception as e:
        return "verify server certification exception {}".format(e)

    # 最终验证成功
    return ""

JWS的签名验证

验证完证书链后,那么签名的验证就好说了,目前的 jwt库 基本上都支持ES256签名了
不过我们需要先从x5c内获取服务器证书,将其转为X509对象后,获取其中的公钥,并使用公钥来验签,基本代码如下

import jwt
from OpenSSL import crypto

# 获取服务器证书
alg = header.get("alg")
x5c = header.get("x5c")
server_cert = x5c[0]
# 将服务器证书转为X509证书对象
cert = "-----BEGIN CERTIFICATE-----\n" + server_cert + "\n-----END CERTIFICATE-----"
server_cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
# 从证书内解析出公钥
public_key = crypto.dump_publickey(crypto.FILETYPE_PEM, server_cert.get_pubkey()).decode("utf-8")
# 使用公钥对整个jws进行验签
decode_jws = jwt.decode(jws, public_key, algorithms=[alg])

完整的代码和单元测试用例我放在我的github 上了
如果你觉得对你有帮助,希望帮忙点个star。

参考

StoreKit2【附源码】JWS X.509证书链验证
JWS-X.509 Certificate Chain

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/623069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

邮件营销技巧!不想被打入冷宫?这五点让你的邮件不再进垃圾箱

邮件营销凭借其低成本、高效率的优势渐渐地在各个行业开始崭露头角。它既适用于外贸行业来拓展客户、又适用于金融行业来和客户保持联系。除此之外,企业还可以用邮件营销来通知活动信息、产品上新信息等等。 不过,很多人在进行邮件营销的时候经常会遇到“…

说点理论-什么叫TED背景

TED背景的概念 指technology, entertainment, design在英语中的缩写,即技术、娱乐、设计的3个首字母缩定。 TED背景的投影最小为4米*4米,大的有IMAX宽影幕布来打投影。给人感觉为:非常技术、非常高大上、非常专业、非常Fashion。 因此,头部一些公司会专门对于非常重要类…

【STM32】STM32G4系列片内模拟器件-比较器的使用

STM32G4系列片内模拟器件-比较器的使用 1.前言2.CubeMX配置3.HAL库函数 1.前言 STM32G4系列内置了片内模拟比较器,为电机控制、电源监测等方面的应用提供了很大的便利。要使用片内比较器,只需使用STM32CubeMX进行简单配置即可。 2.CubeMX配置 首先&am…

不敲一个代码,10分钟做出数据可视化大屏,还不快来学?

大屏幕实时数据可视化解决方案? 简道云去年举办过一场“最美仪表盘”评选活动,在活动中我们收到了很多精美炫酷的仪表盘,而且这所有的数据可视化仪表盘都是“从业务中来”,“到业务中去”的。 下面举几个例子展示下: 所用工具…

一、kafka入门

Kafka入门 为什么要用消息中间件? 异步处理 场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。 串行方式:将注册信息写入数据库成功后,发送注册邮件&#xff…

最强AIGC实战应用速成指南来了!14天掌握核心技术

‍‍OpenAI 创始人 Sam Altman 在近期采访中说到:" AI 是少有的被严重炒作之后,还被严重低估的东西。" 现在的 AI 几乎把互联网上所有的数据都学习了一遍,相当于一个世界知识的统一建模。在如此规模的参数量下,更好的关…

Java 集合全教程—List

Java 集合全教程_Doker 多克的博客-CSDN博客 Java 集合全教程—Set 接口_Doker 多克的博客-CSDN博客 一、概述 列表是一个有序的集合(有时称为序列)。列表可能包含重复的元素。除了从Collection继承的操作外,List接口还包括以下操作&#x…

Java抽象类介绍

1 问题 声明一个名为Employee的抽象类,其中包含name(姓名)和sex(性别)两个String类型的私有属性,并声明一个继承于Employee抽象类的子类Teacher。 2 方法 2.1 定义一个抽象类:Employee。 2.2 为Employee类设计一个抽象方法。 2.3实现抽象类Em…

vue对接海康摄像头,配合ifarme进行展示。给ifarme点击事件(消除ifarme事件,因为ifarme没有点击事件)

1、在public文件夹下建一个文件ifarme.index&#xff0c;和index.html同级。 <!doctype html> <html><head><title></title><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><meta http-equi…

【熬夜送书 | 第三期】清华社赞助 | 《Java系列丛书》

前言 Java作为一门主流语言&#xff0c;它与其他语言相比&#xff0c;有什么优势呢? Java 是一种广泛使用的高级编程语言&#xff0c;具有多项特性&#xff1a; 1.简单易学&#xff1a;Java 语言的语法与 C 相似&#xff0c;但删掉了复杂的指针、运算符重载等内容&#xff0…

C++继承相关内容(三)

目录 一.单例模式和相关习题 1.不能在外部环境创建该类对象 方法&#xff1a; 代码&#xff1a; 2.创建一个不能被继承的类 方法&#xff1a; 3.创建一个不能被继承&#xff0c;但是可以在外部环境中创建该类对象 方法&#xff1a; ​编辑 4.一个能被继承的类&#xf…

【WinForm】WinForm常见窗体技术汇总

文章目录 前言一、窗体调用外部程序与渐变窗体1、效果2、界面设计3、代码 二、按回车键跳转窗体中的光标焦点1、效果2、界面设计3、代码 三、剪切板操作1、效果2、界面设计3、代码 四、实现拖放操作1、效果2、界面设计3、代码 五、移动的窗体1、效果2、界面设计3、代码 六、抓不…

聚观早报 | OpenAI 没有上市计划;马斯克称未来房价下跌将加速

今日要闻&#xff1a;OpenAI 没有上市计划&#xff1b;马斯克称未来房价下跌将加速&#xff1b;Coinbase被SEC起诉&#xff0c;股价闪崩&#xff1b;库克&#xff1a;苹果正密切关注ChatGPT等&#xff1b;推特正致力于开发视频直播产品 OpenAI没有上市计划 当地时间周二&…

068:cesium lookAtTransform围绕一个固定点上下左右旋转查看

第068个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中查看一个固定的点的情况,从上下左右不同的维度进行查看。这里面使用lookAtTransform这个操作函数。lookAtTransform(transform, offset),这里的offset偏移量可以是笛卡尔坐标或航向/俯仰/范围。 直接复制下面…

Java集合常见面试题集锦

1、介绍Collection框架的结构 集合是Java中的一个非常重要的一个知识点&#xff0c;主要分为List、Set、Map、Queue三大数据结构。它们在Java中的结构关系如下&#xff1a; Collection接口是List、Set、Queue的父级接口。 Set接口有两个常用的实现类&#xff1a;HashSet和Tre…

libmodbus编程笔记

一 基础知识 地址映射值 Modbus寄存器 Modbus寄存器地址分配 Modbus ASCII消息帧格式 Modbus RTU帧格式 Modbus RTU相邻帧间隔 Modbus寻址范围 PDU与ADU的关系 Modbus TCP/IP ADU与PDU的关系 Modbus TCP/IP与Modbus串行消息构成对比 Modbus TCP/IP协议最大帧数据长度为260字…

人工智能-实验四

第四次实验 一.实验目的 ​ 了解深度学习的基本原理。能够使用深度学习开源工具。学习使用深度学习算法求解实际问题。 二.实验原理 1.深度学习概述 ​ 深度学习源于人工神经网络&#xff0c;本质是构建多层隐藏层的人工神经网络&#xff0c;通过卷积&#xff0c;池化&…

【2 微信小程序学习 - 小程序的架构.配置.app与page】

1 小程序的架构模型 为了避免卡顿,优化性能,小程序使用双线程模型. 可以理解为创建了两个webview,一个负责渲染界面,一个负责js脚本处理,通过微信客户端的native进行中转交互. 2 小程序的配置文件 在多人开发中,一般不修改project.config.json避免冲突 ,而是单人修改project…

Intradeco通过适用于Excel的Liquid UI自动执行SAP MM并节省80%的处理时间

背景 Intradeco为服装制造提供整体方法&#xff0c;涵盖所有阶段&#xff1a;从构思阶段到最终产品分销。它已发展成为一家全球垂直制造公司&#xff0c;客户遍布美国、墨西哥和加拿大。 挑战 提高运营效率 原因&#xff1a;人员必须浏览多个 SAP 事务才能为新材料创建采购订单…

2023年牛客网最新Java面试八股文附答案整理(不管工作几年都可以看看)

很多人都说今年对于 IT 行业根本没有所谓的“金三银四”“金九银十”。在各大招聘网站或者软件上不管是大厂还是中小公司大多都是挂个招聘需求&#xff0c;实际并不招人&#xff1b;在行业内的程序员基本都已经感受到了任老前段时间口中所谓的“寒气”。 虽然事实确实是如此&a…