You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js中Google Integrity的JWE解密失败原因求助

Play Integrity API JWE令牌解密失败排查与解决方案

我正在用Node.js/Express编写反向代理以提升API调用安全性,根据文档编写了解密Google Play Integrity API返回的JWE令牌的代码,但即使使用Google Play Console提供的正确DECRYPTION_KEY,解密仍失败。测试用的JWE令牌来自通过Expo Metro运行的应用(而非Play商店正式安装的应用),以下是相关代码:

app.get('/checkGoogleToken', async (req, res) => {
    const tokens = fs.readdirSync('./tokens/')
    console.log('Checking Google Token')
    const token = fs.readFileSync('./tokens/' + tokens[0], 'utf-8')
    console.log(token)
    const { plaintext, protectedHeader } = await jose.compactDecrypt(
        token,
        Buffer.from(process.env.DECRYPTION_KEY, "base64")
    ); /* integrity_token is the token got from Integrity API response in the app. 
    DECRYPTION_KEY is found from Google Play Console */

    console.log(protectedHeader);
    console.log(new TextDecoder().decode(plaintext));

    const { payload, Header = protectedHeader } = await jose.compactVerify(
        plaintext,
        crypto.createPublicKey("-----BEGIN PUBLIC KEY-----\n" +
            process.env.VERIFICATION_KEY +
            "\n-----END PUBLIC KEY-----")
    )
})

核心结论:Expo Metro运行环境确实会导致密钥不匹配

是的,通过Expo Metro直接运行应用而非从Play商店安装,必然会导致Play Integrity API生成的JWE令牌使用不同的密钥体系,原因如下:

  • Play Integrity API的令牌加密逻辑与应用的签名身份强绑定:只有当应用通过Google Play官方签名渠道发布(包括上传App Bundle后由Google签名,或使用「App Signing by Google Play」的自有密钥),Google才会使用你在Play Console配置的正式DECRYPTION_KEY加密JWE令牌。
  • Expo Metro运行的应用使用的是调试签名证书(默认是Expo提供的调试密钥,或你本地生成的调试密钥),而非Play商店的正式签名密钥。此时Google会自动匹配调试签名对应的临时密钥加密令牌,而非你配置的正式密钥,自然无法用正式DECRYPTION_KEY解密。

解决步骤

  1. 获取调试环境对应的解密密钥
    在Play Console的「App Integrity」>「Play Integrity API」页面,找到调试环境配置区:

    • 将你的调试签名证书SHA-256指纹添加到调试允许列表
    • Google会生成对应调试环境的解密密钥,将其配置到你的开发环境变量中
  2. 区分环境加载密钥
    在代码中根据运行环境切换密钥,避免混用:

    const decryptionKey = process.env.NODE_ENV === 'production' 
        ? Buffer.from(process.env.PROD_DECRYPTION_KEY, "base64")
        : Buffer.from(process.env.DEV_DECRYPTION_KEY, "base64");
    const { plaintext, protectedHeader } = await jose.compactDecrypt(token, decryptionKey);
    
  3. 同步验证密钥的环境匹配
    调试环境下的令牌签名公钥也需要使用对应调试环境的密钥,否则后续compactVerify步骤同样会失败,需同步配置调试环境的VERIFICATION_KEY。

  4. 临时测试方案(仅用于开发,禁止生产使用)
    如果需要快速验证逻辑,可临时将调试签名指纹添加到生产环境的允许列表,但测试完成后务必移除,避免引入安全风险。

代码优化建议

  • 替换同步文件操作:fs.readdirSync和fs.readFileSync会阻塞事件循环,建议改用异步版本适配Express异步路由:
    const tokens = await fs.promises.readdir('./tokens/');
    const token = await fs.promises.readFile(`./tokens/${tokens[0]}`, 'utf-8');
    
  • 增加异常捕获:避免因目录为空、文件不存在导致服务崩溃:
    app.get('/checkGoogleToken', async (req, res) => {
        try {
            const tokens = await fs.promises.readdir('./tokens/');
            if (tokens.length === 0) {
                return res.status(400).json({ error: 'No tokens found in directory' });
            }
            const token = await fs.promises.readFile(`./tokens/${tokens[0]}`, 'utf-8');
            // 后续解密逻辑...
        } catch (err) {
            console.error('Error processing token:', err);
            return res.status(500).json({ error: 'Failed to process token' });
        }
    })
    

内容的提问来源于stack exchange,提问作者VictoriaStudios

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 01:31:19