Decrypt and verify locally Play Integrity API Token
Asked Answered
R

1

2

I'm trying to implement Play Integrity API in my project. I already implement the logic in an Android app and send the token to my server. I want to decrypt and verify locally because the server don't and won't have internet connection. So I'm following the official guide, but I'm struggling when getting the payload of the JWE. This is what I tried:

String playIntegrityToken = "eyJhbGciOiJBM...";
String base64OfEncodedDecryptionKey = "...";
byte[] decryptionBytes = Base64.decode(base64OfEncodedDecryptionKey);
SecretKey secretKey = new SecretKeySpec(decryptionBytes, 0, decryptionBytes.length, "AES");
JsonWebEncryption jwe = (JsonWebEncryption) JsonWebStructure.fromCompactSerialization(playIntegrityToken);
jwe.setKey(secretKey);
System.out.println(jwe.getPayload());

But I getting the error:

Exception in thread "main" org.jose4j.lang.JoseException: javax.crypto.AEADBadTagException: Tag mismatch!
    at org.jose4j.jwe.SimpleAeadCipher.decrypt(SimpleAeadCipher.java:114)
    at org.jose4j.jwe.SimpleAeadCipher.decrypt(SimpleAeadCipher.java:101)
    at org.jose4j.jwe.AesGcmContentEncryptionAlgorithm.decrypt(AesGcmContentEncryptionAlgorithm.java:79)
    at org.jose4j.jwe.JsonWebEncryption.decrypt(JsonWebEncryption.java:249)
    at org.jose4j.jwe.JsonWebEncryption.getPlaintextBytes(JsonWebEncryption.java:85)
    at org.jose4j.jwe.JsonWebEncryption.getPlaintextString(JsonWebEncryption.java:78)
    at org.jose4j.jwe.JsonWebEncryption.getPayload(JsonWebEncryption.java:93)
    at Main.main(Main.java:17)
Caused by: javax.crypto.AEADBadTagException: Tag mismatch!
    at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:620)
    at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116)
    at com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
    at javax.crypto.Cipher.doFinal(Cipher.java:2168)
    at org.jose4j.jwe.SimpleAeadCipher.decrypt(SimpleAeadCipher.java:110)
    ... 7 more

The library I'm using is Jose4j in its 0.8.0 version.

I also tried changing the library that handle the JWE, for Nimbus JOSE + JWT, the library that decode the decryption key, the SecretKeySpec, the length, the algoritm , but nothing works, What am I doing wrong?

Realgar answered 15/9, 2022 at 4:2 Comment(1)
Have you got any solution for the same?Spense
M
0
private fun googlePlayIntegrity(
    nonce: String
) = scope.launch {

    // cloudProjectNumber through Play Console.
    // get the nonce from your backend or generate it locally or just use a static one like "a0a008af421a4bb4e8344fb8a5103c7ea25226358cd65750f09f998b4bf13307" for testing

    val cloudProjectNumber = BuildConfig.GOOGLE_PLAY_CLOUD_PROJECT_NUMBER.toLong()

    val integrityManager = IntegrityManagerFactory.create(context)

    val integrityTokenRequest = IntegrityTokenRequest.builder()
        .setCloudProjectNumber(cloudProjectNumber)
        .setNonce(nonce)
        .build()

    integrityManager.requestIntegrityToken(integrityTokenRequest)
        .addOnSuccessListener {
            Timber.d("integrity - Success")
            decryptIntegrityToken(it.token())
        }
        .addOnFailureListener {
            Timber.d("integrity - Failure")
        }
}

private fun decryptIntegrityToken(integrityToken: String) {

    // base64OfEncodedDecryptionKey and base64OfEncodedVerificationKey are provided through Play Console.

    val base64OfEncodedDecryptionKey = ""
    val base64OfEncodedVerificationKey = ""

    val decryptionKeyBytes: ByteArray =
        Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT)

    // Deserialized encryption (symmetric) key.
    val decryptionKey: SecretKey = SecretKeySpec(
        /* key */ decryptionKeyBytes,
        /* offset */ 0,
        /* key length */ decryptionKeyBytes.size,
        /* algorithm */ "AES"
    )

    val encodedVerificationKey: ByteArray =
        Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT)

    // Deserialized verification (public) key.
    val verificationKey: PublicKey = KeyFactory.getInstance("EC")
        .generatePublic(X509EncodedKeySpec(encodedVerificationKey))

    val jwe: JsonWebEncryption = JsonWebStructure.fromCompactSerialization(integrityToken) as JsonWebEncryption
    jwe.key = decryptionKey

    // This also decrypts the JWE token.
    val compactJws: String = jwe.payload

    val jws: JsonWebSignature =
        JsonWebStructure.fromCompactSerialization(compactJws) as JsonWebSignature
    jws.key = verificationKey

    // This also verifies the signature.
    val payload: String = jws.payload
    Timber.d("jws - $jws")
    Timber.d("payload - $payload")
}

Don't forget the correct imports:

import android.util.Base64
import org.jose4j.jwe.JsonWebEncryption
import org.jose4j.jws.JsonWebSignature
import org.jose4j.jwx.JsonWebStructure
import java.security.KeyFactory
import java.security.PublicKey
import java.security.spec.X509EncodedKeySpec
import javax.crypto.SecretKey
import javax.crypto.spec.SecretKeySpec

And you'll need this JSON Web Token library in your build.gradle dependencies:

implementation("org.bitbucket.b_c:jose4j:0.9.4")
Moua answered 7/2 at 8:14 Comment(1)
I've also tried the same but it resulted in the following exception : org.jose4j.lang.IntegrityException: A256KW key unwrap/decrypt failed. and checksum failed at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher.engineUnwrap(BaseWrapCipher.java:505) Am I missing anything ?Mistrial

© 2022 - 2024 — McMap. All rights reserved.