Play Integrity API 集成指南

抢先体验计划 (EAP)

Play Integrity API 目前可通过抢先体验计划获取。该 API 尚未正式发布,仍处于积极开发阶段。如有问题和反馈,请发送电子邮件至 integrity-api-eap@google.com

Play Integrity API 服务条款

访问或使用 Play Integrity API 即表示您同意 Play Core 软件开发套件服务条款。请先阅读并了解所有适用的条款及政策,然后再访问该 API。

概览

Play Integrity API 将 Google Play 反滥用功能统一起来,您可以使用这些功能来检测危险性和欺诈性流量。此类流量可能来自未经授权的应用或游戏版本、不可信的设备或其他不可信的环境。通过检测此类流量,您可以采取适当的措施来减少滥用行为,如欺诈、欺骗和未经授权的访问。该 API 同时适用于应用和游戏。如果您的游戏使用该 API,那么您可以观看来自 2021 年 Google for Games 开发者峰会的这段简介视频

目前,该 API 提供已签名和加密的响应,其中包含以下信息:

Play Integrity 图片,重点说明了 Play Integrity 如何与 Google Play 和 Android 设备配合使用
  • 应用完整性:告知您是否正在与 Google Play 识别到的未经修改的二进制文件互动。
  • 帐号详情:告知您当前的用户帐号是否已获得许可,即用户从 Google Play 安装或付费购买获取的应用或游戏。
  • 设备完整性:告知您应用是否正在搭载 Google Play 服务的正版 Android 设备上运行。

API 用法概述

说明了应用、应用的后端和 Play Integrity API 之间有何关系的示意图

大体上讲,该 API 的工作原理如下:

  1. 应用的后端生成一个唯一的 Nonce 并将其发送到其应用。
  2. 应用使用该 Nonce 调用 Integrity API,并接收一个包含 Play 的完整性指导且已签名和加密的令牌。
  3. 应用将该令牌发送到其后端。
  4. 应用的后端解密和验证令牌,并根据令牌载荷中包含的信号来决定如何继续操作。
  5. 应用的后端将决策结果发送到应用。

安全注意事项

虽然 Play Integrity API 有助于确保安全性和防止篡改,但也务必在安全的服务器环境中执行所有解密和验证操作。通过客户端应用公开的任何安全性详细信息都可能会被攻击者从 APK/代码库中提取和移除。具体来说,请注意以下几点:

  • 调用该 API 应该主要是为了保护高价值的非周期性操作,这些操作是用户体验不可或缺的一部分(如登录服务或加入多人游戏服务器)。您不应过于频繁地获取完整性令牌,特别是在需要更多时间才能生成令牌的旧款设备上。平均而言,每个活跃用户每天调用一次是合理的,不过在一天内执行多项高价值操作的用户可能有必要调用多次。
  • Nonce 应该是唯一的,并且攻击者无法预测。
  • 所有解密和验证操作都应该在安全的服务器环境中完成。请勿在客户端应用中解密或验证接收的令牌。
  • 切勿向客户端应用公开任何解密密钥。
  • 与其将单个通过/失败响应从服务器发送回应用,不如发送一些更难复制的信号和决策结果。例如,您可以使用一系列相关的响应,如“允许”、“允许但有限制”、“reCAPTCHA 完成后允许但有限制”和“拒绝”。

将 Play Integrity API 与其他信号一起使用时,让其作为整体反滥用策略的一部分,而不是作为唯一的反滥用机制,这时该 API 效果最佳。该 API 应始终与适合应用的安全性最佳做法结合使用。

API 使用限制

对该 API 的请求受每个应用每天的最大请求次数制约,该最大值由为发起调用的应用分配的使用层确定。系统会自动将 EAP 中的应用分配到一个特殊的 EAP 使用层。

该 EAP 层限制为每个应用每天不得超过 500 万次请求。如果您每天需要超过 500 万次请求,请发送电子邮件至 integrity-api-eap@google.com

