node.jsjwejosegoogle-play-integrity-api

Decrypt and verify locally Play Integrity API Token using NodeJS


Google gives an option to locally extract the play Integrity API Token. Even example with Java is provided here. But many servers are Node.js based. So, how can I decrypt the Play Integrity API token locally with Node.js?


Solution

  • Update:

    Now Google API for Play Integrity is available. We don't need jose package. To work with google api:

    npm install @googleapis/playintegrity

    Then follow this code example:

    const playintegrity = require("@googleapis/playintegrity");
    ...
    ...
    ...
    const auth = new playintegrity.auth.GoogleAuth({
      keyFilename: __dirname + '/<key-file-name>.json',
      /* Scopes can be specified either as an array or as a single, 
        space-delimited string.*/
      scopes: ['https://www.googleapis.com/auth/playintegrity']
    });
    const authClient = await auth.getClient();
    
    const client = await playintegrity.playintegrity({
      version: 'v1',
      auth: authClient
    });
    
    const createResponse = await client.v1
         .decodeIntegrityToken({ 
              integrity_token: req.body.integrity_token, 
              packageName: <package-name> 
         });
    
    const payloadJson = createResponse.data.tokenPayloadExternal;
    
    const nonce = Buffer.from(
              payloadJson.requestDetails.requestHash,
              "base64"
          ).toString(); /* the actual nonce message sent from app */
    

    22 September 2022:

    This example shows how to extract Json Web Signature (JWS) from Json Web Encryption (JWE) token extracted from the Play Integrity Token using Google Play Console provided Decryption key, which is verified with the Google Play Console provided Verification key to extract the actual payload JSON string.

    But the problem is, it's written in Java and uses Java based libraries.

    Luckily, there is a Node.js supported JavaScript npm module available named jose, which can handle this in NodeJS.

    Just npm install jose then follow the below code sample:

    import crypto from "crypto";
    import * as jose from "jose";
    ...
    ...
    ...
    const { plaintext, protectedHeader } = await jose.compactDecrypt(
        req.body.integrity_token, 
        Buffer.from(config.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" + 
            config.VERIFICATION_KEY + 
            "\n-----END PUBLIC KEY-----")
    );
    
    console.log(Header);
    const payloadText = new TextDecoder().decode(payload);
    console.log(payloadText);
    const payloadJson = JSON.parse(payloadText);
    
    console.log(payloadJson.requestDetails.nonce); /* the encrypted nonce message sent from app. */
    
    console.log(Buffer.from(
        payloadJson.requestDetails.nonce, 
        "base64"
    ).toString()); /* the actual nonce message sent from app. */
    

    To get Decryption key and Verification key from Google Play Console, see this answer.