Detect Rooted Device By Using SafetyNet - Failure Listener

705 views Asked by At

I'm trying to detect rooted device on my app before my login process. Regarding to this answer , I decided to use SafetyNet. I edited this code like this:

public class RootedDeviceCheckWithSafetyNetAttestation {
    public static AtomicInteger sendSafetyNetRequest(Activity context) {

        AtomicInteger rootCheck = new AtomicInteger();

        if(GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS) {
            Log.e(TAG, "The SafetyNet Attestation API is available");

            // TODO(developer): Change the nonce generation to include your own, used once value,
            // ideally from your remote server.

            String nonceData = "Safety Net Sample: " + System.currentTimeMillis();
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
            Random mRandom = new SecureRandom();

            byte[] bytes = new byte[24];
            mRandom.nextBytes(bytes);
            try {
                byteStream.write(bytes);
                byteStream.write(nonceData.getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }

            byte[] nonce = byteStream.toByteArray();

            SafetyNetClient client = SafetyNet.getClient(context);
            Task<SafetyNetApi.AttestationResponse> task = client.attest(nonce, MY-API-KEY);

            task.addOnSuccessListener(context, attestationResponse -> {


                /*
                       TODO(developer): Forward this result to your server together with
                       the nonce for verification.
                       You can also parse the JwsResult locally to confirm that the API
                       returned a response by checking for an 'error' field first and before
                       retrying the request with an exponential backoff.
                       NOTE: Do NOT rely on a local, client-side only check for security, you
                       must verify the response on a remote server!
                      */

                String jwsResult = attestationResponse.getJwsResult();

                Log.e(TAG, "Success! SafetyNet result:\n" + jwsResult + "\n");

                if (jwsResult == null) {
                    Log.e(TAG, "jwsResult Null");

                }
                final String[] jwtParts = jwsResult.split("\\.");

                if (jwtParts.length == 3) {
                    String decodedPayload = new String(Base64.decode(jwtParts[1], Base64.DEFAULT));
                    try {
                        JSONObject jsonObject = new JSONObject(decodedPayload);
                        //Toast.makeText(context, "ctsProfileMatch:" + jsonObject.getString("ctsProfileMatch")+","+"basicIntegrity:" + jsonObject.getString("basicIntegrity"), Toast.LENGTH_SHORT).show();
                        if((jsonObject.getString("ctsProfileMatch").equals("false")) && (jsonObject.getString("basicIntegrity").equals("false"))){
                            rootCheck.set(4);
                        }
                        else{
                            rootCheck.set(1);
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                        rootCheck.set(2);
                    }
                    //Toast.makeText(context, decodedPayload, Toast.LENGTH_SHORT).show();
                    //Log.e(TAG, "decodedPayload :     " + decodedPayload);
                }


            });

            task.addOnFailureListener(context, e -> {
                // An error occurred while communicating with the service.
                String mResult = null;
                rootCheck.set(5);

                if (e instanceof ApiException) {
                    // An error with the Google Play Services API contains some additional details.
                    ApiException apiException = (ApiException) e;

                    Log.e(TAG, "Error: " +
                            CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
                            apiException.getStatusMessage());
                    Toast.makeText(context, "Error: " +
                            CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
                            apiException.getStatusMessage(), Toast.LENGTH_LONG).show();
                } else {
                    // A different, unknown type of error occurred.
                    Log.e(TAG, "ERROR! " + e.getMessage());
                    Toast.makeText(context, "ERROR! " + e.getMessage(), Toast.LENGTH_LONG).show();
                }

            });

        } else {
            Log.e(TAG, "Prompt user to update Google Play services.");
            rootCheck.set(3);

        }
        return rootCheck;
    }
}

When I try to on emulator or rooted device, I can see that my rootCheck variable becomes 4 as I written in code.

The problem is; in real phones, at first login, my code can detect rooted device too. But if I close the app and kill from background and re-open again, I see that my code gives this toast Error: CANCELLED: null so I see that rootCheck variable 5 which means that jumps to task.addOnFailureListener area in if (e instanceof ApiException). This problem causes on rooted and normal device, doesn't matter.

What is the problem? Can you help me ?

0

There are 0 answers