keytooljarsigner 均是jdk提供的工具,JDK下载链接 涉及的文件可以参考示例 AndroidSignKeyConvert Android 签名机制参阅 https://source.android.com/docs/security/features/apksigning,命令行工具参阅 https://developer.android.com/tools/apksigner 本文首发地址 https://h89.cn/archives/86.html

本文主要介绍安卓应用签名相关内容,包括签名文件的生成,如何对APK签名,如何查看签名结果,以及常见签名之间的相互转换。

生成 APK 签名文件

使用 keytool 命令生成 .jks

keytool -genkeypair -storetype JKS -keystore ./genkey.jks -storepass 123123 -alias business -keypass 123456 -dname "CN=chenjim, OU=h89, O=cn, L=Shanghai, ST=Shanghai, C=CN" -validity 120000 -keyalg RSA -keysize 2048 将会生成签名文件./genkey.jks,有效期为120000天(约 328 年),别名business,密钥库密码123123business的密钥密码123456。这些密码仅用于本文测试示例,不能用于发布签名。

-storetype JKS 必须显式指定:现代 JDK 默认密钥库类型通常是 PKCS12,.jks.keystore 只是文件名后缀,不决定文件格式。JKS 可以使用不同的 storepasskeypass;PKCS12 为兼容不同工具,应使用相同密码。新建 Android 发布密钥推荐 RSA 2048 位或更高,不要再生成 DSA/SHA-1 密钥。

参数含义可参考 https://docs.oracle.com/en/java/javase/25/docs/specs/man/keytool.html


使用 Android Studio 生成

菜单 Build-->Generate Signed APK-->Create new


对 APK 签名的几种方式

用指定的 Keystore(JKS)对 APK 签名

  1. 用jarsigner对apk签名,这个支持V1签名即jar签名
    jarsigner -keystore ./genkey.jks -signedjar ./signed.apk ./unsigned.apk business
    输入密钥库的密码短语: //123123
    输入business的密钥口令: //123456
    jar 已签名。
    
    unsigned.apk./genkey.jksbusiness 密匙签名为signed.apk 示例 中的 unsigned.apk 是一个未签名的 apk,这里用上述 jks 对其签名
  2. 使用 apksigner 对 APK 签名。必须先对未签名 APK 执行 zipalign,再签名;签名完成后不能再修改 APK。
    zipalign -f -v 4 unsigned.apk aligned.apk
    apksigner sign --ks ./genkey.jks --ks-key-alias business --out signed.apk aligned.apk
    apksigner verify --verbose --print-certs signed.apk
    
    • apksigner 位于 Android SDK/build-tools/<版本>/apksigner,Build Tools 24.0.3 及以上提供。
    • 未指定 --out 时,apksigner 会原地覆盖最后一个 APK 参数。示例显式输出为 signed.apk,避免覆盖输入文件。
    • 默认签名方案由 APK 的 minSdkVersionmaxSdkVersion 决定。需要固定方案时可传入 --v1-signing-enabled--v2-signing-enabled--v3-signing-enabled--v4-signing-enabled

使用私钥和证书(platform.x509.pem 和 platform.pk8)对 APK 签名

此种情况多见Android系统开发中,对系统应用签名,有如下种命令

  1. apksigner sign --key genkey.pk8 --cert genkey.x509.pem --in aligned.apk --out signed.apk 这个是 Android SDK 提供的签名方式。aligned.apk 必须已完成 zipalign,签名后用 apksigner verify --verbose --print-certs signed.apk 验证。它与 Keystore 输入方式支持同一组 v1/v2/v3/v4 签名方案。

  2. java -jar signapk.jar platform.x509.pem platform.pk8 unsigned.apk signed.apk

    • signapk.jar 可以编译Android源码(mmm build/tools/signapk/) 得到,源码中位置 prebuilts/sdk/tools/lib/signapk.jar也可能有
    • platform.x509.pem和platform.pk8在源码目录 build/target/product/security/
    • signapk.jar 是 Android 源码构建场景的历史工具,不同源码分支支持的签名方案不同;新项目优先使用同版本 Build Tools 的 apksigner。若目标包含 Android 9 及以上设备,不要仅假设它具备 v3/v4 支持。

使用集成 IDE 对 APK 签名

多数IDE(IntellJ/AS)是在菜单build-->Generate Signed APK中 Android Studio 可以自动签名,需要在build.gradle中添加如下配置

signingConfigs {
    config {
        storeFile file('../genkey.jks')
        storePassword '123123'
        keyAlias "business"
        keyPassword '123456'
    }
}

buildTypes {
    release {
        minifyEnabled false
        signingConfig signingConfigs.config
    }
}

查看签名信息

查看签名文件(JKS)的签名信息

keytool -list -v -keystore genkey.jks 需要用到之前生成签名文件的密钥库的密码123123 以下是旧 DSA 密钥的历史输出,仅用于识别弱证书。按本文新命令生成的证书应显示 RSA 2048 位或更高,不应以以下 SHA-1/DSA 结果作为发布签名标准。

