使用许可证令牌验证服务的许可情况

Microsoft 游戏开发工具包 (GDK) 运行时中的许可证令牌为游戏服务提供了一种验证方法,验证客户端对于正在运行的应用是否具有有效的许可证,而无需信任该客户端。 这些令牌用于验证电脑上运行的应用的许可证,但 Xbox 应用不需要这些令牌。

许可证令牌格式化为 JSON Web 令牌 (JWT),可使用可用的 JWT 处理程序将其解压缩。 若要测试验证,可在 http://jwt.io 的 JWT 解码工具中输入许可证令牌。

许可证令牌具有下表所示的部分。

部分 说明
标头 提供 x5t 以用于确定要用于验证令牌的证书是否正确。
有效负载 包含 LicenseTokenClaim 值中的 base64 编码的字符串,该字符串提供来自客户端的许可证的附加信息。 还提供 LicenseToken 的到期日期。
签名 用于验证令牌完整性和源的标准 JWT 签名。 使用从 Microsoft 下载的签名证书的公共证书进行验证。

从客户端获取许可证令牌

若要获取许可证令牌,客户端必须调用 XStoreQueryLicenseTokenAsync API,该 API 将传入产品的 Store ID 和自定义开发者字符串,本主题后面将对此进行详细介绍。 然后,此 API 将基于当前登录到 Microsoft Store 应用的用户检索应用的许可证令牌。 如果正在开发沙盒中工作,则测试帐户必须同时登录到 Xbox Windows 应用和 Microsoft Store 应用。 检索到令牌后,应将其发送到自己的服务进行验证。 不应在客户端验证许可证令牌,因为这会否定来自客户端的结果对服务的信任。

有关如何获取许可证令牌的示例,请参阅游戏内商店示例

验证服务的许可证令牌

有关如何处理许可证令牌的完整代码示例,请参阅游戏服务示例

需要从用于签署令牌的证书中获取 RSA 公钥。 若要确定下载证书的完整 URL,需按照以下方式之一确定 certificateId

  • 提取 base64 编码的有效负载中的 certificateId 值。
  • 将标头中的 x5t 值 Base64 解码为字节数组,并将其转换为字符串。 游戏服务示例中提供了如何执行此操作的示例代码。
  • 有了 certificateId 后,在已解码的字符串中获取该值,然后将其追加到 https://licensing.mp.microsoft.com/v8.0/licenseToken/fullCertificate/{certificateId} 以获取证书的直接下载链接。

结果是一个 XML 文档,其中包含可用于创建 X509Certificate2 等证书对象的原始证书数据(有关示例代码,请参阅游戏服务示例)。 应缓存此证书,以免每次验证许可证令牌时都需要下载它。

借助证书的 RSA 公钥,你可使用标准 JWT 签名验证来确保许可证令牌来自可靠的来源,并且令牌的内容不会被修改。

使用许可证令牌中的信息

许可证令牌的有效负载包含一个 LicenseTokenClaim 值,该值是一个由额外 JSON 数据组成的 base64 编码字符串。 解码后可忽略字符串的第一部分,因此请查找表示 JSON 数据开始位置的第一个“{”。 下面的示例演示了此操作。

[Unicode character string that can be ignored]
{
    "certificateId": "C4FC9E583CC4D5FEB96712619B5BE7499FECB5FA",
    "customDeveloperString": "anti-replay string",
    "licensableProducts": [
        {
            "endDate": "9999-12-31T23:59:59.9999999+00:00",
            "isShared": false,
            "id": "fc80277459b04bc7a158b49c0c5574e1",
            "productId": "9NN4ZHKML55R",
            "skuId": "0010",
            "userId": "m8jGdShdpG8vu9nIQiAn3lBIQJ+TD0r2jAJvfmGYmGI="
        }
    ],
    "payload": "T3Jqb3H+YHkjgBksJcsBaL6noHabm5JfyCYaV9nnV+XTiAzNfHKCdUqK2KZkZNk7aYfsVJ0CL2mFQg8XdYtxOv+YmHi+6qhXv6Wp5mx3e4+ZFavbobwjPbVgVsKpDV3TxKdUCIhVPPtDOziqWsUB3+z4plopXM+SargAqqBchQOQklRf5z4NXkAqWer31MmZWwXeEcsfH7Ac/usMlrQakT1IepxnR7+bZIKzp7B9QcWN2lJzyP4TYg8gVnYBGT9cRWxy/IgY0gL5FYNLnDJM1A3D2JcmsCsKuCVpzzn2eXdSAGha00oqBdCcQMr34da54x1s47lvZTdP+Z4Z/BjSQw==",
    "tokenVersion": 1
}

如果客户端具有该产品的有效许可证,则将作为结果在 licensableProducts 列表中返回。 endDate 值以 UTC 时间表示,payload 字符串保留供将来使用。

使用 customDeveloperString 参数防止重播攻击

由于客户端可以截取有效令牌到服务的传输,然后重播该有效令牌,因此请务必正确使用 XStoreQueryLicenseToken API 的 customDeveloperString 参数。 此字符串应在你的服务器上生成,并且对于你想要执行的每个许可证检查,此字符应该是唯一的。 生成字符串后,应将其发送给客户端,然后客户端会将字符串传递到 XStoreQueryLicenseToken API。 然后该值会嵌入到许可证令牌的有效负载中。 它在其中受到保护,不会被令牌上的验证签名修改。

客户端将令牌发送到服务后,你可检查 customDeveloperString 值是否与服务器生成并发送到客户端的值完全匹配。 如果此值不匹配,则可能表示会重播旧令牌以尝试欺骗该服务,因为令牌的所有其他方面仍然有效。 建议对于执行的每个许可证检查,将 customDeveloperString 设置为随机字符串或 GUID。

另请参阅

商业

XStore

XStoreQueryLicenseTokenAsync

XStoreQueryLicenseTokenResult