需從 APK/JAR 中讀取 `META-INF/CERT.RSA` 簽名文件,代碼嘗試:
ZipEntry entry = new ZipEntry("META-INF/"); // 錯誤:以 / 結尾視為目錄
InputStream in = zipFile.getInputStream(entry); // 返回 null 或拋異常
ZipEntry 名稱**區分大小寫**,必須與壓縮包內部路徑**完全一致**。META-INF/CERT.RSA(無前導 `/`,無尾隨 `/`)。
public static InputStream getCertInputStream(String apkPath) {
try (ZipFile zipFile = new ZipFile(apkPath)) {
// 1. 精確匹配文件名(區分大小寫)
ZipEntry entry = zipFile.getEntry("META-INF/CERT.RSA");
if (entry == null) {
// 兼容小寫/其他簽名文件
entry = zipFile.getEntry("META-INF/CERT.SF");
}
if (entry == null) return null;
return zipFile.getInputStream(entry); // 返回流,調用者負責關閉
} catch (IOException e) {
log.error("Read cert failed", e);
return null;
}
}
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry e = entries.nextElement();
String name = e.getName();
if (name.startsWith("META-INF/") && (name.endsWith(".RSA") || name.endsWith(".DSA") || name.endsWith(".EC"))) {
// 找到簽名塊文件
try (InputStream is = zipFile.getInputStream(e)) {
// 解析 PKCS7...
}
}
}
| 陷阱 | 正確做法 |
|---|---|
| 手動 `new ZipEntry("path")` | 用 `zipFile.getEntry("path")` 獲取實際條目對象(含壓縮信息、CRC、時間戳) |
| 路徑分隔符用 `\` | ZIP 規範強制 `/`,跨平台統一用 `/` |
| 忽略大小寫 | Linux/APK 區分大小寫,需精確匹配或遍歷過濾 |
| 流未關閉 | 用 try-with-resources 自動關閉 ZipFile 與 InputStream |
讀取 ZIP 內嵌套文件核心三要素:**精確路徑 (區分大小寫)、用 `getEntry` 而非 `new ZipEntry`、try-with-resources 管理資源**。`META-INF/CERT.RSA` 是 APK 簽名驗證的標準入口,路徑寫對是成功第一步。