发布者: CN=Chenjim, OU=chenjim, O=chenjim, L=Shanghai, ST=Minhang, C=CN
序列号: 654badf0
生效时间: Thu Mar 26 14:32:11 CST 2020, 失效时间: Wed Oct 13 14:32:11 CST 2348
证书指纹:
         SHA1: CC:A0:52:3A:4A:6F:0A:77:3A:68:2C:A0:18:52:1D:A7:36:EB:B5:16
         SHA256: A3:F7:B3:7E:3F:C0:E6:A8:FF:74:2C:4E:FA:FC:AC:66:E4:38:B3:02:2C:94:7E:07:AC:63:B2:0F:30:7F:09:51
签名算法名称: SHA1withDSA (弱)
主体公共密钥算法: 1024 位 DSA 密钥 (弱)

用 apksigner 查看 APK 签名方案和证书

命令 apksigner verify --verbose --print-certs signed.apk 结果类似如下

Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): false
Number of signers: 1

如果需要按某个系统版本判断兼容性,可增加 --min-sdk-version <API>--max-sdk-version <API>。v4 还应检查 APK 旁边是否生成同名 .idsig 文件。


用 keytool 查看 APK 的 v1 签名

  1. 将签名后的signed.apk解压可得到签名文件 META-INF\BUSINESS.RSA,可能后缀为 *.DSA,由前面生成的 jks 加密类型决定 keytool -printcert -file ./BUSINESS.DSA 显示结果 2. 直接使用如下命令。该方式读取 JAR 的 META-INF 条目,只适用于 v1 签名;检查 v2/v3/v4 必须使用 apksigner verifykeytool -printcert -jarfile signed.apk

在项目中用 Gradle 查看签名信息

执行命令.\gradlew.bat :app:signingReport,得到如下签名结果

Variant: release
Config: config
Store: ....AndroidSignKeyConvert\genkey.jks
Alias: business
MD5: 9F:E0:78:DD:BC:2A:C2:FB:90:8D:67:D1:F7:FE:29:BF
SHA1: CC:A0:52:3A:4A:6F:0A:77:3A:68:2C:A0:18:52:1D:A7:36:EB:B5:16
SHA-256: A3:F7:B3:7E:3F:C0:E6:A8:FF:74:2C:4E:FA:FC:AC:66:E4:38:B3:02:2C:94:7E:07:AC:63:B2:0F:30:7F:09:51
Valid until: 2348年10月13日 星期三

应用代码中获取 APK 签名信息

参考自 https://blog.csdn.net/anydrew/article/details/51227517 Android 11(API 30)及以上还受包可见性限制。应用可查询自身或已声明/可见的包;不要将“无法读取其他应用签名”作为安全边界。 核心代码整理如下,参见 测试代码 MainActivity.java 代码需要导入 android.os.Buildandroid.content.pm.SigningInfojava.security.MessageDigestjava.security.NoSuchAlgorithmExceptionjava.util.Locale

@SuppressWarnings("deprecation")
public String getAppSigningCertificateSha256() {
    try {
        PackageManager packageManager = getPackageManager();
        PackageInfo packageInfo;
        Signature[] signatures;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            packageInfo = packageManager.getPackageInfo(
                    getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES);
            SigningInfo signingInfo = packageInfo.signingInfo;
            signatures = signingInfo.hasPastSigningCertificates()
                    ? signingInfo.getSigningCertificateHistory()
                    : signingInfo.getApkContentsSigners();
        } else {
            packageInfo = packageManager.getPackageInfo(
                    getPackageName(), PackageManager.GET_SIGNATURES);
            signatures = packageInfo.signatures;
        }
        if (signatures == null || signatures.length == 0) {
            return null;
        }
        byte[] digest = MessageDigest.getInstance("SHA-256")
                .digest(signatures[0].toByteArray());
        StringBuilder result = new StringBuilder(digest.length * 2);
        for (byte value : digest) {
            result.append(String.format(Locale.US, "%02X", value));
        }
        return result.toString();
    } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
        Log.e("AppSign", "Unable to read signing certificate", e);
        return null;
    }
}

Android 9(API 28)起,GET_SIGNATURESPackageInfo.signatures 已废弃,必须使用 GET_SIGNING_CERTIFICATESSigningInfo,才能正确处理签名证书轮换。示例使用 SHA-256;MD5 不应用于安全校验。

APK 签名方案差异与方法适用性

签名方案差异

  • v1(JAR):Android 1.0 起支持。对 META-INF 中的 APK 条目逐个签名,用于兼容 Android 6.0 及以下;签名后仍可能被修改 ZIP 结构。
  • v2:Android 7.0(API 24)起支持。校验整个 APK 的签名块,适用于 Android 7.0 及以上的完整性保护,通常与 v1 同时启用。
  • v3:Android 9(API 28)起支持。在 v2 基础上增加签名证书轮换证明,用于保留包名的前提下轮换应用签名密钥。
  • v4:Android 11(API 30)起支持。生成独立 .idsig 以支持增量安装时的分块校验,适用于 adb install --incremental 等场景;常规分发仍需 v2/v3。

