Android 内容提供商保护级别和不同的密钥
Posted
技术标签:
【中文标题】Android 内容提供商保护级别和不同的密钥【英文标题】:Android content provider protection level & different keys 【发布时间】:2015-08-24 15:36:38 【问题描述】:我有一个专业(付费)和免费(添加支持)版本的应用程序。两个版本都使用相同的密钥库进行签名,但每个版本都有自己的密钥别名。
知道,我想开发一个与这两个版本兼容的插件,通过内容提供商提供数据。数据很敏感,因此我需要只能通过我的应用(专业版和免费版)访问我的内容提供商。
使用 android:protectionLevel="signature" 的权限不起作用,因为免费版和专业版没有相同的签名 :/ 。我想我应该使用相同的密钥签署我的两个版本,但我认为 Play 商店中的每个应用都需要使用自己的密钥进行签名。
那么,有人知道解决方案吗?有没有办法温和地要求 Google 更改我正在使用的密钥(我可以证明我的身份,因为我没有丢失我的密钥),还是我被卡住了??
编辑:我可以选择取消发布 Pro 版本(因为我目前下载量很少)并使用与免费版本相同的证书重新上传它。如果我这样做,我需要更改它的包吗?
提前致谢
【问题讨论】:
“有没有办法让谷歌改变我正在使用的密钥”——谷歌不是限制因素。 Android 本身将无法使用由不同签名密钥签名的更新替换现有已安装的应用程序。作为替代方案,see whatgetCallingUid()
returns 来自您的提供者的某些 API 方法(例如,query()
)。如果调用者不是您的应用之一,您也许可以使用它来验证调用者并返回空结果(并忽略插入/更新/删除操作)。
感谢您的评论。确实问谷歌无济于事。使用 Uid 是指用它来检索调用者的包名,然后检查它是否正确?但是,如果有人开发了一个具有正确包名称的应用程序以访问我的提供程序怎么办?
"使用Uid是指用它来检索调用者的包名,然后检查是否正确?" -- 我会检查包名称和签名。见我的SignatureUtils
班级:github.com/commonsguy/cwac-security/#usage-signatureutils
太好了,我明天试试,谢谢!!
似乎效果很好,您可以将其添加为解决方案,我会接受。谢谢!
【参考方案1】:
signature
级别的权限很棒,但相当不灵活:应用程序必须使用相同的签名密钥进行签名。在很多情况下,我们需要检查另一个应用程序是否由预期的密钥签名,但该密钥不是 我们的 密钥。在您的情况下,您有两个键。在其他情况下,可能会检查某个合作伙伴应用程序的签名——例如,确认您将要发送给用户的 PayPal 应用程序确实是 PayPal 应用程序,而不是取代 PayPal 应用程序的恶意软件。
要验证另一个应用程序的签名,您可以使用PackageManager
。例如,这是来自my CWAC-Security library 的my SignatureUtils
class 的当前版本:
/***
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://***.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);
在给定应用程序 ID 的情况下,我使用 PackageManager
获取给定包的 Signature
。尽管名称如此,Signature
实际上是用于签署应用程序的密钥对的公钥。 getSignatureHash()
的输出是一组以冒号分隔的十六进制字符对,表示公钥的 SHA256 哈希值...与使用 Java 7+ keytool
命令获得的值相同.
在您的情况下,您正在尝试动态确定传入操作是否来自所需的应用程序,以及该所需的应用程序是否是正确的应用程序(例如,相对于重新打包的恶意软件)。在您的情况下,Binder.getCallingUid()
将为您提供触发 IPC 的应用程序的 Linux UID,该 IPC 触发了您的代码。 PackageManager
可以give you the application ID of the app for that UID(忽略android:sharedUserId
场景)。然后,您将查看该应用程序 ID 是否为预期值,如果是,则检查散列签名密钥是否为预期值。如果任一测试失败,用挥臂机器人的话来说,"Danger, Will Robinson! Danger!"。
一个重要的警告是,一些开发者将通过这些应用重新签名的渠道发布应用。这里最值得注意的是适用于 Android 的 Amazon AppStore,Amazon 有意将您的应用程序包装在他们自己的准 DRM 中,并使用他们代表您生成的密钥对该应用程序进行签名。这与您在其他地方使用的密钥不同,因此您可能需要比较多个有效的签名哈希。
【讨论】:
以上是关于Android 内容提供商保护级别和不同的密钥的主要内容,如果未能解决你的问题,请参考以下文章