目录
一篇文章讲清楚 Android 签名:PK8、X509.PEM、JKS 是什么?它们有什么关系?

在 Android 开发过程中,我们经常会遇到几个与应用签名相关的文件:

plaintext
复制代码
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 签名(Android App Signing)是 Android 系统用来验证应用程序身份与完整性的一种安全机制。

简单来说,Android 签名主要解决两个问题:

  1. 身份认证:确认当前 APK 使用的是哪个签名密钥,从而判断它是否与之前安装的应用具有相同的签名身份。
  2. 完整性验证:确认 APK 签名所保护的内容没有被非法修改。

例如,我们开发了一款 Android 应用,第一次发布的是 <span>1.0</span>版本,后续发布了 <span>2.0</span>版本。

plaintext
复制代码
应用名称:MyApp
包名:com.example.myapp

1.0 版本
  │
  └── 使用 Key-A 签名
          │
          ▼
      安装成功
          │
          ▼
2.0 版本
  │
  ├── 使用 Key-A 签名 → 通常可以正常升级
  │
  └── 使用 Key-B 签名 → 通常无法直接覆盖升级

Android 在更新应用时,不仅会检查应用包名,还会检查签名身份是否兼容。

因此,即使两个 APK 的包名完全相同,如果签名不兼容,通常也无法直接覆盖安装。

需要注意的是,Android 的新版本支持满足特定条件的签名密钥轮换,因此并非任何密钥变化都必然导致升级失败。

另外,签名并不代表应用一定安全,也不意味着应用经过了 Google 的审核。它主要用于确认应用的签名身份,以及保护 APK 的完整性。

二、Android 签名的工作原理

Android 签名主要基于非对称密码学。

非对称密码学涉及两个重要概念:

  • 私钥(Private Key):由开发者保管,用于生成数字签名,不能泄露。
  • 公钥(Public Key):可以公开,用于验证数字签名。

公钥与私钥具有数学上的对应关系。

2.1 签名是如何产生的?

签名过程可以简化为以下流程:

概念示意图:不同 APK 签名方案在具体数据结构和摘要计算方式上有所区别。

整个签名过程可以理解为:

  1. Android 签名工具按照指定签名方案处理 APK 内容。
  2. 使用哈希算法计算需要保护的数据摘要。
  3. 使用私钥和数字签名算法生成签名。
  4. 将签名数据及相应的证书信息写入 APK。

这里需要特别说明:

数字签名并不是使用私钥把整个 APK 加密一遍。

签名与加密是两个不同的概念。

  • 加密主要解决数据保密性问题。
  • 签名主要解决数据完整性和签名身份验证问题。

所以,一个经过签名的 APK,里面的代码和资源并不会因为签名而自动变成密文。

2.2 Android 如何验证签名?

当用户安装或者更新 APK 时,Android 系统会进行签名校验。

其基本过程为:

plaintext
复制代码
              已签名 APK
                   │
         ┌─────────┴─────────┐
         ▼                   ▼
      APK 内容            数字签名
         │                   │
         ▼                   │
    重新计算摘要              │
         │                   │
         └─────────┬─────────┘
                   │
                   ▼
          使用公钥验证签名
                   │
          ┌────────┴────────┐
          ▼                 ▼
        验证成功           验证失败
          │                 │
       内容完整          安装校验失败

实际上,Android 还会根据签名证书及相关签名历史判断应用是否具有兼容的更新身份。

这就意味着,即使第三方修改了 APK 的内容,如果没有对应的私钥,也无法生成能够通过原有签名身份验证的新签名。

三、PK8、X509.PEM、JKS 分别是什么?

理解签名原理之后,再来看这三个文件就比较简单了。

首先看一张对比表:

文件 本质 保存的内容 主要作用
<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 文件?

PK8 指的是使用 PKCS#8 标准表示的私钥。

PKCS 的全称是 Public-Key Cryptography Standards,即公钥密码学标准。

其中,PKCS#8 专门用于描述私钥信息的存储格式。

例如:

plaintext
复制代码
platform.pk8
release.pk8
testkey.pk8

这些文件通常保存 Android 签名所需要的私钥。

注意:<span>.pk8</span>不是一种签名算法,而是一种私钥存储格式。它可以表示 RSA、EC 等不同类型的私钥。

