Android 内容提供商保护级别和不同的密钥

Android content provider protection level & different keys

我有一个应用程序提供专业版(付费)和免费(添加支持)版本。两个版本都使用相同的密钥库签名,但每个版本都有自己的密钥别名。

知道,我想开发一个兼容两个版本的插件,通过内容提供商提供数据。数据很敏感,所以我需要我的内容提供者只能从我的应用程序(专业版和免费版)访问。

使用 android:protectionLevel="signature" 的权限无效,因为免费版和专业版没有相同的签名:/。我想我应该使用相同的密钥对我的两个版本进行签名,但我认为 Play 商店中的每个应用都需要使用自己的密钥进行签名。

那么,有人知道解决方案吗?有没有办法轻轻地要求 Google 更改我正在使用的密钥(我可以证明我的身份,因为我没有丢失我的密钥),或者我被卡住了??

编辑:我可以选择取消发布专业版(因为目前我的下载量很少)并使用与免费版所用证书相同的证书重新上传。如果我这样做,我需要改变它的包装吗?

提前致谢

signature 级别的权限很好,但相当不灵活:应用程序必须由相同的签名密钥签名。在很多情况下,我们想要检查另一个应用程序是否由预期密钥签名,但该密钥不是 our 密钥。在您的情况下,您有两把钥匙。在其他情况下,可能会检查某个合作伙伴应用程序的签名——例如,确认您要将用户发送到的 PayPal 应用程序确实是 PayPal 应用程序,而不是取代了 PayPal 应用程序的恶意软件。

要验证另一个应用程序的签名,您可以使用PackageManager。例如,这里是 my SignatureUtils class from my CWAC-Security library 的当前版本:

/***
  Copyright (c) 2014 CommonsWare, LLC

  Licensed under the Apache License, Version 2.0 (the "License"); you may
  not use this file except in compliance with the License. You may obtain
  a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

package com.commonsware.cwac.security;

import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class SignatureUtils {
  public static String getOwnSignatureHash(Context ctxt)
                                                        throws NameNotFoundException,
                                                        NoSuchAlgorithmException {
    return(getSignatureHash(ctxt, ctxt.getPackageName()));
  }

  public static String getSignatureHash(Context ctxt, String packageName)
                                                                         throws NameNotFoundException,
                                                                         NoSuchAlgorithmException {
    MessageDigest md=MessageDigest.getInstance("SHA-256");
    Signature sig=
        ctxt.getPackageManager()
            .getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures[0];

    return(toHexStringWithColons(md.digest(sig.toByteArray())));
  }

  // based on 

  public static String toHexStringWithColons(byte[] bytes) {
    char[] hexArray=
        { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
            'C', 'D', 'E', 'F' };
    char[] hexChars=new char[(bytes.length * 3) - 1];
    int v;

    for (int j=0; j < bytes.length; j++) {
      v=bytes[j] & 0xFF;
      hexChars[j * 3]=hexArray[v / 16];
      hexChars[j * 3 + 1]=hexArray[v % 16];

      if (j < bytes.length - 1) {
        hexChars[j * 3 + 2]=':';
      }
    }

    return new String(hexChars);
  }
}

我使用 PackageManager 获取给定包的 Signature,给定它的应用程序 ID。尽管名称如此,Signature 实际上是用于签署应用程序的密钥对的 public 密钥。 getSignatureHash() 的输出是一组以冒号分隔的十六进制字符对,表示 public 键的 SHA256 散列...与使用 Java 7+ keytool 命令.

在您的情况下,您正在尝试即时确定传入操作是否来自所需的应用程序,以及所需的应用程序是否正确(例如,相对于重新打包的恶意软件)。在您的情况下,Binder.getCallingUid() 将为您提供触发 IPC 的应用程序的 Linux UID。 PackageManager 可以 give you the application ID of the app for that UID (ignoring android:sharedUserId scenarios). You would then see if that application ID is the expected value, and if it is, check to see if the hashed signing key is the expected value. If either test fails, in the words of an arm-flailing robot, "Danger, Will Robinson! Danger!".

一个重要的警告是,一些开发人员将通过对这些应用程序进行重新签名的渠道发布应用程序。这里最值得注意的是 Android 的 Amazon AppStore,其中亚马逊有意将您的应用程序包装在他们自己的准 DRM 中,并使用他们代表您生成的密钥对该应用程序进行签名。这与您在其他地方使用的密钥不同,因此您可能需要比较多个有效的签名哈希值。