就 API 使用而言,在 Google Play 上分发以及通过其他应用商店和分发渠道分发的同一应用算作单个应用。如果您针对 Play 分发和非 Play 分发使用不同的软件包名称,请发送电子邮件至 integrity-api-eap@google.com,以将非 Play 软件包名称注册为关联到 Play 软件包名称。

有关使用层的更多信息将在正式发布该 API 时提供,届时 EAP 使用层中的应用将迁移到公共层。

前提条件

集成要求

设备要求

  • Android 4.4(KitKat,API 级别 19)或更高版本
  • Play 商店应用 24.6.31 或更高版本
  • Play 服务 V19 (12800000) 或更高版本

获取完整性令牌密钥

出于安全考虑,系统会将您接收的密钥加密。下面是获取和解密完整性令牌密钥的步骤:

  1. 创建一个新的私钥/公钥对。请注意,RSA 密钥必须为 2048 位。

    $ openssl genrsa -aes128 -out private.pem 2048
    $ openssl rsa -in private.pem -pubout > public.pem
    
  2. 将公钥通过电子邮件发送至 integrity-api-eap@google.com,并等待接收加密的完整性令牌密钥 (integrity_token_keys.enc)。

  3. 使用私钥解密完整性令牌密钥。

    $ openssl rsautl -decrypt -oaep -inkey private.pem \
        -in integrity_token_keys.enc > integrity_token_keys.txt
    $ cat integrity_token_keys.txt
    DECRYPTION_KEY=XXXXXXXXXXXXX
    VERIFICATION_KEY=YYYYYYYYYYYYY
    

将 Play Core 集成到您的应用中

Play Core 库是您的应用与 Google Play 商店的运行时接口。如需将 Play Core 集成到您的应用中,请按照 Google Play Core 库概览中的说明操作。

Java 或 Kotlin

请注意,在 EAP 期间,您应使用前提条件部分中的版本,而不是使用 Google 的 Maven 版本的库。

如果您使用的是本地 Maven 代码库,请将以下依赖项添加到应用的 build.gradle 文件:

implementation com.google.android.play:core-integrity

Unity

您应将前提条件部分中的 .unitypackage.tgz 文件导入您的 Unity 项目,而不是使用最新的 Play Unity 插件版本。如需查看安装说明,请参阅安装适用于 Unity 的 Google 软件包

原生代码

请参阅 Play Core 原生库的开发环境设置指南

生成 Nonce

Nonce 是专为请求生成的唯一的一次性令牌。Nonce 可防止重放对 Play Integrity API 的请求。例如,如果用户针对一项特定的受保护操作获取了一个 Play Integrity API 令牌,那么应该无法将其重用于其他后续受保护操作(即所谓的“重放攻击”)。因此,Nonce 应该是唯一的且无法预测。

如需创建 Nonce,建议的方法是让您的安全后端服务器生成一个加密安全的随机数字,该数字不包含任何客户端专用信息。一种不太安全但可以接受的替代方法是组合时间戳或用户 ID 等数据,使用这些值计算加密哈希,然后将此值作为 Nonce 发送到客户端应用以供进一步使用。

Nonce 值会按原样传递给 Google 的系统,因此请务必确保没有个人身份信息或其他敏感数据直接或间接包含在 Nonce 中。在某些情况下,即使是经过哈希处理的数据理论上也可以用来收集一些敏感数据,因此对于特别敏感的应用,您不应依赖于敏感数据。

Nonce 可以在服务器上生成,也可以在客户端生成。然而,我们不能假定客户端是可信的,因为它可能是在攻击者的控制之下。因此,在服务器端随机生成 Nonce 比在客户端更安全。

服务器生成的 Nonce

最强大的解决方案是在安全的服务器环境中使用加密安全的随机数字生成器为每个传入客户端请求生成 Nonce。下图显示了一个示例:

使用服务器生成 Nonce 的示例

Nonce 是唯一的且无法预测,因此这种方法提供了有力的保障,保证无法重放请求。

客户端生成的 Nonce

某些客户端可能无法保留服务器端的所有传入请求或者不能容忍额外往返的延迟。对于此类情况,以下示例演示了另一种解决方案,该解决方案用部分重放保证换取了简单性。本例对请求中的请求参数(包含时间戳)进行了哈希处理:

