signature
-level 权限很棒,但相当不灵活:应用程序必须使用相同的签名密钥进行签名。在很多情况下,我们想要检查另一个应用程序是否由预期的密钥签名,但该密钥不是our钥匙。就您而言,您有两把钥匙。在其他情况下,人们可能会检查某些合作伙伴应用程序的签名,例如,确认您要向用户发送的 PayPal 应用程序确实是 PayPal 应用程序,而不是取代 PayPal 应用程序的恶意软件。
要验证另一个应用程序的签名,您可以使用PackageManager
。例如,这是当前版本的my SignatureUtils class https://github.com/commonsguy/cwac-security/blob/master/security/src/main/java/com/commonsware/cwac/security/SignatureUtils.java from 我的 CWAC-安全库 https://github.com/commonsguy/cwac-security:
/***
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);
}
}
I use PackageManager
得到Signature
对于给定的包,给定其应用程序 ID。尽管名字如此,Signature
实际上是用于签署应用程序的密钥对的公钥。的输出getSignatureHash()
是一组以冒号分隔的十六进制字符对,表示公钥的 SHA256 哈希...与使用 Java 7+ 获得的值相同keytool
命令。
在您的情况下,您试图即时确定传入操作是否来自所需的应用程序,以及所需的应用程序是否正确(例如,与重新打包的恶意软件相比)。就你而言,Binder.getCallingUid()
将为您提供触发 IPC(触发您的代码)的应用程序的 Linux UID。PackageManager
can 为您提供该 UID 的应用程序的应用程序 ID https://stackoverflow.com/questions/12918731/how-to-get-application-package-name-or-uid-which-is-trying-to-bind-my-service-fr(忽略android:sharedUserId
场景)。然后,您将查看该应用程序 ID 是否是预期值,如果是,请检查哈希签名密钥是否是预期值。如果任一测试失败,用挥舞手臂的机器人的话来说,“危险,威尔·罗宾逊!危险!” https://en.wikipedia.org/wiki/Danger,_Will_Robinson.
一个重要的警告是,一些开发人员将通过这些应用程序重新签名的渠道发布应用程序。这里最值得注意的是适用于 Android 的 Amazon AppStore,亚马逊有意将您的应用程序包装在他们自己的准 DRM 中,并使用他们代表您生成的密钥对该应用程序进行签名。这与您在其他地方必然使用的密钥不同,因此您可能需要比较多个有效的签名哈希值。