在 Android AOSP 工程中,<span>.pk8</span>文件通常使用 DER 二进制编码。

可以使用 OpenSSL 查看:

plaintext
复制代码
openssl pkcs8 \
  -inform DER \
  -in platform.pk8 \
  -nocrypt \
  -out platform.key

该命令适用于未加密的 PKCS#8 DER 私钥,输出为 PEM 格式的私钥文件。

转换之后的内容大致如下:

plaintext
复制代码
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAAS...
...
-----END PRIVATE KEY-----

需要注意两件事情。

首先,私钥文件与签名密钥的安全直接相关。如果私钥泄露,攻击者就可能使用相同的签名身份签署恶意 APK。

其次,私钥可以进行密码保护,但 Android AOSP 的某些构建场景要求使用未加密的私钥文件,因此必须通过文件权限、访问控制及安全存储措施保护它。

五、什么是 X509.PEM 文件?

X509.PEM 实际上涉及两个概念:

  • X.509:数字证书的数据结构标准。
  • PEM:一种常用的文本编码封装格式。

因此,<span>platform.x509.pem</span>可以理解为:

一个使用 PEM 编码保存的 X.509 数字证书。

它通常包含以下信息:

plaintext
复制代码
X.509 Certificate
├── Version             证书版本
├── Serial Number       证书序列号
├── Signature Algorithm 证书签名算法
├── Issuer              证书颁发者
├── Validity            有效期
├── Subject             证书主体
├── Public Key          公钥信息
└── Signature           证书自身的数字签名

其文本内容通常如下:

plaintext
复制代码
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJ...
...
-----END CERTIFICATE-----

通过 OpenSSL 可以查看证书内容:

plaintext
复制代码
openssl x509 \
  -in platform.x509.pem \
  -text \
  -noout

如果只想查看公钥,可以执行:

plaintext
复制代码
openssl x509 \
  -in platform.x509.pem \
  -pubkey \
  -noout

这里有一个容易混淆的问题:

证书和公钥是同一个东西吗?

严格来说,并不是。

公钥只是证书中的一部分,证书还包含主体、有效期、签名算法等信息。

另外,Android APK 签名通常使用自签名证书,而不是必须向第三方 CA 机构申请证书。

这与 HTTPS 网站使用的证书体系有所不同。

Android 应用签名的核心并不是验证证书是否由某个可信 CA 签发,而是验证 APK 的签名,并结合安装记录判断应用的签名身份是否符合要求。

因此,即使一个 X.509 证书是开发者自己生成的,也可以用来为 Android 应用签名。

六、什么是 JKS 文件?

JKS 的全称是 Java KeyStore,也就是 Java 密钥库。

与 PK8 和 X509.PEM 不同,JKS 不是单独用于保存私钥或证书的文件格式,而是一种密钥库格式。

它可以管理多个密钥条目,每个条目使用一个别名(Alias)进行标识。

例如:

plaintext
复制代码
release.jks
│
├── Alias: app-release
│   ├── Private Key
│   └── Certificate Chain
│       └── X.509 Certificate
│
└── Alias: another-app
    ├── Private Key
    └── Certificate Chain
        └── X.509 Certificate

通过一个 JKS 文件,就可以集中管理多个密钥及其证书。

一般来说,JKS 可以通过密码保护密钥库和其中的私钥。

6.1 JKS 与 Keystore 的区别

在 Android 开发中,我们经常看到这两种文件:

plaintext
复制代码
release.jks
release.keystore

它们经常被当作同一种东西使用,但这里需要区分两个概念:

  • KeyStore:密钥库的通用概念。
  • JKS:Java KeyStore 的一种具体存储格式。

除了 JKS,Java 还支持 PKCS12 等密钥库格式。

尤其需要注意的是,在现代 JDK 中,<span>keytool</span>默认使用的密钥库类型通常是 <span>PKCS12</span>,而不一定是 <span>JKS</span>。

因此,文件扩展名叫 <span>.jks</span>并不能百分之百证明它的实际格式就是 JKS。

如果需要明确生成 JKS 格式,需要指定:

plaintext
复制代码
-storetype JKS

如果希望使用 PKCS12 格式,则指定:

plaintext
复制代码
-storetype PKCS12

6.2 JKS 为什么适合 Android 应用开发?

