package net.rafaeltoledo.security.ui; import android.databinding.DataBindingUtil; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.util.Log; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.safetynet.SafetyNet; import com.scottyab.rootbeer.RootBeer; import net.rafaeltoledo.security.R; import net.rafaeltoledo.security.databinding.ActivityMainBinding; import net.rafaeltoledo.security.http.HttpClientProvider; import net.rafaeltoledo.security.http.Tls12SslSocketFactory; import net.rafaeltoledo.security.util.EnvironmentChecker; import net.rafaeltoledo.security.util.InstallationChecker; import net.rafaeltoledo.security.util.SignatureUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.SecureRandom; import java.util.Collections; import java.util.Random; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import okhttp3.Call; import okhttp3.Callback; import okhttp3.ConnectionSpec; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.TlsVersion; public class MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener, MainController { private static final String TAG = MainActivity.class.getSimpleName(); private GoogleApiClient client; private final Random random = new SecureRandom(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); client = new GoogleApiClient.Builder(this) .addApi(SafetyNet.API) .enableAutoManage(this, this) .build(); binding.root.setText(new RootBeer(this).isRooted() ? "Device is rooted" : "Device isn't rooted"); binding.installation.setText(InstallationChecker.verifyInstaller(this) ? "Installed from Play Store" : "Installed from unknown source"); binding.enviroment.setText((EnvironmentChecker.alternativeIsEmulator() ? "Running on an emulator" : "Running on a device") + (EnvironmentChecker.isDebuggable(this) ? " with debugger" : "")); binding.tampering.setText((InstallationChecker.checkPackage(this) ? "The package is consistent" : "The package was modified") + (SignatureUtils.checkSignature(this) ? " and the signature is ok" : " and the signature was changed!")); binding.setController(this); } @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { Log.e(TAG, "Failed to connect to Google Client"); } @Override public void requestSafetyNetCheck() { byte[] nonce = getRequestNonce(); SafetyNet.SafetyNetApi.attest(client, nonce) .setResultCallback(result -> { if (result.getStatus().isSuccess()) { showSafetyNetResult(result.getJwsResult()); } else { Log.e(TAG, "Error on SafetyNet request - Code (" + result.getStatus().getStatusCode() + "): " + "" + result.getStatus().getStatusMessage()); } }); } @Override public void performOkHttpRequest() { OkHttpClient client = HttpClientProvider.getClient(); Request request = new Request.Builder() .url("https://github.com/robots.txt") .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "Failed to perform request", e); } @Override public void onResponse(Call call, Response response) throws IOException { Log.d(TAG, response.body().string()); } }); } private void showSafetyNetResult(String result) { /* 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! */ Log.d(TAG, "Success! SafetyNet result:\n" + result + "\n"); } // Ideally, the Nonce should be generated in your server private byte[] getRequestNonce() { String nonceData = "SafetyNet Sample: " + System.currentTimeMillis(); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); byte[] bytes = new byte[24]; random.nextBytes(bytes); try { byteStream.write(bytes); byteStream.write(nonceData.getBytes()); } catch (IOException e) { Log.e(TAG, "Failed to generate nonce", e); return null; } return byteStream.toByteArray(); } }