在客户端设备上生成 Nonce 的示例

时间戳需要包含在请求参数中,这样完全相同的请求就不会产生相同的 Nonce,从而确保每个 Nonce 都是唯一的。然而,由于时间戳和请求参数是在客户端生成的,而您不知道客户端是否在攻击者的控制之下,因此您应假定可能存在对抗行为。

您应根据要保护的操作的价值以及感知到的对应用的威胁来选择 Nonce 生成方法。

获取完整性令牌

生成 Nonce 后,您可以通过先创建 IntegrityManager 来获取完整性令牌,如以下示例所示。使用该管理器调用 requestIntegrityToken(),并通过关联 IntegrityTokenRequest 构建器中的 setNonce() 方法提供 Nonce。

Java 编程语言示例

// Receive the nonce from the secure server.
String nonce = ...

// Create an instance of a manager.
IntegrityManager integrityManager =
    IntegrityManagerFactory.create(applicationContext);

// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> integrityTokenResponse =
    integrityManager
      .requestIntegrityToken(
          IntegrityTokenRequest.builder().setNonce(nonce).build());

Unity 示例

IEnumerator RequestIntegrityTokenCoroutine()
{
    // Receive the nonce from the secure server.
    var nonce = ...

    // Create an instance of a manager.
    var integrityManager = new IntegrityManager();

    // Request the integrity token by providing a nonce.
    var tokenRequest = new IntegrityTokenRequest(nonce);
    var requestIntegrityTokenOperation =
        integrityManager.RequestIntegrityToken(tokenRequest);

    // Wait for PlayAsyncOperation to complete.
    yield return requestIntegrityTokenOperation;

    // Check the resulting error code.
    if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
    {
        AppendStatusLog("IntegrityAsyncOperation failed with error: " +
                        requestIntegrityTokenOperation.Error);
        yield break;
    }

    // Get the response.
    var tokenResponse = requestIntegrityTokenOperation.GetResult();
}

原生代码示例

/// Create an IntegrityTokenRequest opaque object.
const char* nonce = RequestNonceFromServer();
IntegrityTokenRequest* request;
IntegrityTokenRequest_create(&request);
IntegrityTokenRequest_setNonce(request, nonce);

/// Prepare an IntegrityTokenResponse opaque type pointer and call
/// IntegerityManager_requestIntegrityToken().
IntegrityTokenResponse* response;
IntegrityErrorCode error_code = IntegrityManager_requestIntegrityToken(request, &response);

...

/// Proceed to polling iff error_code == INTEGRITY_NO_ERROR
if (error_code != INTEGRITY_NO_ERROR) {
    /// Remember to call the *_destroy() functions.
    return;
}

...

/// Use polling to wait for the async operation to complete.
IntegrityResponseStatus response_status;
IntegrityErrorCode error_code = IntegrityTokenResponse_getStatus(response, &response_status);
if (error_code == INTEGRITY_NO_ERROR && response_status == INTEGRITY_RESPONSE_COMPLETED) {
    const char* integrity_token = IntegrityTokenResponse_getToken(response);
    SendTokenToServer(integrity_token);
}

...

/// Remember to free up resources.
IntegrityTokenRequest_destroy(request);
IntegrityTokenResponse_destroy(response);
IntegrityManager_destroy();

解密令牌并验证完整性

提供的 Nonce 将成为返回的已签名响应令牌的一部分。您可以使用 IntegrityTokenResponse#token() 方法来获取返回的令牌。在安全的服务器环境中(切勿在应用中)解密并验证返回的令牌。

令牌格式

令牌是指嵌套的 JSON 网络令牌 (JWT),即 JSON 网络签名 (JWS)JSON 网络加密 (JWE)。JWE 和 JWS 组件使用紧凑的序列化来表示。

加密和签名算法在各种 JWT 实现中得到了很好的支持:

  • JWE 对 alg 使用 A256KW,对 enc 使用 A256GCM。
  • JWS 使用 ES256。

