在 Android 開發和安全分析中,APK 簽名驗證是個高頻需求。很多人第一反應是用 apksigner 或 keytool,但如果你的系統環境受限、需要自動化批量處理,或者想深度集成到自己的產品裏,命令行工具就顯得捉襟見肘了。
今天我要分享的,是一套純 C++ 實現的 APK 簽名驗證方案,不依賴 Java、不調用外部工具,直接在你的程序裏完成從 APK 讀取到證書解析的全過程。
一、為什麼要自己解析 APK 簽名?
- 跨平台需求:嵌入式系統、Linux 服務器、甚至 iOS 工具鏈,都可能需要驗證 Android APK 簽名
- 性能要求高:批量掃描 APK 時,頻繁啓動外部進程開銷很大
- 定製化分析:只需要證書指紋、公鑰或某個字段,而不是全部信息
- 安全考慮:工具鏈可被替換或偽造,自研解析可控性更高
二、實現思路
APK 本質是 ZIP 文件,簽名信息存放在 META-INF 目錄下的 .RSA / .DSA / .EC 文件中,格式遵循 PKCS#7 標準。
所以整個流程可以分為:
- 打開 APK 並定位簽名文件
- 讀取簽名文件的二進制數據
- 用 OpenSSL 解析 PKCS#7 數據
- 提取證書鏈和指紋信息
三、核心代碼示例
1. 提取簽名文件數據
#include <zip.h>
#include <vector>
#include <string>
std::vector<uint8_t> extract_signature_file(const std::string& apk_path) {
int err = 0;
zip_t* z = zip_open(apk_path.c_str(), 0, &err);
if (!z) return {};
std::vector<uint8_t> data;
zip_int64_t n = zip_get_num_entries(z, 0);
for (zip_int64_t i = 0; i < n; ++i) {
const char* name = zip_get_name(z, i, 0);
if (!name) continue;
std::string entry(name);
if (entry.substr(0, 9) == "META-INF/" &&
(entry.substr(entry.size() - 4) == ".RSA" ||
entry.substr(entry.size() - 4) == ".DSA" ||
entry.substr(entry.size() - 3) == ".EC")) {
zip_stat_t st;
zip_stat_index(z, i, 0, &st);
zip_file_t* f = zip_fopen_index(z, i, 0);
data.resize(st.size);
zip_fread(f, data.data(), st.size);
zip_fclose(f);
break;
}
}
zip_close(z);
return data;
}
2. 解析 PKCS#7 獲取證書信息
#include <openssl/pkcs7.h>
#include <openssl/x509.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
void parse_pkcs7(const std::vector<uint8_t>& p7_data) {
if (p7_data.empty()) return;
BIO* bio = BIO_new_mem_buf(p7_data.data(), p7_data.size());
PKCS7* p7 = d2i_PKCS7_bio(bio, nullptr);
BIO_free(bio);
if (!p7) return;
if (PKCS7_type_is_signed(p7)) {
STACK_OF(X509)* certs = p7->d.sign->cert;
for (int i = 0; i < sk_X509_num(certs); ++i) {
X509* cert = sk_X509_value(certs, i);
char subject[256];
X509_NAME_oneline(X509_get_subject_name(cert), subject, sizeof(subject));
printf("Subject: %s\n", subject);
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int md_len;
X509_digest(cert, EVP_sha256(), md, &md_len);
// 輸出 SHA256 指紋...
}
}
PKCS7_free(p7);
}
四、安全性建議
如果你打算把這個簽名驗證功能集成到商業產品中,尤其是在安全檢測、版權保護等場景,建議對核心解析代碼進行加固保護。
Virbox Protector 可以提供:
- 代碼混淆
- 虛擬機保護
- 反調試
- 反注入
這樣可以有效防止你的簽名驗證邏輯被逆向分析或繞過,保障業務安全。
五、總結
自己用 C++ 實現 APK 簽名解析,不僅能擺脱對 Java 環境和外部工具的依賴,還能獲得更高的性能和定製化能力。對於需要批量處理 APK 或深度安全分析的場景,這是一個值得投入的技術方案。
如果你願意,我可以幫你把這兩篇文章整合為一個系列教程,每篇都有不同的側重點,比如第一篇講提取,第二篇講驗證,第三篇講安全加固,這樣在自媒體平台的傳播效果會更好。
你要我幫你整理成系列嗎?