主要是因为 Android 的 Gradle 构建工具和 Java 工具链可以直接使用 Java KeyStore。

例如,在 Android Studio 中配置 Release 签名:

plaintext
复制代码
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 到底是什么关系?

这是本文最重要的部分。

首先要明确:

PK8 + X509.PEM 和 JKS 并不是两套不同的 Android 签名原理,而是两种不同的密钥与证书存储方式。

可以这样理解:

第一种方式是将私钥和证书分别保存在两个文件中。

plaintext
复制代码
release.pk8       → 私钥
release.x509.pem  → 数字证书(包含公钥)

第二种方式是将私钥和证书统一放入一个密钥库中。

plaintext
复制代码
release.jks
  ├── 私钥
  └── 数字证书(包含公钥)

只要两种方式保存的是同一把私钥与对应的证书,并且使用相同的 APK 签名方案和参数,就可以实现等价的签名身份。

这种区别类似于:

  • PK8 + X509.PEM:分别保存的文件。
  • JKS:统一管理这些内容的容器。

但是,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

八、如何生成 Android 签名文件?

前面介绍了签名文件的基本概念,下面通过实际命令演示如何生成签名文件。

8.1 方式一:生成 JKS 文件

Java 提供了一个密钥管理工具 <span>keytool</span>,它随 JDK 一起提供。

执行以下命令:

plaintext
复制代码
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> 指定证书有效期,单位为天

执行过程中会要求输入密钥库密码,以及证书主体信息等。

完成后,就会生成:

plaintext
复制代码
release.jks

这个文件中已经包含了对应的私钥及 X.509 证书。

需要说明的是,这里的 <span>SHA256withRSA</span>是证书自身的签名算法设置,并不代表所有 APK 签名方案都必须采用该算法。

8.2 查看 JKS 文件内容

使用以下命令:

plaintext
复制代码
keytool -list -v \
  -keystore release.jks \
  -storetype JKS

可以查看密钥别名、证书有效期、签名算法、公钥算法和 SHA-256 指纹等信息。

其中,SHA-256 证书指纹在 Android 开发中非常常见,例如接入部分第三方 SDK、应用身份校验时,都可能需要它。

8.3 方式二:生成 PK8 和 X509.PEM

我们也可以使用 OpenSSL 直接生成独立的私钥和证书文件。

首先,生成 RSA 私钥:

plaintext
复制代码
openssl genpkey \
  -algorithm RSA \
  -pkeyopt rsa_keygen_bits:3072 \
  -out release.key

此时得到的是 PEM 格式的私钥。

接着,转换为 PKCS#8 DER 格式:

plaintext
复制代码
openssl pkcs8 \
  -topk8 \
  -inform PEM \
  -outform DER \
  -in release.key \
  -out release.pk8 \
  -nocrypt

最后,生成一个 X.509 自签名证书:

plaintext
复制代码
openssl req \
  -new \
  -x509 \
  -key release.key \
  -out release.x509.pem \
  -days 3650 \
  -sha256 \
  -subj "/CN=Android Release/O=Example/C=CN"

完成后,得到:

plaintext
复制代码
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 的特定构建场景。生产环境需要对该文件采取严格的访问控制。

九、PK8、X509.PEM 与 JKS 如何相互转换?

在实际开发过程中,我们可能会遇到以下情况:

  • 已有 JKS 文件,但系统开发工具需要 PK8 和 X509.PEM。
  • 已有 PK8 和 X509.PEM,但 Android Studio 需要 JKS。
  • 需要将多个应用统一到自动化签名流程。

这些情况下,就需要进行格式转换。

9.1 JKS 转换为 PK8 + X509.PEM

假设我们已经有:

plaintext
复制代码
release.jks

第一步,将 JKS 转换为 PKCS12 密钥库:

plaintext
复制代码
keytool -importkeystore \
  -srckeystore release.jks \
  -srcstoretype JKS \
  -srcalias release \
  -destkeystore release.p12 \
  -deststoretype PKCS12 \
  -destalias release

第二步,导出证书:

plaintext
复制代码
keytool -exportcert \
  -rfc \
  -alias release \
  -keystore release.jks \
  -storetype JKS \
  -file release.x509.pem

第三步,从 PKCS12 文件中提取私钥:

plaintext
复制代码
openssl pkcs12 \
  -in release.p12 \
  -nocerts \
  -nodes \
  -out release-private.pem

第四步,将私钥转换为 PK8 DER 格式:

plaintext
复制代码
openssl pkcs8 \
  -topk8 \
  -inform PEM \
  -outform DER \
  -in release-private.pem \
  -out release.pk8 \
  -nocrypt

最终得到:

plaintext
复制代码
release.jks
    │
    ▼
release.p12
    │
    ├── release-private.pem
    │          │
    │          ▼
    │      release.pk8
    │
    └── release.x509.pem

注意,<span>release-private.pem</span>和 <span>release.pk8</span>在上述示例中均为未加密私钥,转换后应及时清理不再需要的中间文件,并严格限制访问权限。

9.2 PK8 + X509.PEM 转换为 JKS

假设我们已经有:

plaintext
复制代码
platform.pk8
platform.x509.pem

第一步,将 PK8 私钥转换为 PEM 格式:

plaintext
复制代码
openssl pkey \
  -inform DER \
  -in platform.pk8 \
  -out platform.key

这里假设 <span>platform.pk8</span>是未加密的 PKCS#8 DER 私钥。

第二步,使用私钥和证书生成 PKCS12 密钥库:

plaintext
复制代码
openssl pkcs12 \
  -export \
  -inkey platform.key \
  -in platform.x509.pem \
  -name platform \
  -out platform.p12

第三步,将 PKCS12 转换为 JKS:

plaintext
复制代码
keytool -importkeystore \
  -srckeystore platform.p12 \
  -srcstoretype PKCS12 \
  -destkeystore platform.jks \
  -deststoretype JKS

最终得到:

plaintext
复制代码
platform.pk8 ──────────┐
                       │
platform.x509.pem ─────┤
                       ▼
                  platform.p12
                       │
                       ▼
                  platform.jks

这样,我们就实现了 PK8 + X509.PEM 与 JKS 之间的转换。

需要注意:转换只是改变密钥与证书的存储格式,并不会自动生成新的签名身份。

同时,转换前必须确保私钥与证书相互匹配,否则无法正确完成签名。

十、如何使用这些文件对 APK 进行签名?

Android SDK 提供了一个官方签名工具:

<span>apksigner</span>

它通常位于:

plaintext
复制代码
$ANDROID_HOME/build-tools/<版本>/apksigner

需要注意,<span>apksigner</span>主要用于 APK 签名,不是用于直接签署 AAB 文件的工具。

10.1 使用 JKS 文件签名

假设有一个尚未签名的 APK:

plaintext
复制代码
app-unsigned.apk

首先,通过 <span>zipalign</span>对 APK 进行对齐:

plaintext
复制代码
zipalign -p -f 4 \
  app-unsigned.apk \
  app-aligned.apk

然后签名:

plaintext
复制代码
apksigner sign \
  --ks release.jks \
  --ks-key-alias release \
  --out app-release.apk \
  app-aligned.apk

执行过程中,工具会要求输入相应的密码。

完成后,得到:

plaintext
复制代码
app-release.apk

10.2 使用 PK8 + X509.PEM 签名

也可以不使用 JKS,而是直接指定私钥和证书:

plaintext
复制代码
apksigner sign \
  --key release.pk8 \
  --cert release.x509.pem \
  --out app-release.apk \
  app-aligned.apk

这两种签名方式的最终目的完全一致。

区别仅在于签名工具从哪里加载私钥与证书。

官方 <span>apksigner</span>文档明确支持这两种方式。

Android Developers+1

10.3 验证 APK 签名

签名完成后,可以使用:

plaintext
复制代码
apksigner verify \
  --verbose \
  --print-certs \
  app-release.apk

输出中会包含类似信息:

plaintext
复制代码
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 的签名证书信息及其指纹。

十一、Android 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> 文件

例如,我们可以使用:

plaintext
复制代码
release.jks

生成一个支持 V2、V3 签名的 APK。

同样也可以使用:

plaintext
复制代码
release.pk8
release.x509.pem

生成支持相同签名方案的 APK。

所以要区分两个不同的维度:

  • PK8、X509.PEM、JKS:解决私钥和证书如何保存的问题。
  • V1、V2、V3、V4:解决 APK 如何进行数字签名与验证的问题。