以下示例展示了如何在应用的后端将通过 Play 管理中心或电子邮件进行签名验证的 AES 密钥和 DER 编码的 EC 公钥解码为特定于语言(在本例中为 Java)的密钥。请注意,密钥是使用默认标志以 base64 编码的。

// base64OfEncodedDecryptionKey is provided through Play Console / over email.
byte[] decryptionKeyBytes =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT);
// Deserialized encryption (symmetric) key.
SecretKey decryptionKey =
    new SecretKeySpec(
        decryptionKeyBytes,
        /* offset= */ 0,
        AES_KEY_SIZE_BYTES,
        AES_KEY_TYPE);

// base64OfEncodedVerificationKey is provided through
//  Play Console / over email.
byte[] encodedVerificationKey =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT);
// Deserialized verification (public) key.
PublicKey verificationKey =
    KeyFactory.getInstance(EC_KEY_TYPE)
        .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));

接下来,使用这些密钥先解密完整性令牌(JWE 部分),然后再验证和提取嵌套的 JWS 部分。

JsonWebEncryption jwe =
 (JsonWebEncryption)JsonWebStructure.fromCompactSerialization(integrityToken);
jwe.setKey(decryptionKey);

// This also decrypts the JWE token.
String compactJws = jwe.getPayload();

JsonWebSignature jws =
    (JsonWebSignature) JsonWebStructure.fromCompactSerialization(compactJws);
jws.setKey(verificationKey);

// This also verifies the signature.
String payload = jws.getPayload();

生成的载荷是包含完整性信号的纯文本令牌。

您还必须验证 JSON 载荷的 requestDetails 部分,方法是确保 Nonce 和软件包名称与原始请求中发送的内容相符且时间戳未过时。

JSONObject requestDetails =
    new JSONObject(payload).getJSONObject("requestDetails");
String requestPackageName =
    requestDetails.getString("requestPackageName");
String nonce = requestDetails.getString("nonce");
long timestampMillis = requestDetails.getLong("timestampMillis");
long currentTimestampMillis = ...;

if (!requestPackageName.equals(expectedPackageName)
    // See "Generate nonce" section of the doc on how to store/compute
    // the expected nonce.
    || !nonce.equals(expectedNonce)
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
  // The token is invalid!
  ...
}

返回的载荷格式

载荷是纯文本 JSON,其中包含完整性信号以及开发者提供的信息。

常规载荷结构如下:

{
    requestDetails: { ... }
    appIntegrity: { ... }
    deviceIntegrity: { ... }
    accountDetails: { ... }
}

下面几部分更为详细地介绍了各个字段。

请求详情字段

requestDetails 字段包含在请求中提供的信息,包括 Nonce。这些值应与原始请求中的值相符。

requestDetails: {
    // Application package name this attestation was requested for.
    // Note that this field might be spoofed in the middle of the
    //  request.
    requestPackageName: "com.package.name"
    // base64-encoded web-safe no-wrap nonce provided by the developer.
    nonce: "aGVsbG8gd29scmQgdGhlcmU"
    // The timestamp in milliseconds when the request was made
    //  (computed on the server).
    timestampMillis: 1617893780
}

应用完整性字段

appIntegrity 字段包含与软件包相关的信息。

appIntegrity: {
    // PLAY_RECOGNIZED, UNRECOGNIZED_VERSION, or UNEVALUATED.
    appRecognitionVerdict: "PLAY_RECOGNIZED"
    // The package name of the app.
    // This field is populated iff appRecognitionVerdict != UNEVALUATED.
    packageName: "com.package.name"
    // The sha256 digest of app certificates on device.
    // This field is populated iff appRecognitionVerdict != UNEVALUATED.
    certificateSha256Digest: ["6a6a1474b5cbbb2b1aa57e0bc3"]
    // The version of the app.
    // This field is populated iff appRecognitionVerdict != UNEVALUATED.
    versionCode: 42
}

appRecognitionVerdict 可以具有以下值:

  • PLAY_RECOGNIZED:应用和证书与 Google Play 分发的版本相符。
  • UNRECOGNIZED_VERSION:证书或软件包名称与 Google Play 记录不符。
  • UNEVALUATED:未评估应用完整性。缺少必要的要求(例如,设备不够可信)。

