Monday, August 15, 2022

Attestation Validation on Server Side using Java



Attestation Posts:



In this post we will review server side validation of an attestation token sent from an android device.

The validation will be done by a java based service.


First, we add a dependency in the pom.xml for Google's play integrity API:


<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-playintegrity</artifactId>
<version>v1-rev20220211-2.0.0</version>
</dependency>


Now we can validate the attestation token:



package com.shieldsquare.ruleslib.rules.features;

import com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.playintegrity.v1.PlayIntegrity;
import com.google.api.services.playintegrity.v1.PlayIntegrityRequestInitializer;
import com.google.api.services.playintegrity.v1.model.DecodeIntegrityTokenRequest;
import com.google.api.services.playintegrity.v1.model.DecodeIntegrityTokenResponse;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;

import java.io.ByteArrayInputStream;
import java.util.List;

public class AttestationServer {

public static void validate(String token,
String nonce,
String sidCredentials) throws Exception {
DecodeIntegrityTokenRequest requestObj = new DecodeIntegrityTokenRequest();
requestObj.setIntegrityToken(token);
GoogleCredentials credentials = GoogleCredentials.fromStream(new ByteArrayInputStream(sidCredentials.getBytes()));
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);

HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();
GoogleClientRequestInitializer initializer = new PlayIntegrityRequestInitializer();

PlayIntegrity.Builder builder = new PlayIntegrity.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
.setApplicationName("my-application")
.setGoogleClientRequestInitializer(initializer);
PlayIntegrity play = builder.build();

DecodeIntegrityTokenResponse response = play.v1().decodeIntegrityToken("com.example.myapp", requestObj)
.execute();

boolean valid = false;
if (response.getTokenPayloadExternal().getRequestDetails().getNonce().equals(nonce)) {
List<String> verdict = response.getTokenPayloadExternal().getDeviceIntegrity().getDeviceRecognitionVerdict();
valid = verdict.contains("MEETS_DEVICE_INTEGRITY");
}

// do something with the result
System.out.println("token valid:" + valid);
}
}



The token is received at the end device, and sent to the server side for validation

The nonce is expected to be generated by the server and sent to the client, as a kind of challenge. Alternatively, the nonce can be some kind of unique identifier of the application on the device, which is not expected to be used in other devices.

The sid credentials is the content of a json file, with credentials for service account that has permissions to validate the attestation token.


Notice that the is a limit for the free token validation calls, and after that you'll need to pay for each call, hence make sure to balance between validation requirements and costs.




No comments:

Post a Comment