Google Play Service中安全检测API的使用说明

本篇博客主要记录一下如何利用Google Play Service进行安全检测。

1 Google Play Service的使用原理 
Google Play Service提供了大量的API供用户使用, 
以便让应用低成本地集成Google最新的产品及功能。

应用使用Google Play Service时,基本的原理如下图所示,其中: 
1、应用在使用Google Play Service时, 
必须集成Google提供的Client Library, 
通过Client Library,应用才能与Google Play Service进行IPC通信。

2、Google Play Service是独立运行在系统后台的服务, 
与Google Play Store共同发布。

3、Google Play Store负责更新Google Play Service。 
Google Play Service中安全检测API的使用说明_第1张图片 
可以看出,Google通过引入Client Library后, 
将应用与Google Play Service解耦。

这样即使不修改应用,只要更新了Google Play Services, 
应用就能够使用到Google提供功能的最新版本。 
同时,Client Library还可以进行准入控制等相关操作。

2 集成Client Library 
Google Play Service中安全检测API的使用说明_第2张图片 
为了使用Client Library,首先我们需要利用Android Studio的SDK Manager加载Google Repository。 
如上图所示,主要是勾选其中的红线部分,然后点击下载即可。

下载完Google Repository后,只需要修改应用Module对应的build.gradle文件, 
加载对应的库文件即可。

例如:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    .....................

    //添加对play-service的依赖
    //Google Play Service更新后,修改对应的版本号
    compile 'com.google.android.gms:play-services:11.0.1'
}
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里需要注意的是,com.google.android.gms:play-services引入的是Google Play Service全体API的集合。

当我们仅需要特定API接口时,可以仅引入独立的库, 
这样可以减小应用的体积,避免应用中方法数过多。

例如,仅需要SafetyNet时,可以仅compile:

com.google.android.gms:play-services-safetynet:11.0.1
 
 
   
   
   
   
  • 1

目前,Play Service定义的所有独立API库可以参考如下链接: 
https://developers.google.com/android/guides/setup

3 确保设备上的Google Play Service可用 
当应用导入了Client Library后,我们就可以在应用中使用Client Library提供的接口了。

不过,我们知道一个应用将被安装到不同厂商的机器上, 
部分厂商的ROM中并没有集成Google Play Store,更别提Google Play Service了。

因此,当应用需要使用Google Play Service的功能时, 
首先需要做的就是判断当前设备是否具有对应的能力。

目前从文档来看,判断Google Play Service是否可用, 
最好的办法就是使用Client Library中GoogleApiAvailability提供的接口。

具体的做法类似于:

//如果整个应用都依赖于Google Play Service
//那么就在MainActivity的onResume接口中进行判断
//如果只有部分功能依赖该服务,可在具体使用前进行判断
@Override
protected void onResume() {
    super.onResume();

    //获取GoogleApiAvailability的单例
    GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();

    //利用接口判断device是否支持Google Play Service
    int ret = googleApiAvailability.isGooglePlayServicesAvailable(this);

    //支持的话, 结果将返回SUCCESS
    if (ret == ConnectionResult.SUCCESS) {
        Log.d(TAG, "This phone has available google service inside");
    .............
    } else {
        Log.e(TAG, "This phone don't have available google service inside");

        //不支持时,可以利用getErrorDialog得到一个提示框, 其中第2个参数传入错误信息
        //提示框将根据错误信息,生成不同的样式
        //例如,我自己测试时,第一次Google Play Service不是最新的,
        //对话框就会显示这些信息,并提供下载更新的按键
        googleApiAvailability.getErrorDialog(this, ret, 0).show();
    }
}
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

4 利用Google API Client访问服务 
当device支持Google Play Service时,应用就可以使用对应的功能了。

如下图所示,应用实际上必须使用Client Library中的Google API Client来访问具体的服务。 
Google Play Service中安全检测API的使用说明_第3张图片 
从图中可以看出,Google API Client作为应用与服务的桥梁,负责具体的通信细节。

在应用中获取Google API Client的方法如下所示:

private GoogleApiClient mGoogleApiClient;

//如果在Activity的onStart函数被调用前,创建出GoogleApiClient对象,
//则可以利用AutoManage, 使得该对象在onStart后, 自动连接GooglePlayService
private void connectGooglePlayService() {
    Log.d(TAG, "connect to google play service");

    mGoogleApiClient = new GoogleApiClient.Builder(this)
        //这里使用自动管理,手动连接的话,可以获取client后,主动调用其connect接口
        //传入FailedListener,以便处理失败
        .enableAutoManage(this, new FailedListener())
        //我们关注安全检测,因此可以添加SafetyNet.API
        //不过这种用法目前已经deprecated了
        .addApi(SafetyNet.API)
        //添加回调对象,连接成功或失败均有通知
        .addConnectionCallbacks(new ConnectionCallback())
        .build();
}

private class FailedListener implements GoogleApiClient.OnConnectionFailedListener {
    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Log.e(TAG, "connect failed: " + connectionResult);
        //可以做一些错误处理
        ..............
    }
}

