本人由于毕设需求,需要从安装的APK中找到自个的APK,于是想到了利用公钥进行验证的方式(签名验证如果多个程序需要多个签名,索性使用公钥省事)
首先我们需要明白APK签名原理,在APK目录结构中META-INF目录是用存放签名校验等数据的,其中包括以下文件:MANIFEST.MF,CERT.SF,CERT.RSA。
整个签名过程如下图:
其中MANIFEST.MF文件存放了APK包中所有文件生成的SHA1摘要的Base64编码,使用UE可以看到以下内容:
而CERT.SF文件是对MANIFEST.MF的SHA1摘要,以及之前文件生成的摘要的再次SHA1摘要,最后都在分别进行Base64编码。使用UE可以看到以下内容:
CERT.RSA文件就是关键的签名文件了,其同时也包含了证书相关的信息,我们可以使用MiniCA2.0完成证书的导出查看:
导出了这个证书,之后可以查看其中内容
证书中的各个信息就都能看到了,之后可以查看公钥,值得注意的是,在图上所示的前9个字节中30 82 01 0a 02 82 01 01 00,是用来表示是什么算法产生的公钥这里是RSA.
最后的5个字节(这里没有展示完全自个查看解决)02 03 01 00 01 也是相关的标识,详细可以查看X509证书结构。而中间的内容就是我们的公钥。我们可以通过公钥标识我们
的身份。我是在服务中实现的,代码如下:
@Override public void onStart(Intent intent, int startId) { Log.i(TAG, "checksignatureservice start"); super.onStart(intent, startId); PackageManager pm = getPackageManager(); List<PackageInfo> packInfo = pm.getInstalledPackages(PackageManager.GET_SIGNATURES); Iterator<PackageInfo> iter = packInfo.iterator(); ApplicationInfo appInfo = null; while (iter.hasNext()) { PackageInfo pi = (PackageInfo) iter.next(); appInfo = pi.applicationInfo; if((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0){ continue; }else{ //不是系统程序 Log.d(TAG,pi.packageName); String key = getPublicKey(pi.signatures[0].toByteArray()); if (key.equals(myRsaKey)) { Log.i(TAG,"It is my app"); } } } }
private String getPublicKey(byte[] signature) { try { CertificateFactory certFactory = CertificateFactory .getInstance("X.509"); X509Certificate cert = (X509Certificate) certFactory .generateCertificate(new ByteArrayInputStream(signature)); String publickey = cert.getPublicKey().toString(); publickey = publickey.substring(publickey.indexOf("modulus: ") + 9, publickey.indexOf("\n", publickey.indexOf("modulus:"))); return publickey; } catch (CertificateException e) { e.printStackTrace(); } return null; }
这样就实现了公钥的比对,通过如此来验证APK是否自己发布的APK