Google 如何在 API 调用中验证 SHA1 和包名称?

Posted

技术标签:

【中文标题】Google 如何在 API 调用中验证 SHA1 和包名称?【英文标题】:How is Google verifying SHA1 and package name in API calls? 【发布时间】:2014-11-10 14:53:37 【问题描述】:

在 API 控制台中注册 android 应用以访问 Google API 时,您必须输入应用的 SHA1 证书指纹和应用的包名称。

现在我想知道当 api 调用只是简单的 HTTP 请求时,Google 如何验证这些值是否正确(在最简单的情况下,当您不使用他们的 API 客户端时,可能会附加一些标头值)?调用 API 时必须提供 API 密钥,但这并不能证明输入的值是正确的。

【问题讨论】:

>"simple HTTP requests" 你是什么类型的请求?也许该请求是公开的。 【参考方案1】:

如果您不使用 Google Utils/SDKs/API 客户端,则必须手动传递这些标头。

如果您限制了您的 android 或 ios 应用程序的 API 密钥,则必须传递以下标头-

Android 标头:


  'x-android-package': 'com.example',
  'x-android-cert': '50FEC39F742F3DF212BDC2131A99C7D3C82086F6'

这里x-android-cert标头的值是不带分号的签名密钥的SHA1指纹。

从密钥库文件中获取 SHA1 指纹-

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

从签名的 apk/bundle 中获取 SHA1 指纹-

keytool -printcert -jarfile ~/Downloads/app-release.aab

keytool -printcert -jarfile ~/Downloads/app-release.apk

ios 标头:


  'x-ios-bundle-identifier': 'com.example' 

这里com.example 是包标识符。

下面是class in Java,我认为它在原生 Places SDK 中做同样的工作-

/**
 * Intercepts requests and provides Android-specific headers so that API key restrictions can be
 * enforced.
 */
public class AndroidAuthenticationInterceptor implements Interceptor 

  ...

  @NotNull
  @Override
  public Response intercept(@NotNull Chain chain) throws IOException 
    ...

    final Request.Builder builder = chain.request().newBuilder();
    if (config.packageName != null) 
      builder.addHeader(HttpHeaders.X_ANDROID_PACKAGE, config.packageName);
    

    if (config.certFingerprint != null) 
      builder.addHeader(HttpHeaders.X_ANDROID_CERT, config.certFingerprint);
    

    return chain.proceed(builder.build());
  

参考资料-

    Restricting usage for an Android key for a Google API How do I find out which keystore was used to sign an app? https://developers.google.com/maps/api-security-best-practices https://github.com/googlemaps/google-maps-services-java/blob/9852dbae5d2c10897ad7a8dd4befcd171a2cd48e/src/main/java/com/google/maps/android/AndroidAuthenticationInterceptor.java https://github.com/googlemaps/google-maps-services-java/blob/main/src/main/java/com/google/maps/internal/HttpHeaders.java

【讨论】:

【参考方案2】:

您可以很容易地获得包名以及已安装应用程序的 sha 1 指纹。

private void printSha1() 
    List<ApplicationInfo> mAppList = getPackageManager().getInstalledApplications(0);
    for (ApplicationInfo info :mAppList) 
        Log.d(TAG, "Package Name: " + info.packageName);
        Log.d(TAG, "Sha1: " + getCertificateSHA1Fingerprint(info.packageName));
    


private String getCertificateSHA1Fingerprint(String packageName) 
    PackageManager pm = getPackageManager();
    int flags = PackageManager.GET_SIGNATURES;
    PackageInfo packageInfo = null;
    try 
        packageInfo = pm.getPackageInfo(packageName, flags);
     catch (PackageManager.NameNotFoundException e) 
        e.printStackTrace();
    
    Signature[] signatures = packageInfo.signatures;
    byte[] cert = signatures[0].toByteArray();
    InputStream input = new ByteArrayInputStream(cert);
    CertificateFactory cf = null;
    try 
        cf = CertificateFactory.getInstance("X509");
     catch (CertificateException e) 
        e.printStackTrace();
    
    X509Certificate c = null;
    try 
        c = (X509Certificate) cf.generateCertificate(input);
     catch (CertificateException e) 
        e.printStackTrace();
    
    String hexString = null;
    try 
        MessageDigest md = MessageDigest.getInstance("SHA1");
        byte[] publicKey = md.digest(c.getEncoded());
        hexString = byte2HexFormatted(publicKey);
     catch (NoSuchAlgorithmException e1) 
        e1.printStackTrace();
     catch (CertificateEncodingException e) 
        e.printStackTrace();
    
    return hexString;


public static String byte2HexFormatted(byte[] arr) 
    StringBuilder str = new StringBuilder(arr.length * 2);
    for (int i = 0; i < arr.length; i++) 
        String h = Integer.toHexString(arr[i]);
        int l = h.length();
        if (l == 1) h = "0" + h;
        if (l > 2) h = h.substring(l - 2, l);
        str.append(h.toUpperCase());
        if (i < (arr.length - 1)) str.append(':');
    
    return str.toString();

如果您运行此代码,它将打印包名称及其 sha 1 打印。这是您在创建 API 密钥时提供的两件事,因此 Google 将这两件事与它生成的密钥进行映射。

正如您所看到的,它可以访问包名称及其 SHA 1 打印,剩下的另一件事是您通过代码或通过 xml(Manifest,单独的 xml 配置文件)。

因此,每当 Google 为您提供任何服务时,它都可以检查您从 API 控制台生成密钥时生成的相关映射。

包名代码取自here

【讨论】:

以上是关于Google 如何在 API 调用中验证 SHA1 和包名称?的主要内容,如果未能解决你的问题,请参考以下文章

如何获取android安全码SHA1

Google APi + Passport + React:身份验证流程

gcp api 网关在调用 x-google-backend 之前是不是根据 OpenAPI 规范验证请求正文?

通过 OAuth 对 Google API 进行经过身份验证的调用时遇到问题

Google Map API 不在发布模式下工作

Java后端API调用身份验证的思考