| page.title=Checking Device Compatibility with SafetyNet |
| |
| @jd:body |
| |
| |
| <div id="tb-wrapper"> |
| <div id="tb"> |
| |
| <h2>In this document</h2> |
| <ol> |
| <li><a href="#tos">Additional Terms of Service</a></li> |
| <li><a href="#connect-play">Connect to Play Services</a></li> |
| <li><a href="#cts-check">Requesting a Compatibility Check</a> |
| <ol> |
| <li><a href="#single-use-token">Obtain Single Use Token</a></li> |
| <li><a href="#compat-check-request">Send Compatibility Check Request</a></li> |
| <li><a href="#compat-check-response">Read Compatibility Check Response</a></li> |
| <li><a href="#verify-compat-check">Verify Compatibility Check Response</a></li> |
| </ol> |
| </li> |
| </ol> |
| |
| </div> |
| </div> |
| |
| <p> |
| SafetyNet provides services for analyzing the configuration of a particular device, to make sure |
| that apps function properly on a particular device and that users have a great experience. |
| </p> |
| |
| <p> |
| The service provides an API your app can use to analyze the device where it is installed. The API |
| uses software and hardware information on the device where your app is installed to create a |
| profile of that device. The service then attempts to match it to a list of device models that |
| have passed Android compatibility testing. This check can help you decide if the device is |
| configured in a way that is consistent with the Android platform specifications and has the |
| capabilities to run your app. |
| </p> |
| |
| <p> |
| This document shows you how to use SafetyNet for analyzing a device and help you determine if |
| your app will function as expected on that device. |
| </p> |
| |
| <h2 id="tos"> |
| Additional Terms of Service |
| </h2> |
| |
| <p> |
| By accessing or using the SafetyNet APIs, you agree to the <a href= |
| "https://developers.google.com/terms/">Google APIs Terms of Service</a>, and to these Additional |
| Terms. Please read and understand all applicable terms and policies before accessing the APIs. |
| </p> |
| |
| <div class="sdk-terms" onfocus="this.blur()" style="width:678px"> |
| <h3 class="norule">SafetyNet Terms of Service</h3> |
| As with any data collected in large volume from in-the-field observation, there is a chance of |
| both false positives and false negatives. We are presenting the data to the best of our |
| understanding. We extensively test our detection mechanisms to ensure accuracy, and we are |
| committed to improving those methods over time to ensure they continue to remain accurate. |
| |
| You agree to comply with all applicable law, regulation, and third party rights (including |
| without limitation laws regarding the import or export of data or software, privacy, and local |
| laws). You will not use the APIs to encourage or promote illegal activity or violation of third |
| party rights. You will not violate any other terms of service with Google (or its affiliates). |
| |
| You acknowledge and understand that the SafetyNet API works by collecting hardware and software |
| information, such as device and application data and the results of integrity checks, and sending |
| that data to Google for analysis. Pursuant to Section 3(d) of the |
| <a href= "https://developers.google.com/terms/">Google APIs Terms of Service</a>, you agree that if |
| you use the APIs that it is your responsibility to provide any necessary notices or consents for the |
| collection and sharing of this data with Google. |
| </div> |
| |
| <h2 id="connect-play"> |
| Connect to Google Play Services |
| </h2> |
| |
| <p> |
| The SafetyNet API is part of Google Play services. To connect to the API, you need to create an |
| instance of the Google Play services API client. For details about using the client in your app, |
| see <a href="{@docRoot}google/auth/api-client.html#Starting">Accessing Google |
| APIs</a>. Once you have established a connection to Google Play services, you can use the Google |
| API client classes to connect to the SafetyNet API. |
| </p> |
| |
| <p> |
| To connect to the API, in your activity's <a href= |
| "{@docRoot}reference/android/app/Activity.html#onCreate(android.os.Bundle)">onCreate()</a> |
| method, create an instance of Google API Client using <a href= |
| "{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html"> |
| {@code GoogleApiClient.Builder}</a>. Use the builder to add the SafetyNet API, as shown in the |
| following code example: |
| </p> |
| |
| <pre> |
| protected synchronized void buildGoogleApiClient() { |
| mGoogleApiClient = new GoogleApiClient.Builder(this) |
| .addApi(SafetyNet.API) |
| .addConnectionCallbacks(myMainActivity.this) |
| .build(); |
| } |
| </pre> |
| |
| <p class="note"> |
| <strong>Note:</strong> You can only call these methods after your app has established a connection to |
| Google Play services by receiving the <a href= |
| "{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"> |
| {@code onConnected()}</a> callback. For details about listening for a completed client connection, |
| see <a href="{@docRoot}google/auth/api-client.html#Starting">Accessing Google APIs</a>. |
| </p> |
| |
| <h2 id="cts-check"> |
| Requesting a Compatibility Check |
| </h2> |
| |
| <p> |
| A SafetyNet compatibility check allows your app to check if the device where it is running |
| matches the profile of a device that has passed Android compatibility testing. The compatibility |
| check creates a device profile by gathering information about the device hardware and software |
| characteristics, including the platform build. |
| </p> |
| |
| <p> |
| Using the API to perform a check requires a few implementation steps in your app. Once you have |
| established a connection to Google Play services and requested the SafetyNet API from the Google |
| API client, your app can then perform the following steps to use the service: |
| </p> |
| |
| <ul> |
| <li>Obtain a single use token |
| </li> |
| |
| <li>Send the compatibility check request |
| </li> |
| |
| <li>Read the response |
| </li> |
| |
| <li>Validate the response |
| </li> |
| </ul> |
| |
| <p> |
| For more information about Android compatibility testing, see <a href= |
| "https://source.android.com/compatibility/index.html" class="external-link"> |
| Android Compatibility</a> and the <a href= |
| "https://source.android.com/compatibility/cts-intro.html" class="external-link"> |
| Compatibility Testing Suite</a> (CTS). |
| </p> |
| |
| <p> |
| SafetyNet checks use network resources, and so the speed of responses to requests can vary, |
| depending on a device's network connection status. The code described in this section should be |
| executed outside of your app's main execution thread, to avoid pauses and unresponsiveness in |
| your app user interface. For more information about using separate execution threads, see |
| <a href="{@docRoot}training/multiple-threads/index.html">Sending Operations |
| to Multiple Threads</a>. |
| </p> |
| |
| <h3 id="single-use-token"> |
| Obtain a single use token |
| </h3> |
| |
| <p> |
| The SafetyNet API uses security techniques to help you verify the integrity of the communications |
| between your app and the service. When you request a compatibility check, you must provide a |
| single use token in the form of a number used once, or <em>nonce</em>, as part of your request. A |
| nonce is a random token generated in a cryptographically secure manner. |
| </p> |
| |
| <p> |
| You can obtain a nonce by generating one within your app each time you make a compatibility check |
| request. As a more secure option, you can obtain a nonce from your own server, using a secure |
| connection. |
| </p> |
| |
| <p> |
| A nonce used with a SafetyNet request should be at least 16 bytes in length. After you make a |
| check request, the response from the SafetyNet service includes your nonce, so you can verify it |
| against the one you sent. As the name indicates, you should only use a nonce value once, for a |
| single check request. Use a different nonce for any subsequent check requests. For tips on using |
| cryptography functions, see <a href= |
| "{@docRoot}training/articles/security-tips.html#Crypto">Security Tips</a>. |
| </p> |
| |
| <h3 id="compat-check-request"> |
| Send the compatibility check request |
| </h3> |
| |
| <p> |
| After you have established a connection to Google Play services and created a nonce, you are |
| ready to make a compatibility check request. Since the response to your request may not be |
| immediate, you set up a callback listener to catch the response from the service, as shown in the |
| following code example: |
| </p> |
| |
| <pre> |
| byte[] nonce = getRequestNonce(); // Should be at least 16 bytes in length. |
| SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce) |
| .setResultCallback(new ResultCallback<SafetyNetApi.AttestationResult>() { |
| |
| @Override |
| public void onResult(SafetyNetApi.AttestationResult result) { |
| Status status = result.getStatus(); |
| if (status.isSuccess()) { |
| // Indicates communication with the service was successful. |
| // result.getJwsResult() contains the result data |
| } else { |
| // An error occurred while communicating with the service |
| } |
| } |
| }); |
| </pre> |
| |
| <p> |
| The <a href= |
| "{@docRoot}reference/com/google/android/gms/common/api/Status.html#isSuccess()"> |
| {@code isSuccess()}</a> |
| method indicates whether or not communication with the service was successful, but does not |
| indicate if the device has passed the compatibility check. The next section discusses how to read |
| the check result and verify its integrity. |
| </p> |
| |
| <h3 id="compat-check-response"> |
| Read the compatibility check response |
| </h3> |
| |
| <p> |
| When your app communicates with SafetyNet, the service provides a response containing the result |
| and additional information to help you verify the integrity of the message. The result is |
| provided as a <a href= |
| "{@docRoot}reference/com/google/android/gms/safetynet/SafetyNetApi.html"> |
| {@code AttestationResult}</a> |
| object. Use the <a href= |
| "{@docRoot}reference/com/google/android/gms/safetynet/SafetyNetApi.AttestationResult.html#getJwsResult()"> |
| {@code getJwsResult()}</a> method of this object to obtain the data of the request. The response is |
| formatted as a <a href="https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-36" class="external-link"> |
| JSON Web Signature</a> (JWS), the following JWS excerpt shows the format of the payload data: |
| </p> |
| |
| <pre> |
| { |
| "nonce": "R2Rra24fVm5xa2Mg", |
| "timestampMs": 9860437986543, |
| "apkPackageName": "com.package.name.of.requesting.app", |
| "apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the |
| certificate used to sign requesting app"], |
| "apkDigestSha256": "base64 encoded, SHA-256 hash of the app's APK", |
| "ctsProfileMatch": true, |
| } |
| </pre> |
| |
| <p> |
| If the value of {@code ctsProfileMatch} is {@code true}, this indicates that the device |
| profile matches a device that has passed Android compatibility testing. If the output of the |
| <a href= |
| "{@docRoot}reference/com/google/android/gms/safetynet/SafetyNetApi.AttestationResult.html#getJwsResult()"> |
| {@code getJwsResult()}</a> method is null or contains an {@code error:} field, then communication |
| with the service failed and should be retried. You should use an <a class="external-link" href= |
| "https://developers.google.com/api-client-library/java/google-http-java-client/backoff"> |
| exponential backoff</a> technique for retries, to avoid flooding the service with additional requests. |
| </p> |
| |
| <h3 id="verify-compat-check"> |
| Verify the compatibility check response |
| </h3> |
| |
| <p> |
| You should take steps to make sure the response received by your app actually came from the |
| SafetyNet service and matches the request data you provided. Follow these steps to verify the |
| origin of the JWS message: |
| </p> |
| |
| <ul> |
| <li>Extract the SSL certificate chain from the JWS message. |
| </li> |
| |
| <li>Validate the SSL certificate chain and use SSL hostname matching to verify that the leaf |
| certificate was issued to the hostname {@code attest.android.com}. |
| </li> |
| |
| <li>Use the certificate to verify the signature of the JWS message. |
| </li> |
| </ul> |
| |
| <p> |
| After completing this validation, you should also check the data of the JWS message to make sure |
| it matches your original request, including the nonce, timestamp, package name, and the SHA-256 |
| hashes. You can perform these validation steps within your app, or as a more secure option, send |
| the entire JWS response to your own server for verification, via a secure connection. |
| </p> |
| |
| <h4> |
| Validating the response with Google APIs |
| </h4> |
| |
| <p> |
| Google provides an Android Device Verification API for validating the output of the SafetyNet |
| compatibility check. This API performs a validation check on the JWS message returned from the |
| SafetyNet service. |
| </p> |
| |
| <p> |
| To enable access to the Android Device Verification API: |
| </p> |
| |
| <ol> |
| <li>Go to the <a href="https://console.developers.google.com/" class="external-link"> |
| Google Developers Console</a>. |
| </li> |
| |
| <li>Select a project, or create a new one. |
| </li> |
| |
| <li>In the sidebar on the left, expand <strong>APIs & auth</strong>. |
| Next, click <strong>APIs</strong>. In the |
| list of APIs, make sure all of the APIs you are using show a status of <strong>ON</strong>. |
| </li> |
| |
| <li>In the <strong>Browse APIs</strong> list, find the |
| <strong>Android Device Verification API</strong> and turn it |
| on. |
| </li> |
| |
| <li>Obtain your API key by expanding <strong>APIs & auth</strong> and |
| clicking <strong>Credentials</strong>. |
| Record the <strong>API KEY</strong> (<em>not</em> the <em>Android Key</em>) value on this page for later use. |
| </li> |
| </ol> |
| |
| <p> |
| After enabling this API for your project, you can call the verification service from your app or |
| server. You need the contents of the JWS message from the SafetyNet API and your API key to call |
| the verification API and get a result. |
| </p> |
| |
| <p> |
| To use the Android Device Verification API: |
| </p> |
| |
| <ol> |
| <li>Create a JSON message containing the entire contents of the JWS message in the following |
| format: |
| <pre> |
| { "signedAttestation": "<output of getJwsResult()>" } |
| </pre> |
| </li> |
| |
| <li>Use an HTTP POST request to send the message with a Content-Type of {@code "application/json"} |
| to the following URL: |
| <pre> |
| https://www.googleapis.com/androidcheck/v1/attestations/verify?key=<your API key> |
| </pre> |
| </li> |
| |
| <li>The service validates the integrity of the message, and if the message is valid, it returns a |
| JSON message with the following contents: |
| |
| <pre> |
| { “isValidSignature”: true } |
| </pre> |
| </li> |
| </ol> |
| |
| <p class="note"> |
| <strong>Important:</strong> This use of the Android Device Verification API only validates that the |
| provided JWS message was received from the SafetyNet service. It <em>does not</em> verify that the |
| payload data matches your original compatibility check request. |
| </p> |