签名工具能力

  • jarsigner:仅支持 v1。只能用于必须兼容旧系统的 v1 场景,不能单独作为现代 APK 发布签名。
  • apksigner --ks:支持 v1、v2、v3、v4。推荐使用,输入为 JKS 或 PKCS12 Keystore。
  • apksigner --key --cert:支持 v1、v2、v3、v4。推荐用于 Android 源码中的 .pk8 + .x509.pem 密钥材料。
  • Android Studio / Gradle:支持 v1、v2、v3、v4。推荐日常构建,由 AGP 根据配置和 SDK 范围处理签名方案。
  • signapk.jar:能力取决于 Android 源码分支,不应假设支持 v3/v4;仅在 AOSP 既有构建流程明确要求时使用。

实践上,minSdkVersion < 24 时保留 v1 和 v2;只支持 Android 9 及以上且需要密钥轮换时启用 v3;不要只启用 v4,因为 .idsig 不是可独立安装的 APK 签名。签名后统一执行 apksigner verify --verbose --print-certs


签名文件转换

.jks 转 .p12

keytool -importkeystore -srckeystore ./genkey.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore genkey.p12
输入目标密钥库口令://123456
再次输入新口令://123456
输入源密钥库口令://123123
输入 <business> 的密钥口令 //123456
已成功导入别名 business 的条目。
已完成导入命令: 1 个条目成功导入, 0 个条目失败或取消

.p12 转 .keystore

keytool -v -importkeystore -srckeystore ./genkey.p12 -srcstoretype PKCS12 -destkeystore ./genkey.keystore -deststoretype JKS
输入目标密钥库口令://123123
再次输入新口令://123123
输入源密钥库口令://123456
已成功导入别名 business 的条目。
已完成导入命令: 1 个条目成功导入, 0 个条目失败或取消
[正在存储./genkey.keystore]

可以用以下命令效验.jks和.keystore签名数据,密码都是123123

keytool -list -v -keystore genkey.jks
keytool -list -v -keystore genkey.keystore

.p12 转 .x509.pem 和 .pk8

需要用到openssl,windows可以在Git安装目录找到,如'C:\Program Files\Git\mingw64\bin',linux中没有的自行安装

  1. 导出证书和私钥。私钥转换在管道中完成,避免在磁盘生成未加密的 PEM 私钥。
openssl pkcs12 -in genkey.p12 -clcerts -nokeys -out genkey.x509.pem
openssl pkcs12 -in genkey.p12 -nocerts -nodes | openssl pkcs8 -topk8 -outform DER -inform PEM -out genkey.pk8 -nocrypt

两条命令都会提示输入 PKCS12 导入密码。-nodes 使管道中的私钥无密码保护,不能将输出重定向到普通文件,也不能在终端、日志或版本库中暴露。

  1. 至此 .x509.pem.pk8 已经生成,可以用如下命令签名。.pk8 是无加密私钥,应按发布密钥权限保存。 apksigner sign --key genkey.pk8 --cert genkey.x509.pem --in aligned.apk --out signed.apk

.pk8 + .x509.pem 转 .jks

在 Android 系统开发中,经常需要将平台签名文件转为 JKS,以便在 IDE 中使用。

# 1. pk8 转 PEM 格式私钥
openssl pkcs8 -inform DER -in platform.pk8 -out platform.key -nocrypt

# 2. 证书和私钥合并为 PKCS#12
openssl pkcs12 -export -in platform.x509.pem -inkey platform.key -out platform.p12 -name platform -passout pass:yourpassword

# 3. PKCS#12 转 JKS
keytool -importkeystore -srckeystore platform.p12 -srcstoretype PKCS12 -srcstorepass yourpassword -destkeystore platform.jks -deststoretype JKS -deststorepass yourpassword -destkeypass yourpassword -alias platform

生成 platform.jks(别名 platform,密码 yourpassword)。platform.key 是未加密私钥,转换后应立即安全删除;platform.p12 同样包含私钥,不能提交到版本库。

app/build.gradle 中使用:

signingConfigs {
    platform {
        storeFile file("../platform.jks")
        storePassword "yourpassword"
        keyAlias "platform"
        keyPassword "yourpassword"
    }
}
buildTypes {
    release {
        signingConfig signingConfigs.platform
    }
}

参考文章 keytool Command(Oracle JDK 25) apksigner(Android Developers) APK 签名方案(AOSP) Package visibility(Android Developers)


本文链接:Android应用的签名及签名文件转换 - https://h89.cn/archives/86.html

版权声明:原创文章 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文链接和本声明。

标签: 签名, android, openssl, keytool, jarsigner, apksigner, JKS, PKCS12

🎓 呈言英语 智能英语学习平台
📚单词学习 🎧听说练习 📖阅读理解 ✏️拼写练习 🌟 AI智能推荐 · 科学记忆曲线
🚀 立即开始免费学习

添加新评论