首页/文章/flutter

Flutter Android 正式签名实战:生成密钥、配置构建与验证 APK

2026-10-14
2984 字约 10 分钟
...

Flutter Android 正式签名实战:生成密钥、配置构建与验证 APK

Android 正式签名不需要购买证书。开发者可以自己生成密钥,用它给 APK 签名;密钥的用途是建立稳定的应用身份。签名有效并不等于应用已经通过商店审核,也不等于应用功能已完成真机验收。

本文面向 Flutter Android 项目,介绍从开发签名切换到独立正式签名的完整流程。应用名称、目录、包名和证书信息均使用通用示例,不包含真实项目密码、私钥、签名指纹或账号数据。

一、先理解三个不同概念

概念作用
应用标识 applicationIdAndroid 用来区分安装在设备上的应用
签名私钥与证书私钥用于签名,证书用于验证签名身份
证书指纹 SHA-256 / SHA-1证书摘要,常用于核对身份或配置第三方服务

证书指纹不是签名密码,也不能用来还原私钥。可以分享公开证书,但不能把 keystore、私钥或密码放到公开文章中。签名密钥与证书

同一包名和兼容签名是覆盖升级的基本条件。仅仅换了签名,不会自动多出一个应用;包名相同但证书不兼容时,通常会安装失败。包名不同则可以同时安装,即使名称和图标完全相同。应用标识规则

二、生成一套长期使用的正式密钥

如果项目已有正式签名,先恢复并沿用已有密钥,不要重新生成。下面只适用于首次创建。

keytool 随 JDK 提供。以下示例在 macOS / Linux 上把签名材料放到代码仓库外:

APP_SIGNING_DIR="$HOME/.config/example-app/signing"
mkdir -p "$APP_SIGNING_DIR"
chmod 700 "$APP_SIGNING_DIR"

keytool -genkeypair \
  -keystore "$APP_SIGNING_DIR/app-release.jks" \
  -storetype JKS \
  -alias release \
  -keyalg RSA \
  -keysize 3072 \
  -sigalg SHA256withRSA \
  -validity 10000 \
  -dname "CN=Example App Release"

chmod 600 "$APP_SIGNING_DIR/app-release.jks"

按交互提示设置强密码,不把真实密码写进命令参数或复制到文章中。这里的证书名称是示例;实际名称不需要伪造个人或机构身份。10000 表示有效期天数,约 27 年;RSA 3072 是此示例的算法选择,不是 Android 唯一允许的配置。keytool 参数说明

生成后保存四项信息:keystore 文件、keystore 密码、key alias,以及私钥密码。可以在工具支持的前提下使用同一个强密码,但仍要了解两项密码的用途。后续版本继续使用这套密钥,而不是每次打包都生成一份新的。

三、让 Flutter 的 release 构建使用正式签名

1. 创建本地签名配置

在签名材料目录中创建 key.properties,把占位内容替换成实际值。storeFile 必须是实际绝对路径,不要照抄示例路径:

storePassword=YOUR_STORE_PASSWORD
keyPassword=YOUR_KEY_PASSWORD
keyAlias=release
storeFile=/absolute/path/to/app-release.jks

Java Properties 对反斜杠等字符有转义规则;Windows 路径尤其要注意。不要给实际密码随意加引号,否则引号可能成为密码的一部分。Properties 文件格式

macOS / Linux 可以通过本地链接把配置提供给项目,在项目根目录执行:

chmod 600 "$APP_SIGNING_DIR/key.properties"
ln -s "$APP_SIGNING_DIR/key.properties" android/key.properties

如果 android/key.properties 已存在,先核对它,不要直接覆盖。也可以使用普通本地文件,前提是忽略 Git 并保护访问权限。权限 600 和 700 限制同机其他用户访问,但不能代替加密备份。

在 android/.gitignore 中加入:

key.properties
**/*.jks
**/*.keystore

团队需要共享配置结构时,只提交一个包含占位值的 key.properties.example。如果秘密以前已经被 Git 跟踪,补一条忽略规则并不能消除历史泄露。

2. 修改 Kotlin Gradle 配置

在 android/app/build.gradle.kts 顶部增加导入:

import java.util.Properties

在现有 plugins 块之后、android 块之前加载配置:

val releaseValues = Properties()
val releaseValuesFile = rootProject.file("key.properties")
if (releaseValuesFile.exists()) {
    releaseValuesFile.inputStream().use { stream ->
        releaseValues.load(stream)
    }
}

