JWS 收据验证与安全性 — 基于 JWSUtil 的支付凭证解码文章简介在移动支付场景中客户端收到的支付凭证需要经过完整性和真实性验证才能确保用户确实完成了支付。鸿蒙 IAP 使用 JWSJSON Web Signature作为支付收据格式客户端需要将收据解码后发送到服务端进行验证。MoneyTrack 封装了JWSUtil工具类来处理 JWS 的解码过程本文深入解析 JWS 格式、Base64 URL-safe 编解码、服务端验签及安全性考虑等技术细节。核心知识点1. JWS 解码验证流程以下流程图展示了 JWS 收据从客户端接收到服务端验证的完整链路是否IAP SDK 返回 JWS 收据按 . 分割为三段提取 Header 第一段提取 Payload 第二段提取 Signature 第三段Base64 URL-safe 解码Base64 URL-safe 解码Base64 URL-safe 解码解析 Header JSON获取算法信息解析 Payload JSON获取订单信息服务端验签使用公钥验证签名签名验证通过?收据有效激活会员权益收据无效拒绝激活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 的完整 APIHarmonyOS 的util.Base64Helper提供了完整的 Base64 编解码能力JWSUtil 借助此类完成 URL-safe 解码API说明参数类型返回类型encodeSync(src)标准 Base64 编码Uint8ArrayUint8ArraydecodeSync(src)标准 Base64 解码Uint8ArrayUint8ArrayencodeToStringSync(src)编码为字符串Uint8ArraystringJWS 使用 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等字段是否与预期一致。服务端记录已验证的收据防止重复使用同一收据激活多个设备。// 服务端伪代码JavaPublicKeypublicKeygetAGCPublicKey();JWSVerifierverifiernewRSASSAVerifier(publicKey);SignedJWTsignedJWTSignedJWT.parse(jwsString);booleanisValidsignedJWT.verify(verifier);5. 安全性考虑防篡改JWS 的签名机制确保任何对 Header 或 Payload 的篡改都会被验证环节发现。客户端解码后不应直接信任 Payload 内容必须依赖服务端验签。重放攻击防护收据中包含purchaseTime和orderId。服务端应记录已使用过的orderId拒绝重复的验证请求。同时建议检查purchaseTime与当前时间的偏差拒绝超过 5 分钟的老收据。传输安全客户端向服务端传输收据时必须通过 HTTPS 协议防止中间人窃取收据内容后冒用。项目代码案例JWSUtil.ets 的 decodeJwsObj 方法文件路径components/membership/src/main/ets/util/JWSUtil.etspublic 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 日志 APIOWASP 防重放攻击最佳实践
HarmonyOS掌上记账APP开发实践第65篇:JWS 收据验证与安全性 — 基于 JWSUtil 的支付凭证解码
JWS 收据验证与安全性 — 基于 JWSUtil 的支付凭证解码文章简介在移动支付场景中客户端收到的支付凭证需要经过完整性和真实性验证才能确保用户确实完成了支付。鸿蒙 IAP 使用 JWSJSON Web Signature作为支付收据格式客户端需要将收据解码后发送到服务端进行验证。MoneyTrack 封装了JWSUtil工具类来处理 JWS 的解码过程本文深入解析 JWS 格式、Base64 URL-safe 编解码、服务端验签及安全性考虑等技术细节。核心知识点1. JWS 解码验证流程以下流程图展示了 JWS 收据从客户端接收到服务端验证的完整链路是否IAP SDK 返回 JWS 收据按 . 分割为三段提取 Header 第一段提取 Payload 第二段提取 Signature 第三段Base64 URL-safe 解码Base64 URL-safe 解码Base64 URL-safe 解码解析 Header JSON获取算法信息解析 Payload JSON获取订单信息服务端验签使用公钥验证签名签名验证通过?收据有效激活会员权益收据无效拒绝激活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 的完整 APIHarmonyOS 的util.Base64Helper提供了完整的 Base64 编解码能力JWSUtil 借助此类完成 URL-safe 解码API说明参数类型返回类型encodeSync(src)标准 Base64 编码Uint8ArrayUint8ArraydecodeSync(src)标准 Base64 解码Uint8ArrayUint8ArrayencodeToStringSync(src)编码为字符串Uint8ArraystringJWS 使用 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等字段是否与预期一致。服务端记录已验证的收据防止重复使用同一收据激活多个设备。// 服务端伪代码JavaPublicKeypublicKeygetAGCPublicKey();JWSVerifierverifiernewRSASSAVerifier(publicKey);SignedJWTsignedJWTSignedJWT.parse(jwsString);booleanisValidsignedJWT.verify(verifier);5. 安全性考虑防篡改JWS 的签名机制确保任何对 Header 或 Payload 的篡改都会被验证环节发现。客户端解码后不应直接信任 Payload 内容必须依赖服务端验签。重放攻击防护收据中包含purchaseTime和orderId。服务端应记录已使用过的orderId拒绝重复的验证请求。同时建议检查purchaseTime与当前时间的偏差拒绝超过 5 分钟的老收据。传输安全客户端向服务端传输收据时必须通过 HTTPS 协议防止中间人窃取收据内容后冒用。项目代码案例JWSUtil.ets 的 decodeJwsObj 方法文件路径components/membership/src/main/ets/util/JWSUtil.etspublic 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 日志 APIOWASP 防重放攻击最佳实践