Android内容提供商保护级别&不同的钥匙

时间:2015-08-24 15:36:38

标签: android key android-contentprovider signature

我有一个应用程序可用于Pro(付费)&免费(添加支持)版本。两个版本都使用相同的密钥库进行签名,但每个版本都有自己的密钥别名。

知道,我想开发一个兼容两个版本的插件,通过内容提供商提供数据。数据非常敏感,因此我只能从我的应用程序(Pro和免费版本)访问我的内容提供商。

使用android:protectionLevel ="签名"不工作,因为Free&专业版没有相同的签名:/。我想我应该使用相同的密钥签署我的两个版本,但我认为Play商店中的每个应用都需要使用自己的密钥进行签名。

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

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

提前致谢

1 个答案:

答案 0 :(得分:2)

signature - 级权限很棒,但相当不灵活:应用必须使用相同的签名密钥进行签名。在许多情况下,我们需要检查另一个应用是否由预期密钥签名,但该密钥不是我们的密钥。在您的情况下,您有两个键。在其他情况下,可能会检查一些合作伙伴应用程序的签名 - 例如,确认您要将用户发送到的PayPal应用程序实际上是PayPal应用程序,而不是已替换PayPal应用程序的恶意软件。

要验证其他应用的签名,您可以使用PackageManager。例如,以下是来自my SignatureUtils class的当前版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 https://stackoverflow.com/a/2197650/115145

  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实际上是用于签署应用程序的密钥对的公钥。 getSignatureHash()的输出是以冒号分隔的十六进制字符对集合,表示公钥的SHA256哈希值...使用Java 7+ keytool <获得的值相同/ strong>命令。

在您的情况下,您正试图在运行中确定传入的操作是否来自所需的应用程序,以及所需的应用程序是否正确(例如,与重新打包的恶意软件相比)。在您的情况下,Binder.getCallingUid()将为您提供触发IPC的触发代码的应用程序的Linux UID。 PackageManager可以give you the application ID of the app for that UID(忽略android:sharedUserId个方案)。然后,您将看到该应用程序ID是否为预期值,如果是,请检查散列签名密钥是否为预期值。如果任一测试失败,用手臂甩动机器人的话说"Danger, Will Robinson! Danger!"

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