发版当天才发现构建签名不可用,是一种代价很高的工程失误。产品可能已经完成回归、测试已冻结版本,最终却因为证书不属于当前应用、Profile 与包名不一致,或签名材料只留在某位开发者电脑上,导致候选发布包无法形成。签名问题难以靠多点几次“重新构建”解决,关键是把材料关系、人员权限和构建环境整理清楚。

上一篇已经为设备点检助手固定 com.example.fieldinspector 这一演示包名,以及递增的 versionCode 规则。本篇不再重复版本字段,而是继续处理发布链路中的身份凭据:.p12、.cer、.p7b 到底各做什么,自动签名与手动签名如何选择,以及团队如何在不共享私钥的情况下协作。这里讨论的是工程控制策略,具体证书申请、签发和审核仍应以相应账号在开发者中心可用的流程为准。

发布签名工程技术封面

签名不是一个文件,而是一组相互约束的材料

对应用包进行签名,至少牵涉密钥、证书、Profile 和构建产品四个维度。.p12 属于密钥库材料,关联签名密钥及其访问凭据;.cer 记录公钥证书信息;.p7b 是与应用、授权或设备等信息有关的 Profile;工程中的 build-profile.json5 则负责引用指定的签名配置。理解这些角色,比背文件扩展名更重要。

例如,开发签名可以面向已经登记的调试设备,而发布签名面向分发流程;同样一个工程目录里的配置名称为 default,不代表其中的材料就适用于所有构建目标。实际使用时,开发者账号、应用 bundleName、证书和 Profile 的授权信息必须与目标操作匹配。某个文件存在于磁盘,只能说明路径可访问,不能推导它未过期、授权完整,或者证书仍受认可。

从协作角度看,最敏感的通常不是版本号,而是私钥和密码。开发分支可以公开一个签名配置的名称,但不应把密钥库、口令或生产 Profile 直接提交到代码仓库。即使仓库暂时是私有的,持续集成日志、缓存和离职人员的历史访问也会增加泄露面。

签名材料的职责与构建链路

自动签名与手动签名:先确定使用阶段

DevEco Studio 的自动签名适合降低开发调试时的操作负担。当前官方流程区分“关联注册应用”和“未关联注册应用”两种方式;在 File > Project Structure > Project > Signing Configs 中处理,关联方式还需要核对团队与应用市场里已有的应用身份。自动生成并不意味着可以跳过权限审批。涉及受限 ACL 权限时,需要按当前开放范围和申请状态确认 Profile 的适用条件。官方文档还特别要求本地系统时间与北京时间校准,否则可能引发签名时效校验问题。

手动签名的价值不是“更高级”,而是交付控制权更明确:指定具备授权的人员管理证书及 Profile,明确它们关联的应用和签发主体,并把构建环境的密钥注入过程交由受控机制执行。调试阶段如使用手动签名,官方要求配置调试 Profile,并满足应用创建、调试证书和调试设备等前提。这也说明调试 Profile 与发布所需的身份材料不能被想当然地互换。

选择方式时,我倾向于把“人员在本机调试”与“版本候选包交付”划为两条受控路径。前者可以利用 IDE 管理的调试签名提高效率;后者则必须经过凭据授权、构建目标确认以及产物复核。即便正式项目使用官方提供的自动化能力,也应保留谁申请、谁批准、关联了哪个应用、何时需要轮换材料的记录。不要将自动化理解为无人负责。

给构建配置建立可审阅的关联关系

工程级 build-profile.json5 中,app.signingConfigs 定义具名签名配置;产品项中的 signingConfig 应引用相应名称。官方签名故障文档明确指出,二者不一致可能导致配置不生效。下面展示可以公开讨论的关键关系,其中签名材料由有权限的构建环境配置,故不在示例里写入口令或私钥路径。此结构需与实际 DevEco Studio 及 SDK 配置核对后使用。

