3

I want to use the SafetyNet Attestation API (mind that this documentation seems to be outdated since the methods it uses are deprecated). Using the latest version of Play Services (11.0.1) I came up with the following code:

SecureRandom secureRandom = new SecureRandom();
byte[] nonce = new byte[16];
secureRandom.nextBytes(nonce); // just some random bytes for testing

SafetyNet.getClient(this)
    .attest(nonce, API_KEY)
    .addOnCompleteListener(this, task -> {
        if (task.isSuccessful()) {
            SafetyNetApi.AttestationResponse result = task.getResult();
            String jws = result.getJwsResult();
            Log.d(TAG, "JWS: " + jws);
        } else {
            Exception e = task.getException();

            if (e instanceof ApiException) {
                Log.e(TAG, "Attestation failure: " + ((ApiException) e).getStatusMessage() + ", code: " + ((ApiException) e).getStatusCode(), e);
            } else {
                Log.e(TAG, "Attestation failure: " + e, e);
            }
        }
    });

where API_KEY is the API key from the Google Developer Console. This code is called in an Activity's onCreate(...). Whatever I tried, it results in failure and the e is an instance of ApiException, but it does not provide any useful information about what went wrong since the status message is null and the status code is 8, which - according to the documentation - is an "internal error". I tried to call this with a 5 second delay but no success. The test device has API 24 and Google Play services 11.0.55.

Anyone has any idea what goes wrong and what's the solution for this?


Edit: the old SafetyNet.SafetyNetApi.attest(googleApiClient, nonce) way seems to work fine but it's deprecated so I don't want to use it.

Gergely Kőrössy
  • 5,620
  • 3
  • 28
  • 44

3 Answers3

0

Based from this thread, if you get error code 8 (INTERNAL_ERROR), please double check your app registration in dev console. Note that every registered Android client is uniquely identified by the (package name, Android Signing Certificate SHA-1) pair. If you have multiple package names / signing certificate for your debug and production environments, make sure to register every pair of them.

To verify:

  1. Open the Credentials page and select your project
  2. Make sure every pair has an Android typed OAuth 2.0 client IDs. To create a new OAuth 2.0 client ID for your Android client, select New Credentials->OAuth2 Client ID from the dropdown, select Android and input your Package name / Signing-certificate fingerprint there.

If it doesn't work, I recommend you to contact the Google Play team for help. You can reach them from this link here: https://support.google.com/googleplay#topic=3364260&contact=1.

abielita
  • 13,147
  • 2
  • 17
  • 59
  • The deprecated method works fine, so it's obviously not an API key problem. (Also note that in this case it's not the OAuth key what you want because there's no user data involved.) – Gergely Kőrössy Jun 18 '17 at 15:36
0

Ensure that you are using correct WEB API KEY in your following code:

SafetyNet.getClient(this)
    .attest(nonce, WEB_API_KEY)......

See following image to find WEB API KEY: FCM Console

Community
  • 1
  • 1
0
// Build.gradle    
   implementation 'com.google.firebase:firebase-core:17.2.1'    
   implementation 'com.google.firebase:firebase-messaging:20.1.0'    
   implementation 'com.android.support:support-annotations:28.0.0'    
   implementation 'com.google.android.gms:play-services-safetynet:17.0.0'    
   implementation 'com.google.android.gms:play-services-tasks:17.0.0'        



import android.os.Bundle;    
import android.util.Base64;
import android.util.Log;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.gms.safetynet.SafetyNetClient;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.safetynet.SafetyNet;
import com.google.android.gms.safetynet.SafetyNetApi;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.safetynet.SafetyNet;
import com.google.android.gms.safetynet.SafetyNetApi;
import com.google.android.gms.safetynet.SafetyNetClient;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.safetynet.SafetyNetApi.AttestationResponse;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Random;    


@Override
public void onConnected(Bundle bundle) {
    Log.d("My Project Name:", "Google play services connected");
    runSafetyNetTest(mContext);
}


private byte[] getRequestNonce(String data) {
   ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
   byte[] bytes = new byte[24];
   mRandom.nextBytes(bytes);
   try {
       byteStream.write(bytes);
       byteStream.write(data.getBytes());
   } catch (IOException e) {
       return null;
   }


  public void runSafetyNetTest(Context context) {
               String nonceData = "734K78J56KJ745JH78LKJ9CSOC3477tj35f345j7" + System.currentTimeMillis();
               byte[] nonce = getRequestNonce(nonceData);
               SafetyNetClient client = SafetyNet.getClient(context);
               Task<SafetyNetApi.AttestationResponse> task = client.attest(nonce, this.googleDeviceVerificationApiKey);
               task.addOnSuccessListener( mSuccessListener).addOnFailureListener(mFailureListener);
           }


   private OnSuccessListener<SafetyNetApi.AttestationResponse> mSuccessListener =
           new OnSuccessListener<SafetyNetApi.AttestationResponse>() {
               @Override
               public void onSuccess(SafetyNetApi.AttestationResponse attestationResponse) {
                    mResult = attestationResponse.getJwsResult();
                   // writeLog( "Success! SafetyNet result:\n" + mResult + "\n");
                    final String jwsResult = mResult;
                    final SafetyNetResponse response = parseJsonWebSignature(jwsResult);
                    lastResponse = response;

                    //only need to validate the response if it says we pass
                    if (!response.isCtsProfileMatch() || !response.isBasicIntegrity()) {
// This is Result........

                        callback.success(response.isCtsProfileMatch(), response.isBasicIntegrity());
                        return;
                    } else {
                        //validate payload of the response
                        if(true/*validateSafetyNetResponsePayload(response)*/) {
                            if (googleDeviceVerificationApiKey != "")
                            {
                                //if the api key is set, run the AndroidDeviceVerifier
                                AndroidDeviceVerifier androidDeviceVerifier = new AndroidDeviceVerifier(googleDeviceVerificationApiKey, jwsResult);
                                androidDeviceVerifier.verify(new AndroidDeviceVerifier.AndroidDeviceVerifierCallback() {
                                    @Override
                                    public void error(String errorMsg) {
                                        callback.error(RESPONSE_ERROR_VALIDATING_SIGNATURE, "Response signature validation error: " + errorMsg);
                                    }

                                    @Override
                                    public void success(boolean isValidSignature) {
                                        if (isValidSignature) {
                                            callback.success(response.isCtsProfileMatch(), response.isBasicIntegrity());
                                        } else {
                                            callback.error(RESPONSE_FAILED_SIGNATURE_VALIDATION, "Response signature invalid");

                                        }
                                    }
                                });
                            } else {
                                Log.w(TAG, "No google Device Verification ApiKey defined");
                                callback.error(RESPONSE_FAILED_SIGNATURE_VALIDATION_NO_API_KEY, "No Google Device Verification ApiKey defined. Marking as failed. SafetyNet CtsProfileMatch: " + response.isCtsProfileMatch());
                            }
                        } else {
                            callback.error(RESPONSE_VALIDATION_FAILED, "Response payload validation failed");
                        }
                    }
                }
           };

   private OnFailureListener mFailureListener = new OnFailureListener() {
       @Override
       public void onFailure(@NonNull Exception e) {
           // An error occurred while communicating with the service.
           mResult = null;

           if (e instanceof ApiException) {
               // An error with the Google Play Services API contains some additional details.
               ApiException apiException = (ApiException) e;
               writeLog( "Error: " +
                       CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
                       apiException.getStatusMessage());
           } else {
               // A different, unknown type of error occurred.
               writeLog( "ERROR! " + e.getMessage());
           }

       }
   };