设备完整性字段

deviceIntegrity 字段包含单个值,即 deviceRecognitionVerdict,它表示设备可以在多大程度上强制执行应用完整性。

deviceIntegrity: {
    // "MEETS_DEVICE_INTEGRITY" is one of several possible values.
    deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
}

默认情况下,deviceRecognitionVerdict 可以具有以下某个标签:

  • MEETS_DEVICE_INTEGRITY:应用正在搭载 Google Play 服务的 GMS Android 设备上运行。设备通过了系统完整性检查,并且满足 Android 兼容性要求。
  • MEETS_VIRTUAL_INTEGRITY:应用正在搭载 Google Play 服务的 Android 虚拟环境(如 PC 版 Android 游戏 (AoPC))中运行。环境满足核心 Android 兼容性要求,并且通过了 Google Play 完整性检查。如果您目前未参与 PC 版 Android 游戏抢先体验计划,则可以忽略此标签。
  • 无标签(例如,一个空白值):应用正在有攻击迹象(如 API 挂接)或系统被侵迹象(如取得 root 权限后入侵)的设备上运行,或者应用正在未通过 Google Play 完整性检查的非实体设备(如模拟器)上运行。

合作伙伴可以发送电子邮件至 integrity-api-eap@google.com,以选择接收 deviceRecognitionVerdict 中其他可能的标签:

  • MEETS_BASIC_INTEGRITY:应用正在通过了基本系统完整性检查的设备上运行。设备可能不满足 Android 兼容性要求,也可能未被批准运行 Google Play 服务。例如,设备可能正在运行无法识别的 Android 版本、可能有已解锁的引导加载程序,或者可能没有经过制造商的认证。
  • MEETS_STRONG_INTEGRITY::应用正在搭载 Google Play 服务且具有强有力的系统完整性保证(如由硬件支持的密钥库)的 GMS Android 设备上运行。设备通过了系统完整性检查,并且满足 Android 兼容性要求。

如果满足每个标签条件,那么完整性响应可以包含同一设备的多个标签。通过选择接收多个标签,您可以根据不同级别的设备可信度制定强制执行策略。

例如,假设有一个返回 MEETS_BASIC_INTEGRITYMEETS_DEVICE_INTEGRITYMEETS_STRONG_INTEGRITY 的设备。此设备可能比只返回 MEETS_BASIC_INTEGRITY 的设备更可信,并且您可以相应地量身定制响应方式。

请注意,某些环境条件(如易断线的互联网连接或过载的设备)可能会导致设备完整性检查失败。这样可能会导致系统不为本来可信的设备生成任何标签。为了缓解这些情况,请务必包含重试选项。

帐号详情字段

accountDetails 字段包含单个值,即 licensingVerdict,它表示应用许可/使用权状态。

accountDetails: {
    // This field can be LICENSED, UNLICENSED, or UNEVALUATED.
    licensingVerdict: "LICENSED"
}

licensingVerdict 可以具有以下值:

  • LICENSED:用户拥有应用使用权(例如,用户从 Google Play 或通过 Play Pass 安装或购买了应用)。
  • UNLICENSED:用户没有应用使用权(例如,用户旁加载了应用和/或从未从 Google Play 获取应用)。
  • UNEVALUATED:未评估许可详情,因为缺少必要的要求(例如,设备不够可信或应用是 Google Play 未知的应用)。

错误代码

Task<IntegrityTokenResponse> integrityTokenResponse = ...;
integrityTokenResponse.addOnFailureListener(error -> ...);

下表列出了该 API 可能会返回的错误以及建议的后续步骤。

错误代码 说明 您的操作
API_NOT_AVAILABLE Integrity API 不可用。Play 商店版本可能太低,或者未将应用列入有权使用此 API 的许可名单。
  1. 确保将应用列入有权使用此 API 的许可名单。
  2. 让用户更新 Play 商店应用。
