在 Android 开发过程中,我们经常会遇到几个与应用签名相关的文件:
platform.pk8
platform.x509.pem
release.jks
release.keystore
尤其是在 Android 系统开发、系统应用开发、APK 打包发布时,经常会接触到这些文件。
对于普通 Android 应用开发者来说,可能只需要一个 <span>release.jks</span>文件就可以完成 APK 签名。而对于 Android 系统开发者来说,更多时候会接触到 <span>platform.pk8</span>和 <span>platform.x509.pem</span>。
那么,这些文件究竟是什么?它们之间又有什么区别和联系?为什么 Android 系统需要签名?签名又是如何保证 APK 安全的?
本文从 Android 签名的基本原理开始,逐步介绍 PK8、X509.PEM、JKS 的作用,以及它们之间的关系和实际使用方法。
Android 签名(Android App Signing)是 Android 系统用来验证应用程序身份与完整性的一种安全机制。
简单来说,Android 签名主要解决两个问题:
例如,我们开发了一款 Android 应用,第一次发布的是 <span>1.0</span>版本,后续发布了 <span>2.0</span>版本。
应用名称:MyApp
包名:com.example.myapp
1.0 版本
│
└── 使用 Key-A 签名
│
▼
安装成功
│
▼
2.0 版本
│
├── 使用 Key-A 签名 → 通常可以正常升级
│
└── 使用 Key-B 签名 → 通常无法直接覆盖升级
Android 在更新应用时,不仅会检查应用包名,还会检查签名身份是否兼容。
因此,即使两个 APK 的包名完全相同,如果签名不兼容,通常也无法直接覆盖安装。
需要注意的是,Android 的新版本支持满足特定条件的签名密钥轮换,因此并非任何密钥变化都必然导致升级失败。
另外,签名并不代表应用一定安全,也不意味着应用经过了 Google 的审核。它主要用于确认应用的签名身份,以及保护 APK 的完整性。
Android 签名主要基于非对称密码学。
非对称密码学涉及两个重要概念:
公钥与私钥具有数学上的对应关系。
签名过程可以简化为以下流程:
概念示意图:不同 APK 签名方案在具体数据结构和摘要计算方式上有所区别。
整个签名过程可以理解为:
这里需要特别说明:
数字签名并不是使用私钥把整个 APK 加密一遍。
签名与加密是两个不同的概念。
所以,一个经过签名的 APK,里面的代码和资源并不会因为签名而自动变成密文。
当用户安装或者更新 APK 时,Android 系统会进行签名校验。
其基本过程为:
已签名 APK
│
┌─────────┴─────────┐
▼ ▼
APK 内容 数字签名
│ │
▼ │
重新计算摘要 │
│ │
└─────────┬─────────┘
│
▼
使用公钥验证签名
│
┌────────┴────────┐
▼ ▼
验证成功 验证失败
│ │
内容完整 安装校验失败
实际上,Android 还会根据签名证书及相关签名历史判断应用是否具有兼容的更新身份。
这就意味着,即使第三方修改了 APK 的内容,如果没有对应的私钥,也无法生成能够通过原有签名身份验证的新签名。
理解签名原理之后,再来看这三个文件就比较简单了。
首先看一张对比表:
| 文件 | 本质 | 保存的内容 | 主要作用 |
|---|---|---|---|
<span>.pk8</span> |
PKCS#8私钥文件 | 私钥 | 生成数字签名 |
<span>.x509.pem</span> |
X.509证书文件 | 公钥、证书信息、证书签名等 | 提供用于验证APK签名的公钥 |
<span>.jks</span> |
JavaKeyStore密钥库 | 私钥、证书及证书链等 | 集中管理签名密钥和证书 |
<span>.keystore</span> |
常见密钥库文件名后缀 | 取决于实际存储格式 | 用途通常与<span>.jks</span> 类似 |
它们最核心的区别在于:
PK8 保存私钥,X509.PEM 保存证书,而 JKS 是用来集中保存和管理这些密钥、证书的容器。
下面分别介绍。
PK8 指的是使用 PKCS#8 标准表示的私钥。
PKCS 的全称是 Public-Key Cryptography Standards,即公钥密码学标准。
其中,PKCS#8 专门用于描述私钥信息的存储格式。
例如:
platform.pk8
release.pk8
testkey.pk8
这些文件通常保存 Android 签名所需要的私钥。
注意:<span>.pk8</span>不是一种签名算法,而是一种私钥存储格式。它可以表示 RSA、EC 等不同类型的私钥。
在 Android AOSP 工程中,<span>.pk8</span>文件通常使用 DER 二进制编码。
可以使用 OpenSSL 查看:
openssl pkcs8 \
-inform DER \
-in platform.pk8 \
-nocrypt \
-out platform.key
该命令适用于未加密的 PKCS#8 DER 私钥,输出为 PEM 格式的私钥文件。
转换之后的内容大致如下:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAAS...
...
-----END PRIVATE KEY-----
需要注意两件事情。
首先,私钥文件与签名密钥的安全直接相关。如果私钥泄露,攻击者就可能使用相同的签名身份签署恶意 APK。
其次,私钥可以进行密码保护,但 Android AOSP 的某些构建场景要求使用未加密的私钥文件,因此必须通过文件权限、访问控制及安全存储措施保护它。
X509.PEM 实际上涉及两个概念:
因此,<span>platform.x509.pem</span>可以理解为:
一个使用 PEM 编码保存的 X.509 数字证书。
它通常包含以下信息:
X.509 Certificate
├── Version 证书版本
├── Serial Number 证书序列号
├── Signature Algorithm 证书签名算法
├── Issuer 证书颁发者
├── Validity 有效期
├── Subject 证书主体
├── Public Key 公钥信息
└── Signature 证书自身的数字签名
其文本内容通常如下:
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJ...
...
-----END CERTIFICATE-----
通过 OpenSSL 可以查看证书内容:
openssl x509 \
-in platform.x509.pem \
-text \
-noout
如果只想查看公钥,可以执行:
openssl x509 \
-in platform.x509.pem \
-pubkey \
-noout
这里有一个容易混淆的问题:
证书和公钥是同一个东西吗?
严格来说,并不是。
公钥只是证书中的一部分,证书还包含主体、有效期、签名算法等信息。
另外,Android APK 签名通常使用自签名证书,而不是必须向第三方 CA 机构申请证书。
这与 HTTPS 网站使用的证书体系有所不同。
Android 应用签名的核心并不是验证证书是否由某个可信 CA 签发,而是验证 APK 的签名,并结合安装记录判断应用的签名身份是否符合要求。
因此,即使一个 X.509 证书是开发者自己生成的,也可以用来为 Android 应用签名。
JKS 的全称是 Java KeyStore,也就是 Java 密钥库。
与 PK8 和 X509.PEM 不同,JKS 不是单独用于保存私钥或证书的文件格式,而是一种密钥库格式。
它可以管理多个密钥条目,每个条目使用一个别名(Alias)进行标识。
例如:
release.jks
│
├── Alias: app-release
│ ├── Private Key
│ └── Certificate Chain
│ └── X.509 Certificate
│
└── Alias: another-app
├── Private Key
└── Certificate Chain
└── X.509 Certificate
通过一个 JKS 文件,就可以集中管理多个密钥及其证书。
一般来说,JKS 可以通过密码保护密钥库和其中的私钥。
在 Android 开发中,我们经常看到这两种文件:
release.jks
release.keystore
它们经常被当作同一种东西使用,但这里需要区分两个概念:
除了 JKS,Java 还支持 PKCS12 等密钥库格式。
尤其需要注意的是,在现代 JDK 中,<span>keytool</span>默认使用的密钥库类型通常是 <span>PKCS12</span>,而不一定是 <span>JKS</span>。
因此,文件扩展名叫 <span>.jks</span>并不能百分之百证明它的实际格式就是 JKS。
如果需要明确生成 JKS 格式,需要指定:
-storetype JKS
如果希望使用 PKCS12 格式,则指定:
-storetype PKCS12
主要是因为 Android 的 Gradle 构建工具和 Java 工具链可以直接使用 Java KeyStore。
例如,在 Android Studio 中配置 Release 签名:
android {
signingConfigs {
create("release") {
storeFile = file("release.jks")
storePassword = providers
.gradleProperty("RELEASE_STORE_PASSWORD")
.get()
keyAlias = "release"
keyPassword = providers
.gradleProperty("RELEASE_KEY_PASSWORD")
.get()
}
}
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("release")
}
}
}
其中:
| 配置项 | 含义 |
|---|---|
<span>storeFile</span> |
密钥库文件路径 |
<span>storePassword</span> |
密钥库密码 |
<span>keyAlias</span> |
密钥条目的别名 |
<span>keyPassword</span> |
私钥密码 |
示例通过 Gradle 属性读取密码,避免直接将明文密码写入构建脚本。实际生产中应使用不提交到代码仓库的本地配置或 CI 密钥管理服务。
这是本文最重要的部分。
首先要明确:
PK8 + X509.PEM 和 JKS 并不是两套不同的 Android 签名原理,而是两种不同的密钥与证书存储方式。
可以这样理解:
第一种方式是将私钥和证书分别保存在两个文件中。
release.pk8 → 私钥
release.x509.pem → 数字证书(包含公钥)
第二种方式是将私钥和证书统一放入一个密钥库中。
release.jks
├── 私钥
└── 数字证书(包含公钥)
只要两种方式保存的是同一把私钥与对应的证书,并且使用相同的 APK 签名方案和参数,就可以实现等价的签名身份。
这种区别类似于:
但是,JKS 不是简单地将两个文件压缩成 ZIP,它有自己的密钥库结构和访问保护机制。
实际开发中,二者常见的使用场景如下:
| 开发场景 | 常见签名文件 |
|---|---|
| AndroidStudio应用开发 | <span>.jks</span>、<span>.keystore</span> |
| Gradle自动化构建 | JKS或PKCS12密钥库 |
| AOSP系统编译 | <span>.pk8</span> +<span>.x509.pem</span> |
| Android系统应用签名 | <span>.pk8</span> +<span>.x509.pem</span> 或密钥库 |
| APK命令行签名 | 两种方式都支持 |
Google 官方 <span>apksigner</span>工具也明确支持上述两种签名输入方式。
Android Developers+1
前面介绍了签名文件的基本概念,下面通过实际命令演示如何生成签名文件。
Java 提供了一个密钥管理工具 <span>keytool</span>,它随 JDK 一起提供。
执行以下命令:
keytool -genkeypair \
-v \
-keystore release.jks \
-storetype JKS \
-alias release \
-keyalg RSA \
-keysize 3072 \
-sigalg SHA256withRSA \
-validity 10000
参数说明:
| 参数 | 说明 |
|---|---|
<span>-genkeypair</span> |
生成一对公钥和私钥 |
<span>-keystore</span> |
指定输出密钥库 |
<span>-storetype</span> |
指定密钥库格式 |
<span>-alias</span> |
设置密钥别名 |
<span>-keyalg</span> |
指定密钥算法 |
<span>-keysize</span> |
指定RSA密钥长度 |
<span>-sigalg</span> |
指定证书签名算法 |
<span>-validity</span> |
指定证书有效期,单位为天 |
执行过程中会要求输入密钥库密码,以及证书主体信息等。
完成后,就会生成:
release.jks
这个文件中已经包含了对应的私钥及 X.509 证书。
需要说明的是,这里的 <span>SHA256withRSA</span>是证书自身的签名算法设置,并不代表所有 APK 签名方案都必须采用该算法。
使用以下命令:
keytool -list -v \
-keystore release.jks \
-storetype JKS
可以查看密钥别名、证书有效期、签名算法、公钥算法和 SHA-256 指纹等信息。
其中,SHA-256 证书指纹在 Android 开发中非常常见,例如接入部分第三方 SDK、应用身份校验时,都可能需要它。
我们也可以使用 OpenSSL 直接生成独立的私钥和证书文件。
首先,生成 RSA 私钥:
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:3072 \
-out release.key
此时得到的是 PEM 格式的私钥。
接着,转换为 PKCS#8 DER 格式:
openssl pkcs8 \
-topk8 \
-inform PEM \
-outform DER \
-in release.key \
-out release.pk8 \
-nocrypt
最后,生成一个 X.509 自签名证书:
openssl req \
-new \
-x509 \
-key release.key \
-out release.x509.pem \
-days 3650 \
-sha256 \
-subj "/CN=Android Release/O=Example/C=CN"
完成后,得到:
release.key # PEM 格式私钥
release.pk8 # PKCS#8 DER 格式私钥
release.x509.pem # X.509 PEM 格式证书
其中,<span>release.key</span>与 <span>release.pk8</span>保存的是同一个私钥,只是编码格式不同。
而 <span>release.x509.pem</span>中保存的是由对应公钥生成的证书。
注意:上面的 PK8 文件没有密码保护,适用于演示以及需要无密码 PK8 的特定构建场景。生产环境需要对该文件采取严格的访问控制。
在实际开发过程中,我们可能会遇到以下情况:
这些情况下,就需要进行格式转换。
假设我们已经有:
release.jks
第一步,将 JKS 转换为 PKCS12 密钥库:
keytool -importkeystore \
-srckeystore release.jks \
-srcstoretype JKS \
-srcalias release \
-destkeystore release.p12 \
-deststoretype PKCS12 \
-destalias release
第二步,导出证书:
keytool -exportcert \
-rfc \
-alias release \
-keystore release.jks \
-storetype JKS \
-file release.x509.pem
第三步,从 PKCS12 文件中提取私钥:
openssl pkcs12 \
-in release.p12 \
-nocerts \
-nodes \
-out release-private.pem
第四步,将私钥转换为 PK8 DER 格式:
openssl pkcs8 \
-topk8 \
-inform PEM \
-outform DER \
-in release-private.pem \
-out release.pk8 \
-nocrypt
最终得到:
release.jks
│
▼
release.p12
│
├── release-private.pem
│ │
│ ▼
│ release.pk8
│
└── release.x509.pem
注意,<span>release-private.pem</span>和 <span>release.pk8</span>在上述示例中均为未加密私钥,转换后应及时清理不再需要的中间文件,并严格限制访问权限。
假设我们已经有:
platform.pk8
platform.x509.pem
第一步,将 PK8 私钥转换为 PEM 格式:
openssl pkey \
-inform DER \
-in platform.pk8 \
-out platform.key
这里假设 <span>platform.pk8</span>是未加密的 PKCS#8 DER 私钥。
第二步,使用私钥和证书生成 PKCS12 密钥库:
openssl pkcs12 \
-export \
-inkey platform.key \
-in platform.x509.pem \
-name platform \
-out platform.p12
第三步,将 PKCS12 转换为 JKS:
keytool -importkeystore \
-srckeystore platform.p12 \
-srcstoretype PKCS12 \
-destkeystore platform.jks \
-deststoretype JKS
最终得到:
platform.pk8 ──────────┐
│
platform.x509.pem ─────┤
▼
platform.p12
│
▼
platform.jks
这样,我们就实现了 PK8 + X509.PEM 与 JKS 之间的转换。
需要注意:转换只是改变密钥与证书的存储格式,并不会自动生成新的签名身份。
同时,转换前必须确保私钥与证书相互匹配,否则无法正确完成签名。
Android SDK 提供了一个官方签名工具:
<span>apksigner</span>
它通常位于:
$ANDROID_HOME/build-tools/<版本>/apksigner
需要注意,<span>apksigner</span>主要用于 APK 签名,不是用于直接签署 AAB 文件的工具。
假设有一个尚未签名的 APK:
app-unsigned.apk
首先,通过 <span>zipalign</span>对 APK 进行对齐:
zipalign -p -f 4 \
app-unsigned.apk \
app-aligned.apk
然后签名:
apksigner sign \
--ks release.jks \
--ks-key-alias release \
--out app-release.apk \
app-aligned.apk
执行过程中,工具会要求输入相应的密码。
完成后,得到:
app-release.apk
也可以不使用 JKS,而是直接指定私钥和证书:
apksigner sign \
--key release.pk8 \
--cert release.x509.pem \
--out app-release.apk \
app-aligned.apk
这两种签名方式的最终目的完全一致。
区别仅在于签名工具从哪里加载私钥与证书。
官方 <span>apksigner</span>文档明确支持这两种方式。
Android Developers+1
签名完成后,可以使用:
apksigner verify \
--verbose \
--print-certs \
app-release.apk
输出中会包含类似信息:
Verifies
Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): true
Signer #1 certificate DN: CN=Android Release, ...
Signer #1 certificate SHA-256 digest: ...
实际输出取决于 APK 使用的签名方案、目标 Android 版本以及工具版本,并非每个 APK 都会显示上述所有方案为 <span>true</span>。
<span>--print-certs</span>可以查看 APK 的签名证书信息及其指纹。
在实际开发中,还经常会看到 V1、V2、V3、V4 这样的签名方案名称。
需要注意:
V1、V2、V3、V4 是 APK 签名方案,与 PK8、X509.PEM、JKS 这些密钥文件格式不是同一个概念。
| 签名方案 | 支持版本 | 主要特点 |
|---|---|---|
| V1 | Android早期版本 | 基于JAR签名机制 |
| V2 | Android7.0+ | 对APK的整体结构提供更强的完整性保护 |
| V3 | Android9.0+ | 在V2基础上增加签名密钥轮换支持 |
| V4 | Android11+ | 辅助增量安装的签名方案,使用独立的<span>.idsig</span> 文件 |
例如,我们可以使用:
release.jks
生成一个支持 V2、V3 签名的 APK。
同样也可以使用:
release.pk8
release.x509.pem
生成支持相同签名方案的 APK。
所以要区分两个不同的维度:
签名方案并不是越多越好,而是需要根据应用支持的 Android 版本,由构建工具选择合适的组合。
在 Android 系统开发中,经常可以看到:
build/target/product/security/
├── testkey.pk8
├── testkey.x509.pem
├── platform.pk8
├── platform.x509.pem
├── shared.pk8
├── shared.x509.pem
├── media.pk8
├── media.x509.pem
├── networkstack.pk8
└── networkstack.x509.pem
这些通常是 AOSP 构建过程中使用的测试签名密钥。
其中比较常见的是:
| 签名名称 | 典型用途 |
|---|---|
<span>testkey</span> |
默认测试签名 |
<span>platform</span> |
Android核心平台相关组件 |
<span>shared</span> |
特定共享进程相关组件 |
<span>media</span> |
媒体及下载相关系统组件 |
<span>networkstack</span> |
NetworkStack等系统模块 |
这些文件的名称不是由 PKCS#8 或 X.509 标准定义的,而是 Android 构建系统中的签名密钥约定。
Android Open Source Project+1
Android 系统中存在一些签名级权限。
例如,某些权限的保护级别为:
android:protectionLevel="signature"
对于这类权限,Android 会检查申请应用与权限声明方之间的签名关系。
当一个系统应用使用与声明权限的 Android 平台组件相兼容的签名身份时,就可能满足相关签名权限的授权条件。
但这里有一个重要区别:
使用 Platform 签名不等于自动获得所有系统权限,也不等于应用自动成为系统应用。
系统权限的最终授予还受到权限声明、保护级别、安装位置、系统版本和相关授权策略等因素影响。
不一定。
AOSP 源码中自带的 <span>platform.pk8</span>和 <span>platform.x509.pem</span>通常是公开的测试密钥。
它们适合开发和测试环境,不适合直接作为正式产品的安全身份使用。
设备厂商发布正式系统时,通常应替换为自行管理的生产签名密钥。
所以:
AOSP 测试 Platform 签名
≠
设备厂商生产 Platform 签名
即使两份文件都叫:
platform.pk8
platform.x509.pem
也不意味着它们保存着相同的密钥。
文件名相同不代表签名身份相同,真正需要比较的是签名证书及其对应的签名身份。
APK 文件本身仍然可以被修改。
但是,如果修改了签名方案所保护的内容,原来的数字签名就可能失效。
修改后的 APK 通常需要重新签名,而重新签名者是否拥有原来的私钥,决定了它能否保持相同的签名身份。
通常不会。
Android APK 签名证书的有效期并不采用 HTTPS 证书那种由系统 CA 信任链进行有效期校验的方式。
因此,证书到期通常不会直接导致已安装 APK 无法运行。
不过,仍然建议在生成签名证书时设置足够长的有效期,并留意应用商店、第三方平台或相关工具的要求。
如果丢失的是应用原有的签名私钥,并且没有备份,那么通常无法继续以相同签名身份发布更新。
但存在一些例外,例如:
因此,签名私钥比 <span>.jks</span>这个文件名本身更加重要。
不能。
正常签名的 APK 包含签名数据和公钥证书等验证所需的信息,但不会包含签名私钥。
因此,可以提取 APK 的签名证书,却无法从证书反推出对应的私钥。
这也是非对称密码学能够用于数字签名的基础。
不能。
X509.PEM 证书通常只包含公钥和证书相关信息。
APK 数字签名需要使用对应的私钥。
所以一般需要:
PK8 + X509.PEM
或者:
JKS(包含私钥与证书)
仅有证书无法完成签名。
可以分别提取公钥,计算摘要后对比。
从私钥中提取公钥:
openssl pkey \
-inform DER \
-in release.pk8 \
-pubout \
-outform DER \
| openssl dgst -sha256
从证书中提取公钥:
openssl x509 \
-in release.x509.pem \
-pubkey \
-noout \
| openssl pkey \
-pubin \
-outform DER \
| openssl dgst -sha256
如果两条命令输出的 SHA-256 摘要一致,说明两者包含相同的公钥,因而可以确认私钥与证书的公钥相匹配。
上述方法假设 PK8 是未加密的 DER 格式私钥。
最后,用一张表总结全文:
| 概念 | 本质 | 作用 |
|---|---|---|
| PrivateKey | 私钥 | 生成数字签名 |
| PublicKey | 公钥 | 验证数字签名 |
| PK8 | PKCS#8私钥存储格式 | 保存私钥 |
| X509.PEM | PEM编码的X.509证书 | 保存公钥及证书信息 |
| JKS | Java密钥库格式 | 集中管理私钥与证书 |
| PKCS12 | 通用密钥库格式 | 保存私钥、证书及证书链 |
| apksigner | Android官方APK签名工具 | 生成和验证APK签名 |
| V1/V2/V3/V4 | APK签名方案 | 定义APK的签名和验证机制 |
贯穿整篇文章,我们其实只需要记住三个核心结论。
第一,Android 签名的核心是非对称密码学,通过私钥生成数字签名,通过公钥验证数字签名,实现应用身份识别和完整性校验。
第二,PK8 和 X509.PEM 是两个独立的文件。PK8 保存私钥,X509.PEM 保存数字证书,证书中包含与私钥对应的公钥。
第三,JKS 是一个密钥库容器,它可以同时管理私钥和证书。因此,PK8 + X509.PEM 与 JKS 可以表达相同的签名身份,只是保存方式不同。
对于 Android 应用开发者,通常使用 JKS 或 PKCS12 就足够了;对于 Android 系统开发者,了解 PK8 和 X509.PEM 则非常重要。
无论使用哪种格式,真正需要长期、严格保护的是签名私钥。一旦私钥丢失或者泄露,应用的后续更新与身份安全都可能受到影响。
本文主要参考 Android 官方文档:





















