Google Cloud Translate API 使用 Android 应用程序限制时密钥不起作用

Google Cloud Translate API Key Doesn't Work When Using Android Application Restrictions

我正在尝试在我的 Android 应用程序中仅使用密钥作为身份验证来实现 Google Cloud 的 T运行slation API。我没有服务器或任何可以提高安全性的东西,所以我想通过指定应用程序包名称和 SHA-1 哈希来限制 API 密钥仅供我的 Android 应用程序使用。以下是我在云 API 和服务页面中使用的设置:

为确保信息正确,我将 Gradle App ID 设置为指定的包名称:

我 运行 在 windows 中为 SHA-1 散列提供的 keytool 命令:

Gradle 的签名报告工具也 returns 相同的散列:

当我尝试为支持的语言调用简单的 GET 请求时,响应始终是 403 Forbidden 错误。 然而,当我取消仅 Android 应用程序和 package/hash 设置的限制时,密钥有效。 我显然在这里做错了什么或忘记了什么?当我什至记录 BuildConfig.getPackage() 时,它 returns 相同的包名。两种获取 SHA-1 散列的方法都返回相同的散列。我不确定出了什么问题。

几天前终于弄明白了。仅从应用程序包发送 API 请求并不意味着它在某处使用该信息进行编码,以便 API 端点知道它来自授权访问者。

请求需要两个header属性来指定源码包和签名,形式为:

{
    "X-Android-Package": "package.name.here",
    "X-Android-Cert": "debug:or:release:signature:here"
}

这在 Google 文档中没有。我不知道为什么,令人沮丧的是,这显然是每个人在使用纯 REST 而不是 Google 的客户端库时都需要知道的事情。

这意味着对这些值进行硬编码并不是一个好主意,因为有人可以反编译 apk 并获得这些 headers 的授权访问凭据。但是您可以通过使用几个可用函数来获取此信息来增加难度。

获取包名很简单:

context.getPackageName()

签名需要一些工作

public String getSignature(Context context) {
    PackageInfo info = null;
    try {
        info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES);
        Signature[] sigHistory = info.signingInfo.getSigningCertificateHistory();
        byte[] signature = sigHistory[0].toByteArray();
        MessageDigest md = MessageDigest.getInstance("SHA1");
        byte[] digest = md.digest(signature);
        StringBuilder sha1Builder = new StringBuilder();
        for (byte b : digest) sha1Builder.append(String.format("%02x", b));
        return sha1Builder.toString();
    } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return null;
}

确保阅读 SigningInfo documentation 以正确使用它。

从那里,设置请求 headers。我一直在使用 Apache 的 Http 客户端:

HttpGet getReq = new HttpGet(uri);
getReq.setHeader("X-Android-Package", context.getPackageName());
getReq.addHeader("X-Android-Cert", getSignature(context));