本人由於畢設需求,需要從安裝的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");
}
}
}
}
getPublicKey函數如下:
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