PLAY_STORE_NOT_FOUND 在设备上未找到官方 Google Play 商店应用。 让用户安装适当版本的 Play 商店。
NETWORK_ERROR 未找到可用网络。 让用户检查网络连接。
PLAY_STORE_ACCOUNT_NOT_FOUND 在设备上未找到 Play 商店帐号。 让用户向 Google Play 商店进行身份验证。
APP_NOT_INSTALLED 未安装发起调用的应用。 出了点问题;可能是攻击。不可操作。
PLAY_SERVICES_NOT_FOUND Play 服务不可用或需要更新。 让用户安装或更新 Play 服务。
APP_UID_MISMATCH 发起调用的应用的 UID(用户 ID)与软件包管理器中的 UID 不符。 出了点问题;可能是攻击。不可操作。
TOO_MANY_REQUESTS 发起调用的应用向该 API 发出的请求太多,已经被限制。 使用指数退避算法重试。
CANNOT_BIND_TO_SERVICE 绑定到 Play 商店中的服务失败。这可能是因为在设备上安装的 Play 商店版本太低。 让用户更新 Play 商店。
NONCE_TOO_SHORT Nonce 长度太短。Nonce 必须至少为 16 字节(在 base64 编码之前)。 使用更长的 Nonce 重试。
NONCE_TOO_LONG Nonce 长度太长。Nonce 必须小于 500 字节(在 base64 编码之前)。 使用更短的 Nonce 重试。
GOOGLE_SERVER_UNAVAILABLE 未知内部 Google 服务器错误。 使用指数退避算法重试。不妨考虑提交 bug。
NONCE_IS_NOT_BASE64 Nonce 未采用 base64、网络安全且不换行的形式。 使用正确的 Nonce 格式重试。
INTERNAL_ERROR 未知内部错误。 使用指数退避算法重试。不妨考虑提交 bug。

原生代码专用错误代码

我们在原生错误代码前面加上了 INTEGRITY_ 前缀,以避免潜在的命名冲突。除了上面列出的错误代码之外,原生 API 还包含以下错误代码:

错误代码 说明 您的操作
INTEGRITY_INITIALIZATION_NEEDED IntegrityManager 未初始化。 先调用 IntegrityManager_init()
INTEGRITY_INITIALIZATION_FAILED 初始化 Integrity API 时出错。 使用指数退避算法重试。不妨考虑提交 bug。
INTEGRITY_INVALID_ARGUMENT 传递给 Integrity API 的参数无效。 使用正确的参数重试。

安装时完整性保护 (EAP)

我们也邀请参与 Play Integrity API 抢先体验计划的开发者加入安装时完整性保护抢先体验计划,以加强他们的反滥用策略。在您提交安装要求之后,安装时保护不需要开发者执行任何操作。当您将安装时保护与使用 Play Integrity API 执行的应用完整性运行时检查相结合时,您的应用能够得到最佳保护。

概览

有了安装时完整性保护,您可以减少安装未知和未经授权的应用或游戏版本。首先,您针对应用的软件包名称指定安装要求。然后,每当在搭载 Android 11 或更高版本且 GMS 许可的 Android 设备上从任何来源安装采用您的软件包名称的应用时,Android 平台都会根据您的安装要求的离线副本评估该应用。未能满足要求会导致安装验证错误。

您的安装要求

为了使用安装时保护功能,您必须随应用的软件包名称提供一系列允许的签名证书(包括任何调试证书,以确保您可以继续测试)。如果您的应用连同您的某个签名证书一起安装,安装将照常进行。如果您的应用具有未知的签名证书,安装会失败,并且系统会显示安装验证错误。

您还可以指定两个额外的可选安装要求:

  • 您可以要求绝不能使用 adb 安装采用您的软件包名称的应用。
  • 您可以要求必须始终预安装您的应用(换句话说,该应用不能是全新安装)。

其他注意事项