在现有 android 块内添加 release 签名配置,并在已有 buildTypes.release 中引用它:

signingConfigs {
    create("release") {
        keyAlias = releaseValues.getProperty("keyAlias")
        keyPassword = releaseValues.getProperty("keyPassword")
        storePassword = releaseValues.getProperty("storePassword")
        storeFile = releaseValues.getProperty("storeFile")?.let { path ->
            rootProject.file(path)
        }
    }
}

buildTypes {
    release {
        signingConfig = signingConfigs.getByName("release")
    }
}

将这些内容合并到已有配置,保留项目自己的其他设置。若原来 release 使用 signingConfigs.getByName("debug"),应替换该设置。release 必须引用正式配置;缺少私密配置时,应让 release 签名验证失败,而不是默默退回开发签名。debug 构建可以继续使用工具默认的开发签名。

Flutter 官方也采用“本地 Properties 文件 + Gradle signingConfigs”的配置方式。Flutter Android 签名配置

还要防止缺少配置时 Gradle 生成未签名产物。对于没有 flavor 的示例项目,在文件末尾加上 release 构建前的检查:

tasks.configureEach {
    if (name == "preReleaseBuild") {
        doFirst {
            check(releaseValuesFile.isFile) {
                "Release signing requires android/key.properties."
            }
        }
    }
}

有 flavor 的项目应对相应的 release variant 设置检查,并实际验证缺少密钥时构建失败。配置存在但路径、密码或别名错误时,后续签名验证仍应报错。

四、构建正式包,并检查实际签名

先执行项目规定的分析、测试和检查脚本。普通 Flutter 项目的 APK 构建命令为:

flutter build apk --release

如果项目接入了 Shorebird,并希望安装包继续支持热更新,应使用 Shorebird 构建基础版本,例如:

shorebird release android --flutter-version 3.47.7 --artifact apk

示例 SDK 版本应替换为项目实际固定且 Shorebird 支持的版本。新的基础版本要设置合适的新版本号和递增的构建号;同一平台、同一版本不要重复注册。Shorebird 注册成功后,还需要自行安装、分发或提交商店。Shorebird 基础包发布

找到 CLI 输出的 APK 路径,再使用 Android SDK Build Tools 中的 apksigner:

apksigner verify --verbose --print-certs /path/to/app-release.apk

检查签名验证通过、证书名称符合预期、证书 SHA-256 指纹与准备使用的 release 证书一致。开发证书通常带有 Android Debug 名称,不能仅因为文件叫 app-release.apk 就认定它使用了正式签名。

只检查 Gradle 文件不足以证明签名成功,必须检查最终 APK。签名后再修改 APK 内容会破坏签名,因此不要手工改包后直接分发。apksigner 验证说明

还应核对版本号、包名、支持的 CPU 架构和包完整性。仅 arm64 的 APK 不代表所有 Android 设备均可安装;正式签名通过也不代表真机启动、数据保留和网络恢复已经验收。

五、从开发签名切换时怎么处理旧安装

一个常见情况是:手机已有开发签名的测试包,新生成的正式包保持同一包名,却使用另一张证书。普通覆盖安装会因签名不兼容失败。增加版本号、换 APK 文件名,都不能绕过签名校验。

在没有配置兼容签名轮换的情况下,应先确认旧数据如何导出、备份和恢复,再安排旧测试包到正式包的迁移。卸载会影响应用私有数据,不应为了完成演示自动卸载或清空用户数据。改包名可以同时安装,但新应用会有自己的数据空间,原来的本地数据不会自动迁移。

正式应用发布后,应稳定保管其签名。Android 有受条件限制的签名轮换和 Google Play 密钥升级机制,但不能把“换个密钥重新打包”当成普通升级流程。签名管理与升级

六、备份、商店与热更新的边界

keystore 已加密码,不意味着密码文件也已经加密。将密钥、密码和恢复说明存入受控的加密备份,至少准备一份独立于当前开发机器的备份,并验证能恢复签名身份。只备份 APK、公开证书或指纹,无法恢复私钥。

直接分发 APK 时,开发者自己持有并使用应用签名密钥。接入 Google Play App Signing 时,还要区分上传密钥与最终应用签名密钥;Google Play 可以用托管的应用签名密钥为用户安装包签名。配置第三方登录等服务时,要核对最终安装包对应的证书,而不是只看上传证书。Google Play App Signing

