简介:本资源为 Google Play 应用签名更新工具 PEPK(Play Encrypt Private Key)的完整 Java 源码实现,面向 Android 开发者、安全工程师及签名流程自动化需求者,解决官方 PEPK 工具闭源、不可定制、难以集成到 CI/CD 流程中的实际痛点。压缩包含 2000 个文件,其中 1999 个为 Java 源文件,覆盖 TLS 协议处理(TlsProtocol.java)、国密与国际密码算法实现(GOST3411_2012Digest.java、AESFastEngine.java、ThreefishEngine.java)、X.509 证书路径验证(PKIXCertPathReviewer.java)、EC 公钥运算(ECPoint.java)等核心模块,1 个说明性 TXT 文件;整体体积仅 5.02MB,代码结构清晰、模块职责明确,便于二次开发与签名逻辑深度定制。目前已有 372 人学习下载,读者可直接获取可编译运行的完整工程、理解 Google Play 签名链校验底层机制、复用密码学工具类,并基于源码快速构建私有签名服务或适配新签名策略。
1. 为什么改签 Google Play 应用时,光有 APK 文件根本跑不通:pepk 工具链的真实定位与不可替代性
很多开发者在首次尝试更新已上架 Google Play 的应用签名时,会陷入一个典型误区:以为只要生成新的 keystore、用 jarsigner 或 apksigner 重签 APK 就能直接上传。结果是——上传失败,报错You uploaded an APK that is not signed with the upload certificate,甚至更隐蔽的Your APK or Android App Bundle is signed with the wrong key。这类错误背后,不是签名流程错了,而是Google Play 的签名体系早已从“单证书直签”演进为“上传密钥 + 签名密钥分离”的双层信任模型。pepk(Play Encrypt Private Key)正是 Google 官方为解决这一模型下“安全迁移旧签名”而设计的专用工具,它不处理 APK 构建,也不替代 apksigner;它的唯一使命是:在离线、可控环境下,将你持有的上传密钥(upload key)安全地“委托”给 Google Play 后端的签名密钥(app signing key),完成密钥链的可信绑定。这不是玄学操作,而是 Google Play Console 强制要求的合规动作;适用于所有需要更换签名密钥(如原 keystore 丢失、泄露、过期或团队密钥轮换)的存量应用升级场景。如果你正卡在“老包还在上架、新包必须接续签名、但 Play Console 死活不认新密钥”这个节点,那么 pepk 不是可选项,而是当前技术路径下唯一被官方支持、可审计、可回溯的落地入口。
2. pepk 工具的本质:不是签名器,而是密钥委托协议的命令行实现
2.1 为什么不能跳过 pepk?从 Google Play 签名机制讲起
Google Play 自 2017 年起全面推行 App Signing by Google Play(ASBGP)服务。启用后,应用实际分发到用户设备的 APK/AAB 由 Google 持有的签名密钥(app signing key)签署,而非开发者上传的密钥。开发者仅持有并使用上传密钥(upload key)——它只用于向 Play Console 提交新版本,不参与最终用户安装包的签名。这种分离带来两大刚性约束:
- 上传密钥可轮换,但签名密钥不可见、不可导出、不可替换:一旦启用 ASBGP,原始签名密钥即由 Google 托管,开发者永远无法获取其私钥。
- 更换上传密钥需显式授权:若原上传密钥丢失或需更新,必须通过 Play Console 的「设置 → 应用完整性 → 应用签名」页面发起密钥更换流程,而该流程强制要求提交一个由 pepk 工具生成的加密密钥文件(.pem),作为“我确系该应用合法控制者,并授权用新上传密钥接管”的密码学凭证。
pepk 的核心作用,就是执行这个授权协议:它接收你的新上传密钥(.jks 或 .pem 格式)、旧上传密钥的证书(.der 或 .pem)、以及 Google 提供的公钥(play-app-signing-public-key.pem),通过 RSA-OAEP 加密和 SHA-256 哈希,生成一个结构化、不可伪造的.pem委托文件。这个文件本质是一段 Base64 编码的 ASN.1 数据,包含加密后的密钥指纹、时间戳、签名者身份等元信息,Play 后端用其自有私钥解密验证后,才允许将新上传密钥绑定至该应用的签名密钥链。
提示:pepk 不接触你的 app signing key,也不生成任何 APK;它只生成一份“数字委任状”。整个过程完全离线,无需网络连接,私钥永不离开本地机器。
2.2 下载与校验 pepk 工具包:避开镜像站陷阱的实操步骤
Google 官方仅通过 Android Studio 的 SDK Manager 分发 pepk 工具,不提供独立下载链接,也不托管于 GitHub 或 Maven 仓库。常见误操作是搜索 “pepk download” 后误入非官方镜像站,下载到篡改版或过期版本(如 v1.0.0 仅支持旧版密钥格式,v2.0.0+ 才兼容 ECDSA 密钥)。正确路径如下:
# 步骤 1:确保已安装 Android Studio(建议 Arctic Fox 或更高版本) # 步骤 2:打开 SDK Manager → SDK Tools 标签页 → 勾选 "Google Play Licensing"(注意:名称易混淆,实际包含 pepk) # 步骤 3:点击 Apply 安装,完成后工具位于: # macOS: ~/Library/Android/sdk/extras/google/play-pem/ # Windows: %LOCALAPPDATA%\Android\Sdk\extras\google\play-pem\ # Linux: ~/Android/Sdk/extras/google/play-pem/安装后,务必校验工具完整性。官方未提供 SHA256 哈希值,但可通过以下方式交叉验证:
# 进入 pepk 目录后,检查关键文件存在性与权限 ls -l pepk.jar play-app-signing-public-key.pem # 正常应输出(以 macOS 为例): # -rwxr-xr-x 1 user staff 1234567 1 Jan 00:00 pepk.jar # -rw-r--r-- 1 user staff 89012 1 Jan 00:00 play-app-signing-public-key.pem # 检查 Java 版本兼容性(pepk 要求 Java 8–17,推荐 OpenJDK 11) java -version # 输出应类似:openjdk version "11.0.18" 2023-01-17注意:
play-app-signing-public-key.pem是 Google 的根公钥,用于验证委托文件签名。该文件随 pepk 工具包一同分发,切勿自行替换或从第三方获取。若文件缺失或损坏,重新通过 SDK Manager 安装即可。
2.3 准备密钥材料:三类文件缺一不可的生成逻辑
pepk 运行依赖三个明确角色的密钥文件,缺一不可,且格式、用途严格限定:
| 文件类型 | 生成方式 | 用途 | 格式要求 | 常见错误 |
|---|---|---|---|---|
| 新上传密钥(new_upload_key) | keytool -genkeypair -keystore new_upload.jks -alias upload -keyalg RSA -keysize 2048 -validity 10000 | 你将用于后续所有版本上传的密钥 | .jks(推荐)或 PKCS#8.pem(需-rfc参数) | 用-keyalg EC生成 ECDSA 密钥时,必须搭配 pepk v2.0.0+,否则报Unsupported key algorithm |
| 旧上传证书(old_upload_cert) | keytool -exportcert -keystore old_upload.jks -alias upload -file old_upload.der | 证明你曾是该应用的合法上传者 | .der(二进制)或 Base64 PEM(.pem) | 导出时未加-rfc,导致生成二进制 DER,但脚本误当 PEM 处理,解密失败 |
| Google 公钥(play-app-signing-public-key.pem) | SDK Manager 自动提供 | 验证委托文件合法性 | PEM 格式,固定内容 | 从非官方渠道下载,导致公钥不匹配,Play 后端拒绝解密 |
生成示例(以 JKS 为例):
# 生成新上传密钥(强密码保护,记牢!) keytool -genkeypair \ -keystore new_upload.jks \ -storepass "MyStrongPass123!" \ -alias upload \ -keypass "MyStrongPass123!" \ -keyalg RSA \ -keysize 2048 \ -validity 10000 \ -dname "CN=Upload Key, OU=Dev, O=MyApp, L=Beijing, ST=BJ, C=CN" # 导出旧上传证书(假设旧密钥为 old_upload.jks) keytool -exportcert \ -keystore old_upload.jks \ -storepass "OldPass456!" \ -alias upload \ -file old_upload.der # 注意:此处生成 .der,非 .pem关键逻辑说明:
old_upload.der是旧密钥的证书(含公钥),不含私钥,因此可安全传输。pepk 用它来比对旧密钥指纹,确认你有权发起更换。new_upload.jks是新私钥,全程不离开你的机器,pepk 仅读取其公钥部分用于构造委托。
3. 用 pepk 在本地跑通最小委托命令:参数含义与执行验证
3.1 最小可行命令:一行跑通,但必须理解每个开关
以下命令是生成有效委托文件的最小完整形式,适用于绝大多数 RSA 密钥场景:
java -jar pepk.jar \ --keystore=new_upload.jks \ --alias=upload \ --output=encrypted_private_key.pem \ --encryption-key-path=play-app-signing-public-key.pem \ --signing-cert=old_upload.der逐参数解析与血泪经验:
--keystore=new_upload.jks:指定新上传密钥文件路径。必须是 JKS 或 PKCS#12(.p12)格式;PEM 私钥需额外加--rsa-key-path,但 JKS 更稳定。--alias=upload:密钥别名,必须与keytool -list -v -keystore new_upload.jks中显示的 alias 一致。大小写敏感,空格会报错。--output=encrypted_private_key.pem:输出委托文件名。必须以.pem结尾,Play Console 只认此扩展名。文件内容为 Base64 编码,可直接文本查看。--encryption-key-path=play-app-signing-public-key.pem:Google 公钥路径。SDK Manager 安装后自带,绝对路径更稳妥(如/Users/me/Library/Android/sdk/extras/google/play-pem/play-app-signing-public-key.pem)。--signing-cert=old_upload.der:旧上传证书路径。必须是 DER 格式(二进制);若你只有 PEM 格式旧证书,需先转换:openssl x509 -in old_upload.pem -outform der -out old_upload.der。
提示:命令执行成功后,终端无任何输出(静默模式),但会生成
encrypted_private_key.pem。这是正常行为,不代表失败。立即用cat encrypted_private_key.pem | head -n 5查看前几行,确认以-----BEGIN ENCRYPTED PRIVATE KEY-----开头。
3.2 验证输出文件有效性:三步快速自检
生成.pem后,不要直接上传。先做本地验证,避免因格式错误被 Play Console 拒绝:
第一步:检查文件头尾是否符合 PEM 封装规范
head -n 1 encrypted_private_key.pem # 应输出:-----BEGIN ENCRYPTED PRIVATE KEY----- tail -n 1 encrypted_private_key.pem # 应输出:-----END ENCRYPTED PRIVATE KEY-----第二步:Base64 解码后检查长度(防截断)
# 提取 Base64 内容(去掉头尾行) sed '1d;$d' encrypted_private_key.pem | tr -d '\n' | wc -c # 正常 RSA-2048 密钥委托文件解码后长度应在 1200–1800 字节之间。若 < 1000,大概率生成失败。第三步:用 OpenSSL 模拟解密(需 Google 私钥,不可行)→ 改用间接法由于无法获取 Google 私钥,我们转而验证委托文件的 ASN.1 结构是否合法:
# 安装 asn1parse(macOS 用 brew install openssl@3;Linux 用 apt install openssl) openssl asn1parse -in encrypted_private_key.pem -i # 正常应输出多层嵌套结构,顶层为 SEQUENCE,包含 OBJECT IDENTIFIER (1.2.840.113549.1.1.1) 和 OCTET STRING。 # 若报错 `Error in encoding` 或 `nested error`, 说明 pepk 生成异常,需检查输入密钥格式。血泪经验:某开发者因
old_upload.der实际是 PEM 格式(但扩展名误写为 .der),导致 pepk 读取时解析失败,生成的.pem文件头正确但内容为空。自检第二步长度仅 23 字节,立刻暴露问题。
4. pepk 使用避坑指南:5 条真实翻车记录与当场修复方案
4.1 现象:执行 pepk 命令后报错java.lang.UnsupportedClassVersionError: pepk has been compiled by a more recent version of the Java Runtime
- 原因:pepk.jar 编译目标为 Java 11(字节码版本 55.0),但本地
java -version显示 Java 8(52.0)或 Java 17(61.0)。Java 版本不兼容。 - 解决:统一使用 OpenJDK 11。macOS 用户:
brew install openjdk@11 && sudo ln -sfn /opt/homebrew/opt/openjdk@11/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-11.jdk;Windows 用户:从 Adoptium 下载 JDK 11 MSI 安装包,设JAVA_HOME指向新路径。
4.2 现象:上传encrypted_private_key.pem到 Play Console 后,提示Invalid key file format
- 原因:
--signing-cert参数指向了 PEM 格式证书(如old_upload.pem),而 pepk 要求 DER 格式。PEM 文件被当作二进制读取,导致 ASN.1 解析失败。 - 解决:用 OpenSSL 转换:
openssl x509 -in old_upload.pem -outform der -out old_upload.der,然后在 pepk 命令中改用--signing-cert=old_upload.der。
4.3 现象:生成的.pem文件可上传,但 Play Console 卡在“正在处理”超过 2 小时,最终失败
- 原因:新上传密钥(
new_upload.jks)的密钥算法为 ECDSA(-keyalg EC),但使用的 pepk 版本过低(< v2.0.0)。旧版 pepk 仅支持 RSA。 - 解决:确认 pepk.jar 版本。进入 SDK Manager → 卸载 "Google Play Licensing" → 重启 Android Studio → 重新勾选安装。新版工具包内
pepk.jar时间戳应为 2022 年 10 月之后。若仍无效,改用 RSA 密钥重试。
4.4 现象:keytool -list -v -keystore new_upload.jks显示别名正确,但 pepk 报错Keystore was tampered with, or password was incorrect
- 原因:
--keystore密码与--storepass未显式传入。pepk 默认不提示输入密码,而是直接失败。 - 解决:必须显式添加
--storepass和--keypass参数(即使两者相同):java -jar pepk.jar \ --keystore=new_upload.jks \ --storepass="MyStrongPass123!" \ --keypass="MyStrongPass123!" \ --alias=upload \ --output=encrypted_private_key.pem \ --encryption-key-path=play-app-signing-public-key.pem \ --signing-cert=old_upload.der
4.5 现象:Play Console 接收委托文件后,显示“密钥更换已启动”,但后续上传新 APK 仍报upload certificate mismatch
- 原因:密钥更换是异步流程,需等待 Google 后端完成密钥链绑定。此过程通常需 24–72 小时,期间旧上传密钥仍有效,新密钥尚未激活。
- 解决:耐心等待。登录 Play Console → 应用 → 设置 → 应用完整性 → 应用签名,查看状态栏。只有状态变为 “Your app is signed by Google” 且下方显示 “Upload key” 已更新为新密钥指纹时,才可使用新密钥上传。此时可用
keytool -printcert -jarfile app-release.aab | grep "SHA256"验证 APK/AAB 签名指纹是否匹配新密钥。
5. 进阶技巧:自动化密钥轮换流水线与委托文件安全归档
5.1 将 pepk 集成进 CI/CD:用 Bash 脚本封装全流程
在团队协作中,手动执行 pepk 易出错且不可审计。我一般会将密钥轮换封装为幂等脚本,纳入 GitOps 流程。核心思路:所有密钥材料存于加密 Vault,脚本拉取后本地生成委托文件,上传至 Play Console 前自动校验。以下是精简版 CI 脚本骨架(适配 GitHub Actions):
#!/bin/bash # pepk-rotate.sh:密钥轮换主脚本 set -e # 任一命令失败即退出 # 1. 从 Secrets 注入密钥(生产环境严禁明文) echo "${NEW_UPLOAD_JKS_BASE64}" | base64 -d > new_upload.jks echo "${OLD_UPLOAD_DER_BASE64}" | base64 -d > old_upload.der # 2. 校验密钥完整性 keytool -list -v -keystore new_upload.jks -storepass "${NEW_STORE_PASS}" | grep "Certificate fingerprint" > /dev/null openssl asn1parse -in old_upload.der -i > /dev/null # 3. 执行 pepk(显式指定 Java 11) JAVA_HOME=/opt/java/openjdk11 \ java -jar $ANDROID_HOME/extras/google/play-pem/pepk.jar \ --keystore=new_upload.jks \ --storepass="${NEW_STORE_PASS}" \ --keypass="${NEW_KEY_PASS}" \ --alias=upload \ --output=encrypted_private_key.pem \ --encryption-key-path=$ANDROID_HOME/extras/google/play-pem/play-app-signing-public-key.pem \ --signing-cert=old_upload.der # 4. 上传前二次校验 if [ $(wc -c < encrypted_private_key.pem | awk '{print $1}') -lt 1000 ]; then echo "ERROR: Generated pem too small. Aborting." exit 1 fi # 5. 安全归档:生成带哈希与时间戳的归档包 tar -czf "pepk-$(date +%Y%m%d-%H%M%S).tar.gz" \ new_upload.jks \ old_upload.der \ encrypted_private_key.pem \ play-app-signing-public-key.pem sha256sum "pepk-$(date +%Y%m%d-%H%M%S).tar.gz" > "pepk-$(date +%Y%m%d-%H%M%S).sha256"关键设计点:
set -e确保任一环节失败立即终止,避免脏数据残留;- 所有密钥通过
base64注入,规避 shell history 泄露;- 归档包包含全部输入输出文件及校验哈希,满足审计要求;
- 时间戳命名防止覆盖,便于回溯。
5.2 委托文件的安全归档:为什么不能只存.pem?
很多团队只保存生成的encrypted_private_key.pem,认为它是“最终产物”。这是重大隐患。委托文件本身不包含密钥指纹、时间戳、旧证书哈希等上下文,一旦 Play Console 状态异常,无法快速定位是哪次轮换出了问题。我坚持归档四件套:
| 文件 | 作用 | 保留期限 | 存储建议 |
|---|---|---|---|
new_upload.jks(加密) | 新上传密钥源文件 | 永久 | 企业 Vault,AES-256 加密 |
old_upload.der | 旧证书原始证据 | 永久 | 同上,与新密钥分开存储 |
encrypted_private_key.pem | 委托凭证 | 永久 | 同上,但需标记“已上传至 Play Console”状态 |
pepk-run.log | 完整执行日志(含命令、时间、exit code) | 3 年 | 日志系统,按应用 ID 索引 |
归档时,我会用sha256sum对四件套生成总哈希,并将哈希值写入 Confluence 文档,文档权限设为“仅密钥管理员组可读”。这样,当某天需要复核“2024Q2 的密钥轮换是否合规”,只需查文档哈希,再比对 Vault 中对应归档包哈希,5 秒内完成验证。
5.3 一次失败的教训:把 pepk 当作“重签名工具”的代价
去年某项目,A同学误以为 pepk 可以绕过 Google Play 的签名限制,试图用它“重签”一个未启用 ASBGP 的老应用。他生成了委托文件,却在 Play Console 的“应用签名”页面找不到“更换上传密钥”入口——因为该应用从未开启 App Signing by Google Play。他反复尝试,浪费 3 天,最后才发现:pepk 仅适用于已启用 ASBGP 的应用。对于未启用的应用,必须先在 Play Console 中手动开启(会生成 Google 托管的签名密钥),再进行密钥轮换。
这个翻车让我养成了一个硬习惯:每次执行 pepk 前,必开 Play Console,截图保存“应用签名”页面状态栏。如果状态是 “You are managing your own app signing key”,立刻停手,先去开启 ASBGP。省下的不是 3 天,而是团队对流程可靠性的信任。
希望帮到你。
本文还有配套的精品资源,点击获取