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://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);
}
}
在给定应用程序 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 中,并使用他们代表您生成的密钥对该应用程序进行签名。这与您在其他地方使用的密钥不同,因此您可能需要比较多个有效的签名哈希。