我正在开发一个与服务器通信的 Android 应用程序,我想在运行时验证我的应用程序自发布以来没有被修改(拥有修改过的应用程序的用户不应该能够登录到应用程序).
由于修改后的应用程序签名与原始签名不同,我决定:
- 提取嵌入在 android 应用程序中的签名证书
- 将其与登录请求一起发送到服务器(在客户端(即 apk)编写整个验证过程是行不通的,因为有人可以修改 apk 以绕过它。)
- 验证
- 如果证书有效处理登录请求/否则返回错误
这是我上面第 1 条的代码:
Context context = this;
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
Signature[] signatures = packageInfo.signatures;
所以我的问题是:
- 是否有更好的方法来验证 apk?
- 如果这个方法没问题,
2.1 发送证书会占用带宽吗?
2.2 如何验证证书?(我在服务器端有 mykey.jks,我最初用来签署 apk)
(这也是我关于 stackoverflow 的第一个问题,因此非常感谢指出我在提问时犯的任何错误!。)
最佳答案
首先,有人总有可能绕过这种安全措施,例如通过硬编码传递给您的服务器的证书。你只能尽量让它变得尽可能难,但它永远不会 100% 安全。
话虽如此,您正在做的事情似乎还不错。请注意,在最新的 SDK 中,pm.signatures
已弃用,取而代之的是 pm.signingInfo
。
在大小方面,证书并不大,但要使请求更小,您可能应该只发送一个哈希值(例如 SHA256)。
为了防止人们在请求中对证书进行硬编码,您还可以考虑将另一个值与证书一起进行哈希处理,即哈希(证书 + 时间戳),并在请求中发送时间戳,以便您可以重新计算哈希服务器端.如果时间戳距离当前日期太远,则拒绝该请求。同样,它不是完全安全的,而是增加了另一层复杂性来对您的代码进行逆向工程。您还可以添加 versionCode 并开始拒绝旧版本的请求(例如,如果您在其中一个旧版本中检测到安全漏洞)并提示这些用户更新应用程序(或在后台为他们更新并仅提示安装,感谢 Play 提供的新 API)。
正如有人指出的那样,您的代码还可以检查安装是否来自 Play (packageManager.getInstallerPackageName(getPackageName()).equals("com.android.vending");
),但该信息很容易被欺骗,因此不确定它是否增加了安全性。
希望对你有帮助,
关于android - 如何在客户端验证 APK 签名证书?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54086812/