private class ConnectionCallback implements GoogleApiClient.ConnectionCallbacks {
    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Log.d(TAG, "connect to google play service success");
        //连接成功, 进行相应的工作
        ...........
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.e(TAG, "connect to google play service fail");
        //连接失败,进行处理
        ...........
    }
}
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

一旦应用创建出GoogleApiClient,同时成功连接Google Play Service后, 
就可以通过对应的API,使用相应的功能了。

5 SafetyNet的安全检测功能 
接下来我们以SafetyNet为例,看看如何使用Google Play Service提供的安全检测功能。

5.1 Attestation API 
从文档和测试返回的结果来看,Attestation API主要用于检测: 
应用所在设备Android运行环境的安全性和兼容性,有点类似CTS测试的味道。

需要注意的是: 
在使用这个API前,需要进入 Google Developers Console, 
开通 Android Device Verification API 并申请对应的Apk key。 
具体的网址可以自行搜索,有Google邮箱即可申请。

申请到key值后,需要在应用的AndroidManifest.xml中添加如下内容:

data android:name="com.google.android.safetynet.ATTEST_API_KEY"
    --这里*隐藏了部分信息--> 
    android:value="AIzaSyCPVFlNC********-*********2NcvGi2sj0" />
 
 
   
   
   
   
  • 1
  • 2
  • 3

当添加完key值后,应用就可以使用SafetyNet的attest接口了。

这里我们以一种老式的调用方式为例,介绍一下API的使用方式:

private void attest() {
    // attest接口需要一个token,且长度不小于16 bytes
    byte[] nonce = getRequestNonce(); // Should be at least 16 bytes in length.

    //可以看到,接口中传入了mGoogleApiClient
    //可以想到,其底层实现需要依赖于mGoogleApiClient来通信
    SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
            //设置回调接口,当连接服务得到返回结果后就会通知
            //可以看到这里是种异步回调的设计
            .setResultCallback(new ResultCallback() {
                @Override
                public void onResult(SafetyNetApi.AttestationResult result) {
                    Status status = result.getStatus();

                    //服务端成功返回结果后,就可以取出信息了
                    if (status.isSuccess()) {
                        //Attest得到的结果是JWS字符串,需要本地解析
                        String ret = result.getJwsResult();
                        Log.d(TAG, "attest success with ret: " + ret);
                        ..............
                    } else {
                        // An error occurred while communicating with the service.
                        Log.d(TAG, "attest fail");
                    }
                }
            });
}

private byte[] getRequestNonce() {
    String token = "ATTest " + System.currentTimeMillis() + TAG;
    return token.getBytes();
}
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

需要说明的是: 
该API每日访问Google服务的次数是受限制的(估计因为用的是免费的,所以才受限吧)。

实际上,Google API控制台会统计每个API的请求次数,如下图所示: 
Google Play Service中安全检测API的使用说明_第4张图片

5.2 Safe Browsing API 
Safe Browsing API主要用于检测: 
某个url对应的网址是否具有潜在威胁。

与Attestation API一样,使用该API前同样需要进入Google Developers Console, 
开通Safe Browsing API并申请对应的App key值。

在使用时,需要在AndroidManifest.xml中添加类似如下内容:

<meta-data android:name="com.google.android.safetynet.API_KEY"
    --对于同一个应用而言,使用不同的API时可以共享同一个key -->
    
    android:value="AIzaSyCPVFlNC********-*********2NcvGi2sj0" />
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4

应用使用该API的方式类似于:

private void browseTest(String url) {
    //容易看出具体的使用方式与Attest类似
    SafetyNet.SafetyNetApi.lookupUri(mGoogleApiClient, url,
            //指定关心威胁的种类
            SafeBrowsingThreat.TYPE_POTENTIALLY_HARMFUL_APPLICATION,
            SafeBrowsingThreat.TYPE_SOCIAL_ENGINEERING)
            //同样定义回调接口
            .setResultCallback(new ResultCallback<SafetyNetApi.SafeBrowsingResult>() {
                    @Override
                    public void onResult(SafetyNetApi.SafeBrowsingResult result) {
                        Status status = result.getStatus();

                        if ((status != null) && status.isSuccess()) {
                            // Indicates communication with the service was successful.
                            // Identify any detected threats.
                            Log.d(TAG, "browse test success");

                            //返回的结果中可以获取List
                            //SafeBrowsingThreat可以通过getThreatType,得到威胁的类型
                            mHandler.sendMessage(
                                    mHandler.obtainMessage(BROWSE, result.getDetectedThreats()));
                        } else {
                            // An error occurred. Let the user proceed without warning.
                            Log.e(TAG, "browse test fail: " + result.getStatus());
                            ...........
                        }
                    }
            });
}
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

5.3 Verify Apps API 
最后,我们看看Google Play Service的Verify Apps API。

这个API主要用于检测: 
手机上已安装的应用是否有害。