签名方案并不是越多越好,而是需要根据应用支持的 Android 版本,由构建工具选择合适的组合。

十二、什么是 Android 平台签名(Platform Signing)?

在 Android 系统开发中,经常可以看到:

plaintext
复制代码
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

12.1 Platform 签名有什么特殊之处?

Android 系统中存在一些签名级权限。

例如,某些权限的保护级别为:

plaintext
复制代码
android:protectionLevel="signature"

对于这类权限,Android 会检查申请应用与权限声明方之间的签名关系。

当一个系统应用使用与声明权限的 Android 平台组件相兼容的签名身份时,就可能满足相关签名权限的授权条件。

但这里有一个重要区别:

使用 Platform 签名不等于自动获得所有系统权限,也不等于应用自动成为系统应用。

系统权限的最终授予还受到权限声明、保护级别、安装位置、系统版本和相关授权策略等因素影响。

12.2 AOSP 的 Platform 签名和厂商的 Platform 签名一样吗?

不一定。

AOSP 源码中自带的 <span>platform.pk8</span>和 <span>platform.x509.pem</span>通常是公开的测试密钥。

它们适合开发和测试环境,不适合直接作为正式产品的安全身份使用。

设备厂商发布正式系统时,通常应替换为自行管理的生产签名密钥。

所以:

plaintext
复制代码
AOSP 测试 Platform 签名
          ≠
设备厂商生产 Platform 签名

即使两份文件都叫:

plaintext
复制代码
platform.pk8
platform.x509.pem

也不意味着它们保存着相同的密钥。

文件名相同不代表签名身份相同,真正需要比较的是签名证书及其对应的签名身份。

十三、常见问题

13.1 APK 已经签名,还能修改吗?

APK 文件本身仍然可以被修改。

但是,如果修改了签名方案所保护的内容,原来的数字签名就可能失效。

修改后的 APK 通常需要重新签名,而重新签名者是否拥有原来的私钥,决定了它能否保持相同的签名身份。

13.2 签名证书过期了,APK 就无法运行了吗?

通常不会。

Android APK 签名证书的有效期并不采用 HTTPS 证书那种由系统 CA 信任链进行有效期校验的方式。

因此,证书到期通常不会直接导致已安装 APK 无法运行。

不过,仍然建议在生成签名证书时设置足够长的有效期,并留意应用商店、第三方平台或相关工具的要求。

13.3 JKS 文件丢失了怎么办?

如果丢失的是应用原有的签名私钥,并且没有备份,那么通常无法继续以相同签名身份发布更新。

但存在一些例外,例如:

  • 使用 Google Play App Signing 时,上传密钥丢失可以按平台流程重置。
  • Android 支持满足特定条件的签名密钥轮换。
  • 如果还有同一私钥的 PK8、PEM 或其他密钥库备份,可以重新制作 JKS。

因此,签名私钥比 <span>.jks</span>这个文件名本身更加重要。

13.4 能否从 APK 中提取 PK8 私钥?

不能。

正常签名的 APK 包含签名数据和公钥证书等验证所需的信息,但不会包含签名私钥。

因此,可以提取 APK 的签名证书,却无法从证书反推出对应的私钥。

这也是非对称密码学能够用于数字签名的基础。

13.5 能否用 X509.PEM 直接签名 APK?

不能。

X509.PEM 证书通常只包含公钥和证书相关信息。

APK 数字签名需要使用对应的私钥。

所以一般需要:

plaintext
复制代码
PK8 + X509.PEM

或者:

plaintext
复制代码
JKS(包含私钥与证书)

仅有证书无法完成签名。

13.6 如何判断 PK8 和 X509.PEM 是否匹配?

可以分别提取公钥,计算摘要后对比。

从私钥中提取公钥:

plaintext
复制代码
openssl pkey \
  -inform DER \
  -in release.pk8 \
  -pubout \
  -outform DER \
  | openssl dgst -sha256

从证书中提取公钥:

plaintext
复制代码
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 官方文档:

本文由 拉大锯 原创发布于 阳光沙滩 , 未经作者授权,禁止转载
评论
0 / 1024
推荐文章
一篇文章讲清楚 Android 签名:PK8、X509.PEM、JKS 是什么?它们有什么关系?
深入解析Android应用与系统签名的核心文件:PK8、X509.PEM与JKS。本文从非对称加密原理出发,厘清私钥、证书与密钥库的本质区别及相互转换方法,涵盖AOSP平台签名与普通应用发布的实际场景。通过对比V1-V4签名方案及解答常见疑难,助你掌握保障APK完整性与身份认证的关键技术,规避签名丢失或冲突风险。
系统监控运维系统
你是否也深受证书过期、磁盘满载等运维痛点的折磨?排查耗时,影响线上体验。本文分享一款自研运维系统面板,集成证书统一监测、资源预警及WebSSH等功能,实现问题快速定位与处理。附代码共享链接,欢迎参考或二次开发,提升运维效率。
断点说搞个万年历,怎么搞?
想快速拥有一个专属的程序员日历?只需一篇清晰的设计文档,剩下的交给AI!本文带你从零搭建“sob-calendar”项目,集成农历、节气及独特的程序员运势功能。通过实际案例展示如何利用AI高效生成代码并部署上线,让繁琐的开发工作变得轻松有趣,快来体验科技与传统历法的奇妙碰撞吧!
linux服务器的日志、容器清理
本手册提供了一套全面的Linux服务器磁盘清理与排查方法,帮助用户高效管理磁盘空间。通过查看磁盘使用情况、分析Docker占用、清理无用容器和日志,确保系统运行流畅。适合需要优化服务器性能的运维人员参考。
记给网站接入图形验证码
本文分享了如何为个人站点添加图形验证码以提升安全性,详细介绍了使用第三方接口实现验证码功能的过程,并通过代码示例展示了具体实现方式。对于关注网站安全的开发者来说,这篇文章提供了实用的参考价值。
Java开发环境搭建
本文是一份详细的Java开发环境搭建指南,涵盖了Java的发展历史、版本选择及安装配置过程。无论你是初学者还是有经验的开发者,都能从中找到实用的信息和步骤,帮助你快速上手Java开发。
Vue3.js项目的创建
本文详细介绍了如何使用Vue3创建项目,包括Node.js的安装和配置,以及项目的初始化过程。适合初学者学习如何快速搭建Vue3开发环境,并推荐了相关课程供进一步学习。
Network Tools : 您的运维“助手”升级了
Network Tools是一款专为解决运维难题而设计的软件,集设备管理、配置备份、性能监控和告警处理于一体。通过统一的工作台,帮助运维人员更高效地管理资产、定位故障并提升团队协作效率。无论是设备在线状态还是具体问题排查,都能得到全面支持。
Ubuntu 软件安装与彻底卸载指南
本文详细介绍了在 Ubuntu 系统中如何规范安装和彻底卸载 .deb 格式软件,包括依赖修复、残留清理以及图形化工具推荐,适合需要维护系统环境的用户。
JavaWeb开发客户程序更新特殊环境下的骚操作
本文分享了在无外网环境下,如何利用Docker和docker-compose实现高效部署的实战经验。通过基础镜像和本地jar包挂载的方式,解决了内网环境下的依赖问题,适合需要灵活部署方案的技术人员参考。
Linux 防火墙完全指南:从原理到多发行版实战
本文全面解析Linux防火墙的底层原理与主流工具配置,涵盖UFW、firewalld、iptables、nftables等方案,适合不同发行版用户。从基础概念到生产环境加固建议,助你掌握核心安全策略,确保服务器与网络的安全运行。
水上派对英语单词篇
探索FLOATOPIA水上派对的完整指南,涵盖签到流程、场地指引、安全须知、活动安排及夜场舞会等实用英语词汇与对话。无论是初学者还是老手,都能轻松掌握关键术语,享受完美水上体验。
麒麟操作系统下使用 virt-customize 重置虚拟机 root 密码实战
本文详细介绍了在麒麟操作系统环境下使用 virt-customize 工具重置 KVM 虚拟机 root 密码的完整流程,包含安装步骤、常见问题及解决方案,帮助运维人员高效处理密码遗忘或批量初始化场景,避免数据丢失。
浅析JS预编译-函数篇
本文详细讲解了JavaScript中函数的预编译过程,包括AO对象的创建、变量和函数声明的提升,以及执行阶段的变化。适合初学者了解JS执行机制,帮助深入理解代码运行逻辑。
访问Github的另一种姿势
本文介绍了如何通过Watt ToolKit等工具加速访问Github,适合对网络技术感兴趣的读者。内容涵盖工具介绍、下载和使用方法,以及一些实用技巧,值得一看。
使用Gulp压缩JS
本文详细介绍了如何使用Gulp压缩JavaScript文件,特别是支持ES6语法的处理方法。通过实际操作和效果对比,帮助开发者提升代码性能,适合对前端自动化工具感兴趣的读者。
使用Gulp压缩CSS
本文详细介绍了如何使用Gulp工具对CSS文件进行压缩,提升网页加载速度。适合需要优化静态资源的开发者,提供清晰的步骤和示例,帮助快速上手。
从0部署Nuxtjs项目
本文详细介绍了如何将Nuxt.js项目部署到公网,涵盖Node.js安装、环境配置、源码上传及使用PM2启动服务的全过程,适合开发者学习和实践。
Linux 命令行美化利器:tree 命令完全指南(安装 + 全部参数详解)
掌握 Linux 下的 `tree` 命令,快速直观地查看目录结构。本文详细讲解安装方法、参数含义及实战场景,适合开发者和系统管理员提升工作效率。
Linux常用命令(一)
本文详细介绍了Linux基础命令,包括文件操作、系统显示、网络状态、软件包管理等,适合初学者学习和查阅,是掌握Linux系统的实用指南。
大模型推理参数解码:温度、Top-p 与核采样如何塑造生成文本的灵魂
探索大语言模型的采样参数如何塑造生成内容,从温度到Top-p,从惩罚机制到熵控制,揭示背后的信息论原理与实践策略。了解如何通过参数调优,让AI既保持知识的严谨,又拥有创造力的自由。
记从0使用Claude code写代码
本文介绍了如何使用Claude Code搭配DeepSeek进行编程,详细讲解了安装Node.js、全局安装Claude、配置模型以及使用方法。对于开发者来说,这是一份实用的技术教程,尤其适合对AI编程工具感兴趣的读者。
Android-Studio-Gradle同步失败-Library为null的通用排查指南
遇到 Android Studio Gradle Sync 失败时,不要轻易删除全局缓存。本文详细解析了错误原因,并提供了一系列精准的排查步骤和解决方案,帮助开发者快速定位并解决问题,提升开发效率。
PHP实现密码加密
本文详细介绍了Bcrypt密码加密技术及其在PHP中的应用,帮助开发者提升用户密码的安全性。通过实际代码示例,展示了如何使用password_hash和password_verify函数进行密码加密与验证,是学习网络安全知识的实用指南。
WordCloud效果,滚动标签
本文详细介绍了如何在Vue项目中集成WordCloud组件,包括依赖安装、属性配置和使用方法。适合开发者学习如何实现数据可视化功能,提升项目交互体验。
从0使用WordPress搭建一个优美的网站
本文详细介绍了如何使用WordPress搭建一个美观的网站,从安装到主题配置和功能拓展,为读者提供了实用的操作指南。无论是初学者还是有一定经验的开发者,都能从中获得有价值的参考。
JS实现在网站底部添加运行时间
想知道如何在网站底部显示运行时间?本文详细讲解了通过JavaScript实现这一功能的方法,包括时间计算逻辑和代码实现。适合前端开发者学习参考,轻松为网站添加实用功能。
在Vercel上部署Hexo博客
本文详细介绍了如何在Vercel上快速部署Hexo博客,无需后端服务即可实现高效发布。相比传统方式,省去了手动生成和上传静态文件的步骤,更加便捷。适合想要搭建个人博客的开发者参考。
从0搭建一个Hexo博客
本文详细介绍了Hexo博客框架的使用方法,从安装到部署全流程讲解,适合想快速搭建个人博客的技术爱好者。内容清晰易懂,是入门Hexo的理想指南。
离线设备激活方案:古老的 Windows 光盘激活,离线算法授权等
本文深入解析了离线激活的核心原理与实现方式,从历史案例到现代技术,全面剖析了如何在无网络环境下确保软件授权的安全性。通过设备指纹、授权文件校验等手段,为开发者提供了可复用的架构设计思路,适用于工业、医疗等对网络依赖较低的场景。