Android APK 签名和 Shorebird 补丁签名是两个机制。给 APK 配置正式签名,不会自动启用 Shorebird 的补丁私钥签名;接入热更新也不会自动替代 Android 安装包签名。Shorebird 补丁签名

可以记录这些非秘密验收信息:release 构建结果、签名算法、证书指纹、APK 摘要、基础版本、架构以及测试设备结果。公开文章使用占位值,删除真实包名、个人目录、账号、组织 ID 和生产地址,并检查截图是否泄露密钥路径或密码。

文档核对日期:2026-10-14。本文范围是 Android APK 签名;iOS 证书与分发方式需另行配置。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
flutter2026-10-14
Flutter 新版本发布实战:完整安装包与 Shorebird 热更新
一、先判断该发完整包还是补丁 | 这次改动 | 发布方式 | | | | | Dart 文案、Flutter 页面布局、业务逻辑修复 | 通常可以发布补丁 | | 纯 Dart 依赖变化 | 可以尝试补丁构建,仍需验证兼容性 | | Kotlin、Java、Swift、Objective C 或原生构建配置变化 | 发布完整包 | | 插件的原生实现变化、新增原生插件、权限变化 | 发布完整包 | | 新增或修改打包图片、字体、应用图标...
学习
flutter2026-07-01
Dart 入门回顾
1\. Dart 是什么 Dart 是一门类型安全、支持面向对象、支持异步编程的语言。它的语法和 JavaScript、TypeScript、Java、C# 都有一些相似之处。 Dart 常见使用场景: Flutter 跨平台应用开发 命令行工具 Web 应用 服务端接口或脚本 作为学习现代类型语言的入口 Dart 的几个核心特点: 静态类型:变量、函数返回值、参数都有类型。 类型推断:很多时候可以用 ,让 Dart 自动推断类型。 空...
学习
flutter2026-07-01
Flutter 常用 Widget 入门
Flutter 的核心思想是:一切皆 Widget。文字、图片、按钮、页面结构、布局、间距、滚动、动画、手势,几乎都由 Widget 组合出来。 1\. Widget 先分成几类理解 | 类别 | 常用 Widget | 解决什么问题 | | | | | | 应用入口 | 、 | 搭建应用外壳、主题、路由 | | 页面骨架 | 、 、 | 标准页面结构 | | 基础显示 | 、 、 | 显示文字、图标、图片 | | 布局容器 | 、 、...
学习
flutter2024-11-21
在 Flutter 中使用 webview_flutter 4.0
1. 2. ,已入选 3. 4. 本文是第 4 篇,定位是新手入门,介绍和演示 webview 的基础用法。最后还介绍了事件处理的技巧。因为是补写的,所以本来应该首发的却发在最后了。 环境准备已经在 说过了,不再赘述。既然是新手入门,就会本着详尽的原则,而且会多配示例。 获取页面信息 获取页面 title 核心方法:controller.getTitle 完整示例,放在 main.dart 就能运行。运行示例,点击获取 title 的按...
学习
flutter2024-11-06
flutter android端运行报错Namespace not specified. Specify a namespace in the module‘s build file
在使用flutterSDK 3.24之后发现新建项目,项目默认都是基于AGP8了,由于 AGP 开始强制要求 namespace 配置,不仅仅是主工程,也包括第三方库都强制要求 namespace 。但是我们在引入第三方库的时候会发现部分库没能及时的去做相关配置,这也就导致了我们会出现标题错误: 出现这个问题,我们可能会通过去找到本地缓存的第三方库代码去修改它的build.gradle和AndroidManifest.xml来解决缺失...
学习
flutter2024-11-03
Flutter中如何实现APP升级功能背景
背景 每个App应用中一定会包含的功能 ,App的运行平台不同实现升级功能的方式也就不同,基本分类如下所示。本文主要聚焦在使用Flutter框架开发的应用在安卓系统内如何更新,以及所涉及要点梳理。 IOS系统 Andriod系统 官网下载 各类应用市场 (本文聚焦点) 功能清单(按步骤) UpgradeCard组件(展示升级内容) 进行新旧版本对比判断是否需要更新 下载更新然后安装应用 UpgradeCard组件 组件需要包含基本标题、...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录