{
  "app": {
    "signingConfigs": [
      {
        "name": "release",
        "type": "HarmonyOS"
      }
    ],
    "products": [
      {
        "name": "default",
        "signingConfig": "release",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}

这只是用来说明名称绑定的配置片段,不是完整可签名的材料配置。真实工程中的 material 还需要匹配证书路径、密钥库、Profile、签名算法和有效的凭据,具体值应由受控签名环境提供。官方公开的构建配置示例里使用了 certpath、storeFile、profile、keyAlias、signAlg、storePassword 和 keyPassword 等字段;其中密码尤其不能放入通用开发脚本。

如果需要在团队内讨论某个发布配置,应该传递配置名、产品名、证书指纹或授权范围等不包含密钥的元信息,而不是为了让他人复现而复制整套生产材料。前端工程师可以复核产品映射,发布管理员负责密钥访问,两者并不矛盾。

用独立的发布门禁脚本提前发现配置漂移

签名工具才能真正判断密码、证书链、Profile 和产物是否有效;但在真正调用工具之前,依然可以使用普通 TypeScript 对发布意图做一轮静态核对,抓出明显的分支误配。下面的代码是可用于已有构建脚本中的纯数据校验函数,输入来自配置解析后的对象,而不是直接把 JSON5 当 JSON 处理。它只判断本团队定义的关联规则,不会读取密钥文件,更不能替代签名工具。

interface ProductConfig {
  name: string;
  signingConfig?: string;
}
interface SigningConfigMeta {
  name: string;
  type: string;
}
interface ReleaseMeta {
  bundleName: string;
  products: ProductConfig[];
  signingConfigs: SigningConfigMeta[];
}

function validateRelease(meta: ReleaseMeta): string[] {
  const problems: string[] = [];
  if (meta.bundleName !== 'com.example.fieldinspector') {
    problems.push('发布应用身份与登记包名不一致');
  }
  const names: Set<string> = new Set<string>();
  for (const item of meta.signingConfigs) {
    if (names.has(item.name)) problems.push('签名配置名称重复');
    names.add(item.name);
    if (item.type !== 'HarmonyOS') problems.push('签名类型需要复核');
  }
  for (const product of meta.products) {
    if (!product.signingConfig) {
      problems.push(`产品 ${product.name} 未指定签名配置`);
    } else if (!names.has(product.signingConfig)) {
      problems.push(`产品 ${product.name} 引用了不存在的签名配置`);
    }
  }
  return problems;
}

const candidate: ReleaseMeta = {
  bundleName: 'com.example.fieldinspector',
  products: [{ name: 'default', signingConfig: 'release' }],
  signingConfigs: [{ name: 'release', type: 'HarmonyOS' }]
};
const problems: string[] = validateRelease(candidate);
if (problems.length > 0) throw new Error(problems.join(';'));

实际部署时,应从流水线解析出的应用身份、产品配置和签名配置建立 ReleaseMeta,并明确哪些产品属于调试、哪些属于候选发布。运行这个函数返回空数组,只意味着静态映射符合约定;绝不能把它作为签名成功、证书有效或商店审核通过的证明。后续步骤还包括验证产物签名、版本及授权条件,并保留不含敏感数据的审计结果。

签名关联检查与常见误配标注

团队交付时,重点检查四类容易漏掉的边界

第一类是人和环境。开发者更换电脑时,如果发布能力只存在于他个人的密钥库,整个团队就可能卡在同一处。生产材料应存于受控的凭据服务或管理员指定的位置,由授权人员和受控流水线使用;公共代码仓库只保存与秘密无关的声明。定期核查访问者名单,能比事故发生后追查下载记录更省事。

第二类是证书和 Profile 的时效。签名失败时不能一律归咎于构建缓存。先看系统时间、证书有效期、Profile 是否针对正确的包名和授权能力,再核对构建产品是否引用正确的签名配置。自动签名发生问题时,官方故障文档还提供了应用绑定、时间和材料失效等排查方向。每一次重新生成材料都应与具体原因关联,而不是不受控制地反复申请。

第三类是调试与发布隔离。调试证书不应顺手进入发布仓库,发布用的口令也不应为方便本地运行而复制给整个研发团队。建议为两类构建建立不同的权限边界,并记录出包的工程提交、构建模式、产品、版本号和签名配置名称。出现问题时,可以在不公开密码的前提下精确复盘候选包来自哪次构建。

第四类是能力和权限声明。有些受限能力是否能进入发布包,取决于申请审批及 Profile 中的授权内容。把权限名写进 module.json5 并不能绕过授权要求。项目第一次申请新能力时,应在评审时确认 API 开放范围和审核材料,而不要等上线前夕才发现候选包的签名权限不足。

为便于自查,可以准备三个不用接触密钥的反向用例:将产品引用改成不存在的 release2,应被静态校验发现;改动演示包名,应触发身份提醒;将签名配置列表设为空,则应提示缺少映射。它们验证的是前置规则,不会制造任何真实证书。随后让负责签名的人使用官方签名工具检查真正的候选产物,才算完成交付链路的下一道关口。

签名工程的成熟度,不是看团队能保存多少份 .p12,而是看它能不能在不暴露敏感材料的情况下稳定产出可追溯的候选包。前一篇建立了应用身份和版本基线,这一篇建立签名材料和协作边界;下一篇将讨论 Debug、Release 与 APP 构建产物之间的关系,确保选对模式、选对产品、选对实际交付文件。

官方参考:自动签名、手动签名、申请调试 Profile、签名服务常见问题。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