android - 如何在客户端验证 APK 签名证书?

标签 android security certificate client-server apk

我正在开发一个与服务器通信的 Android 应用程序,我想在运行时验证我的应用程序自发布以来没有被修改(拥有修改过的应用程序的用户不应该能够登录到应用程序).

由于修改后的应用程序签名与原始签名不同,我决定:

  1. 提取嵌入在 android 应用程序中的签名证书
  2. 将其与登录请求一起发送到服务器(在客户端(即 apk)编写整个验证过程是行不通的,因为有人可以修改 apk 以绕过它。)
  3. 验证
  4. 如果证书有效处理登录请求/否则返回错误

这是我上面第 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;  

所以我的问题是:

  1. 是否有更好的方法来验证 apk?
  2. 如果这个方法没问题,
    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/

相关文章:

android - 从android中的流位图创建视频

java - 二进制 XML 文件行 #19 : Error inflating class only on Android 3. 2

c# - 如何破解一个dBase文件的密码?

javascript - JavaScript 中的 SSL 连接

ssl - ASP.NET : how to use ssl certificate

android - 自定义摄像头常用解决方案

c# - Android 如何以编程方式使线性布局可滚动?

google-chrome - 当你使用 'badidea' 或 'thisisunsafe' 绕过 Chrome 证书/HSTS 错误时,它只适用于当前站点吗?

mongodb - mongoDB 可以只允许来自特定 IP 的连接吗?

ios - 要求客户端证书是否能有效消除 MITM 攻击?