与前面两个API不同的是,在使用这个API前,必须开启终端对应的Feature。

对应的代码类似于:

private void enableVerifyFeature() {
    if (!mEnableVerifyFeature) {
        //获取SafeNetClient的实例,这个对象的底层应该封装了GoogleApiClient        
        mSafetyNetClient = SafetyNet.getClient(this);

        //SafeNetClient有两个接口与Verify App所需的Feature有关
        //分别为isVerifyAppsEnabled和enableVerifyApps
        //其中,isVerifyAppsEnabled主要用于判断Feature是否开启
        //enableVerifyApps在判断Feature未开启时,会弹出dialog提醒用户开启(若已开启Feature, 则不弹出dialog)
        mSafetyNetClient.enableVerifyApps()
                .addOnCompleteListener(new OnCompleteListener<SafetyNetApi.VerifyAppsUserResponse>() {
                    @Override
                    public void onComplete(Task<SafetyNetApi.VerifyAppsUserResponse> task) {
                        if (task.isSuccessful()) {
                            SafetyNetApi.VerifyAppsUserResponse result = task.getResult();

                            //用户点击完dialog或feature已经开启后,进入该分支
                            if (result.isVerifyAppsEnabled()) {
                                Log.d(TAG, "The user gave consent " +
                                        "to enable the Verify Apps feature.");
                                ..................
                            } else {
                                Log.e(TAG, "The user didn't give consent " +
                                        "to enable the Verify Apps feature.");
                                ...............
                            }
                        } else {
                            Log.e(TAG, "A general error occurred.");
                            ...............
                        }
                    }
                });
    }
}
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

一旦Verify App对应的Feature开启后,我们就可以利用Verify App的API了,其中老式的用法类似于:

private void listHarmfulApps() {
    //同样依赖于GoogleApiClient
    SafetyNet.SafetyNetApi.listHarmfulApps(mGoogleApiClient)
            //获取到结果后,回调
            .setResultCallback(new ResultCallbacks() {
                @Override
                public void onSuccess(@NonNull SafetyNetApi.HarmfulAppsResult harmfulAppsResult) {
                    Log.d(TAG, "list harm full success");
                    ..............
                }

                @Override
                public void onFailure(@NonNull Status status) {
                    Log.d(TAG, "list harm full fail: " + status);
                }
            });
}
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

最后,我们来看看官方文档推荐的新的调用方式:

private void listHarmfulApps() {
    mSafetyNetClient.listHarmfulApps()
        //添加Listener回调
        .addOnCompleteListener(new OnCompleteListener<SafetyNetApi.HarmfulAppsResponse>() {
                @Override
                public void onComplete(Task<SafetyNetApi.HarmfulAppsResponse> task) {
                    Log.d(TAG, "listHarmfulApps onComplete");

                    //接口返回有效结果
                    if (task.isSuccessful()) {
                        SafetyNetApi.HarmfulAppsResponse result = task.getResult();
                        long scanTimeMs = result.getLastScanTimeMs();
                        Log.d(TAG, "list harmful apps used time: " + scanTimeMs);

                        //获取有害应用列表
                        List<HarmfulAppsData> appList = result.getHarmfulAppsList();
                        if (appList.isEmpty()) {
                            Log.d(TAG, "There are no known " +
                                    "potentially harmful apps installed.");
                        } else {
                            Log.e(TAG,
                                    "Potentially harmful apps are installed!");

                            //得到有害应用的信息
                            for (HarmfulAppsData harmfulApp : appList) {
                                Log.e(TAG, "Information about a harmful app:");
                                Log.e(TAG,
                                        "  APK: " + harmfulApp.apkPackageName);
                                Log.e(TAG,
                                        "  SHA-256: " + harmfulApp.apkSha256);

                                // Categories are defined in VerifyAppsConstants.
                                Log.e(TAG,
                                        "  Category: " + harmfulApp.apkCategory);
                           }
                        }
                    } else {
                        Log.e(TAG, "An error occurred. ");
                    }
                }
        //添加一个失败的回调接口
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                e.printStackTrace();
            }
        });
}
 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

需要说明的是: 
自己测试时发现,listHarmfulApps接口并不稳定,在Android 6.0上偶尔会遇到编码为12004的错误。 
参考Google的API文档,发现这是Verify App API的internal error。 
此外,该接口在Android 7.0平台上,似乎也没法有效使用(不知是否与具体厂商有关)。

最后,我不得不吐槽一下,这个接口的能力简直是渣渣,居然几乎难以有效检测出威胁样本。

6 总结 
本篇文档主要介绍了Google Play Service的原理和集成方法, 
并以SafetyNet API为例,介绍了应用如何使用Google Play Service提供的功能。

参考文献 
https://developers.google.com/android/guides/overview 

https://developer.android.com/training/safetynet


原文地址: http://blog.csdn.net/gaugamela/article/details/73658554

你可能感兴趣的:(Android,Security,Google,Safetynet,Google,Play,Service,safetyNet,API)