在使用安装时保护时,您应注意以下几点:

  • 安装时完整性保护功能会检查在搭载 Google Play 服务和 Android 11(API 级别 30)及更高版本且 GMS 许可的 Android 设备上从任何来源安装的每个应用。安装时保护功能无法保护在已启用 mod 或 root 权限的 Android 设备上安装的应用。
  • 务必提供所有允许用于您的应用的签名证书。如有任何经授权的应用商店对您的应用进行了重新签名,确保将这些签名证书包含在您的许可名单中。对于测试,您可以提交开发签名证书作为允许的签名证书,也可以使用内部应用分享功能安装您的测试 build(因为允许内部应用分享安装)。
  • 新增、更新或移除的安装要求需要一些时间才能传播到设备。推送它们可能最多需要两周的时间,设备会在不同的时间接收它们,具体取决于每个设备的更新设置以及数据访问和 Wi-Fi 接入情况。如果最初无法传播要求,Google Play 服务会重试多次来更新它们。
  • 如果用户尝试安装因不满足安装要求而被拒绝的应用,Android 平台会生成 INSTALL_FAILED_VERIFICATION_FAILURE,系统会根据具体的安装程序对其进行解释并将其显示在界面中,或者在旁加载期间将其显示为错误消息。

请求安装时保护

如需加入安装时完整性保护 EAP,请使用此 Google 表单提交您的安装要求。对于新应用,会对所有版本号应用安装时保护。对于现有应用,会从您指定的版本号开始应用保护。

如需从安装时完整性保护 EAP 中移除您的应用,请与您的 Google 合作伙伴经理联系。请注意,移除的安装要求需要一些时间才能传播到设备。

更多 Play 完整性保护工具

除了 Play Integrity API 之外,不妨考虑使用以下完整性保护工具作为反滥用策略的一部分。

从分发范围中排除不可信的设备

您可以阻止在未通过完整性检查的设备上从 Google Play 安装您的应用。此选项可阻止未知和不可信的设备从 Google Play 下载应用,但如果用户可以访问您的一个或多个 APK 文件,他们仍可直接安装您的应用。

如需设置设备排除规则,请执行以下操作:

  1. 转到 Play 管理中心的设备目录页面。
  2. 在显示支持的设备的下拉菜单中,选择排除的设备
  3. 选择右上角的管理排除规则
  4. SafetyNet Attestation API 旁边,选择一个选项:
    • 不排除:默认情况下,此选项处于选中状态。
    • 排除未通过基本完整性测试的设备:这相当于排除在 Play Integrity API 响应中未返回 MEETS_BASIC_INTEGRITY 的设备。
    • 全部排除:这相当于排除在 Play Integrity API 响应中未返回 MEETS_DEVICE_INTEGRITY 的设备。
  5. 点击应用

自动完整性保护

自动完整性保护 (AIP) 是 Play Integrity API 的一键式替代方案,可保护能够完全离线运行的应用和游戏。借助 AIP,Google Play 会通过为应用代码添加运行时检查来限制修改和再分发行为,同时利用先进的混淆技术和反逆向工程技术使这类检查很难被移除。如果未能通过安装程序检查,则系统可以提示用户从 Google Play 获取您的应用。如果未能通过修改检查,则应用不会运行。

AIP 可以与 Play Integrity API 相结合,作为反滥用策略的一部分,让攻击者在做出更改后更难重新打包和再分发您的应用。如需了解详情,请参阅我们的自动完整性保护指南

如需请求访问 AIP,请与您的 Google 合作伙伴经理联系。在您的帐号获得访问权限后,您可以从 Play 管理中心的应用完整性页面中为您的应用开启和关闭 AIP。

反馈

如有问题和反馈,请发送电子邮件至 integrity-api-eap@google.com

集成指南变更记录

版本 说明 日期
2.0.0 08/17/21
1.1.0
  • 在“概览”部分中添加了指向简介视频的链接。
  • 向下载文件夹中添加了原生 API。
  • 在“API 使用限制”部分中添加了有关针对 Play 上的分发与 Play 外的分发使用不同软件包名称的信息。
  • 在“设备完整性字段”部分中添加了 PC 版 Android 游戏 (AoPC) 作为虚拟标签的示例。
07/14/21
1.0.1
  • 添加了原生 API 详细信息和错误代码。
07/06/21
1.0.0
  • 初始版本。
07/02/21