JWS 收据验证与安全性 — 基于 JWSUtil 的支付凭证解码
文章简介
在移动支付场景中,客户端收到的支付凭证需要经过完整性和真实性验证,才能确保用户确实完成了支付。鸿蒙 IAP 使用 JWS(JSON Web Signature)作为支付收据格式,客户端需要将收据解码后发送到服务端进行验证。MoneyTrack 封装了JWSUtil工具类来处理 JWS 的解码过程,本文深入解析 JWS 格式、Base64 URL-safe 编解码、服务端验签及安全性考虑等技术细节。
核心知识点
1. JWS 解码验证流程
以下流程图展示了 JWS 收据从客户端接收到服务端验证的完整链路:
2. JWS 三部分结构的详细说明
JWS 格式遵循 RFC 7515 标准,由三个以点号(.)分隔的部分组成:header.payload.signature。
Header(头部):描述签名的算法和类型。典型内容为{"alg":"RS256","typ":"JWS"},指明使用 RSA-SHA256 算法签名。客户端应解析此字段确认服务端使用的签名算法与预期一致。
Payload(载荷):包含实际的支付凭证数据,如订单 ID、商品 ID、购买时间、用户 ID 等。这是应用最关心的数据部分,需解码后提取订单信息发送给服务端进行业务验证。
Signature(签名):对header.payload的签名数据。服务端使用 AGC 提供的公钥进行验签,确保数据未被篡改。私钥由 AppGallery Connect 安全保管,客户端无法伪造。
// JWS 三段式解码 const jws: string[] = data.split('.'); let header = jws[0]; // 第一段:Header let payload = jws[1]; // 第二段:Payload let signature = jws[2]; // 第三段:Signature if (jws.length < 3) { return ''; } // 格式不合法3. util.Base64Helper 的完整 API
HarmonyOS 的util.Base64Helper提供了完整的 Base64 编解码能力,JWSUtil 借助此类完成 URL-safe 解码:
| API | 说明 | 参数类型 | 返回类型 |
|---|---|---|---|
encodeSync(src) | 标准 Base64 编码 | Uint8Array | Uint8Array |
decodeSync(src) | 标准 Base64 解码 | Uint8Array | Uint8Array |
encodeToStringSync(src) | 编码为字符串 | Uint8Array | string |
JWS 使用 Base64 URL-safe 变体,与标准 Base64 的差异需手动转换:将-替换为+、_替换为/,并补充尾部=填充符:
const base64 = new util.Base64Helper(); const textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true }); // URL-safe → 标准 Base64 const centerLineRegex: RegExp = new RegExp('-', 'g'); const underLineRegex: RegExp = new RegExp('_', 'g'); payload = payload.replace(centerLineRegex, '+').replace(underLineRegex, '/'); // 补充填充符 const pad = payload.length % 4; if (pad) { payload += new Array(4 - pad + 1).join('='); } // 解码 result = textDecoder.decodeToString(base64.decodeSync(payload));4. 服务端验签说明
客户端的 JWS 解码仅用于提取收据内容,真正的签名验证必须在服务端完成。服务端验签流程如下:
- 客户端将解码后的 Payload(或原始 JWS 字符串)发送到服务端。
- 服务端使用 AppGallery Connect 提供的公钥,验证 JWS 签名的合法性。
- 验证通过后,服务端检查 Payload 中的
orderId、productId、purchaseTime等字段是否与预期一致。 - 服务端记录已验证的收据,防止重复使用同一收据激活多个设备。
// 服务端伪代码(Java)PublicKeypublicKey=getAGCPublicKey();JWSVerifierverifier=newRSASSAVerifier(publicKey);SignedJWTsignedJWT=SignedJWT.parse(jwsString);booleanisValid=signedJWT.verify(verifier);5. 安全性考虑
防篡改:JWS 的签名机制确保任何对 Header 或 Payload 的篡改都会被验证环节发现。客户端解码后不应直接信任 Payload 内容,必须依赖服务端验签。
重放攻击防护:收据中包含purchaseTime和orderId。服务端应记录已使用过的orderId,拒绝重复的验证请求。同时建议检查purchaseTime与当前时间的偏差,拒绝超过 5 分钟的老收据。
传输安全:客户端向服务端传输收据时,必须通过 HTTPS 协议,防止中间人窃取收据内容后冒用。
项目代码案例
JWSUtil.ets 的 decodeJwsObj 方法
文件路径:components/membership/src/main/ets/util/JWSUtil.ets
public static decodeJwsObj(data: string): string { const jws: string[] = data.split('.'); let result: string = ''; if (jws.length < 3) { return result; } try { const textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true }); const base64 = new util.Base64Helper(); let payload = jws[1]; payload = payload.replace(new RegExp('-', 'g'), '+').replace(new RegExp('_', 'g'), '/'); const pad = payload.length % 4; if (pad) { payload += new Array(4 - pad + 1).join('='); } result = textDecoder.decodeToString(base64.decodeSync(payload)); } catch (err) { hilog.error(0xFF00, TAG, `decodeJwsObj parse err: ${JSON.stringify(err)}`); } return result; }推荐参考文档
- JSON Web Signature (JWS) RFC 7515 标准
- HarmonyOS util.Base64Helper 文档
- HarmonyOS util.TextDecoder 文档
- @kit.PerformanceAnalysisKit hilog 日志 API
- OWASP 防重放攻击最佳实践