API 加密协议
为什么使用加密协议?
为维护双方数据和信息安全,保护资源方(开发者)提供的资源数据不被第三方窃取,以及双方的通讯不被中间人劫持,我们在设计与资源方 Webhook 通信接口时,使用增强加密的协议保证接口调用的安全性。
加密协议选择
为降低资源方理解和实现的复杂度,我们采取 IETF 标准提案 RFC5861 JWE (JSON Web Encryption) 对应用层 JSON 数据进行数据加密、完整性保护和来源认证,加密后的内容通过 HTTP 协议传输。协议数据结构如下图所示:
作为开放的互联网标准提案,JWE 有很多种开源实现。资源方可以根据自身需求,选择合适的开源函数库集成到后台服务中。
协议细节和示例
发送给 webhook 的请求
小程序开放阿拉丁发送给 webhook 的请求 JSON Object,主要由以下公共字段组成:
|
|
Webhook 返回的结果
Webhook 返回的结果 JSON Object,主要由以下公共字段组成:
|
|
对请求和结果 JSON 消息加密
为了降低加密复杂度和计算开销,JWE 选择的加密算法组合是:
|
|
这一组合。其中:
A128KW
代表密钥生成算法通过预共享密钥 PSK (Pre Shared Key) 生成每次会话使用的内容加密密钥 CEK (Content Encryption Key)。A128CBC-HS256
代表加密算法底层使用 AES_128_CBC_HMAC_SHA_256 算法产生密文以及认证 TAG。除了保护了数据内容之外,同时对数据的来源和完整性进行了签名。
同时,为了尽量节省带宽,我们采取 Compact 格式序列化 JWE 对象。
加密参数
JWE 需要传入预共享密钥 PSK
参数进行初始化。在我们的加密算法组合下,PSK
是 16 字节(128位)长度的密钥,由资源方在开放平台中设置。
由于 PSK
允许为任意二进制字节串,包括不可打印字符。为便于输入和展示,资源方需要在平台中输入 base64url(PSK)
,即经过 base64url 编码的 PSK
。PSK
仅存储在双方服务器中,用于加密内容密钥 CEK,不会在消息中传递。
在 JWE 的 protected header 里,需要额外传输一个 kid
字段,向对方标识使用的是哪个 PSK
。kid
主要为了便于在开发者更新 PSK
时,双方加密通信不受影响。
开发者每一次更新 PSK
,kid
都会 +1。开发者在开放平台页面上,可以看到当前 PSK
对应的 kid
值。当开发者更新 PSK
时,已经收到更新的开放平台服务器会用新的 kid,PSK
组合请求 webhook,未收到更新的开放平台服务器会使用旧的 kid,PSK
组合请求 webhook。资源方可以通过 kid
判断应该使用哪个 PSK
解密请求消息和加密返回消息。
注意:接收方服务器需要使用同一个 kid,PSK
组合加密返回消息,以免发送方服务器无法解密返回消息内容。
会话参数
为了便于双方进行调试或者日志追查,请求方需要在 JWE 的 protected header 里额外传输一个 rid
字段,代表请求会话 ID。rid
的格式为 毫秒时间戳-随机数
,响应方需将 rid
原样返回给发送方以保证响应的是最新的请求。
rid
放在 JWE 协议的 protected header 中,没有被加密,可以通过 base64url decode 直接解码出来。但 rid
是签名内容的一部分,也就是说 rid
如果被篡改会导致整个消息签名不通过。
示例
我们下面以一个例子说明一个遵从 JWE 标准的序列化消息生成过程。开发者可以使用同样的参数对序列化后的 JWE 消息进行解密,以验证加解密实现是否正确。
|
|
通过 HTTP POST 发送
传输层我们采取规范的 HTTP 传输协议,将加密层的内容放入 HTTP body 中,使用 POST 请求发送给接收方。待发送的字符串会以原串的方式放到 body 中,设置 HTTP Header “Content-Type: application/jwt
“ (遵从 RFC7519 Section 10.3 约定)表明数据格式是 JWE 加密数据,以示与常见的 x-www-form-urlencoded
数据区分。
应用层的错误会有应用层的错误码处理,但由于我们仅支持加密传输,未解密前无法看到消息内容。如果发送方采取了不恰当的加密方式(例如出现了bug),接收方无法解密应用层内容,需要返回对应的错误码。我们复用 HTTP 错误码 400 加纯文本出错内容来实现这一功能。
|
|