尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Unity安卓打包报错Unable to sign深度解析:签名配置与CI排查指南

Unity安卓打包报错Unable to sign深度解析:签名配置与CI排查指南 深夜十二点CI流水线又亮了红灯。点开构建日志最后一行赫然写着Unable to sign the application; please provide passwords!。群里安静得可怕老安卓打包的Unity工程师看到这句话基本都能脑补出接下来的流程翻上古文档、试各种密码、怀疑人生、最后发现是某个最不起眼的配置丢了。这个报错在整个Unity打包链路里属于卡在最后一步的典型——资源编译、代码合并、Dex打包全过了结果在给APK/AAB签名时签名工具拿不到该用的密码只能原地罢工。对于刚接触Unity安卓打包的开发者来说这行日志往往是最让人摸不着头脑的报错之一而对我来说它已经成了判断项目签名配置是否完好的警报器。这篇文章就围绕这个报错把从编辑器打包到命令行打包、从个人开发到团队CI可能踩的坑完整拆一遍。1. 这个报错到底发生在哪一步先搞懂Android签名链路要真正解决这个报错不能只盯着填密码这几个字。你得先明白Unity在打Android包时签名这个动作发生在哪个环节以及为什么它非要在这一步找你要密码。1.1 为什么Unity打包需要密码Android应用在发布时必须经过数字签名这是系统的安全机制。签名相当于给APK/AAB打上一个独特的身份标记证明这个包确实由你发布也防止别人篡改内容后还能以你的名义安装。这个签名不是Unity自动完成的而是依赖JDK里的jarsigner或apksigner工具读取你的密钥库keystore文件来完成。当你使用自定义keystore时这个文件本身有密码保护文件里的每个别名key alias也有独立的密码。Unity负责把密码传给签名工具但Unity不会替你记住密码也不会凭空猜出密码。所以一旦在需要签名时Unity发现勾选了自定义keystore、却拿不到对应的密码它就会干脆利落地中断构建抛出Unable to sign the application; please provide passwords!。1.2 默认debug签名与自定义签名的区别这里有一个很重要的概念区分。Unity在Android平台下如果没有勾选自定义keystore会默认使用一个debug签名文件。这个debug.keystore是由Unity或Android SDK自动生成的密码固定且公开专门用于开发调试阶段。它和自定义签名release签名的区别我常用下面这个表来说明对比项Debug签名自定义签名Release签名文件来源Unity/Android SDK自动生成开发者用keytool手动生成密码固定为android公开自己设定必须保密有效期有效期通常较长但仅限调试一般按发布需求设置建议20年以上使用场景本地调试、测试包应用市场发布、正式包是否可覆盖同应用升级只能覆盖同样用debug签名的包必须与你app发布签名的包一致很多人第一次遇到签名报错就是因为在Player Settings里勾选了自定义keystore但密码没填、或者填错了。这时Unity不会退回去自动用debug签名因为它遵循你的明确指令用这个keystore。所以报错就出现了。1.3 报错出现的位置编辑器构建和命令行构建这个报错有两个高发场景一个是在Unity编辑器里点Build另一个是在命令行/CI环境里调用打包脚本。编辑器里报错通常是因为Player Settings面板里的密码字段为空或错误这个还比较好排查命令行环境报错就复杂多了它可能来自构建脚本没读取环境变量、或者脚本里压根没写上签名相关代码、又或者CI机器上的环境变量没配置到位。我见过不止一次本地编辑器打包完美通过一上CI就报这个错最后查下来的原因都绕不开命令行打包的签名信息来源和编辑器不一样这个我会在第三章专门展开。2. 最常见的元凶Publishing Settings里的签名配置没写全如果你是在Unity编辑器里点击Build遇到这个报错那90%以上的问题出在Edit Project Settings Player Publishing SettingsAndroid平台这个面板里。这个面板负责管理安卓包的所有发布配置签名只是其中一块。2.1 四个字段逐项排查在Publishing Settings下找到Project Signing区域这里至少有四个需要关注的字段Project Keystorekeystore文件路径。如果勾选了Custom Keystore必须指定一个真实存在的.keystore或.jks文件。Keystore Password整个keystore文件的主密码store password。它保护的是整个密钥库文件。Key Alias密钥别名。一个keystore文件里可以存放多个密钥每个密钥都有一个别名签名时必须指定使用哪一个。Key Password这个别名对应的密码key password。它保护的是具体的密钥可以和Keystore Password相同也可以不同。问题往往出在后三个字段。Unity为了安全不会在面板里明文展示密码而且在你更换项目或者Unity版本升级后这些密码字段有时会被重置为空。所以排查的第一步就是把这四个字段重新检查一遍尤其是勾选了Custom Keystore的状态下四个字段必须全部有效。2.2 一个很容易忽略的坑勾选了Custom Keystore但文件路径失效这个坑特别隐蔽。有时候你明明填好了密码但打包依然报这个错为什么因为你选的keystore文件路径失效了。比如从别人那里拷贝了项目项目保留了keystore路径配置但keystore文件本身没有拷贝过来或者被移动到了其他目录。Unity在打包时发现keystore文件不存在它的表现不一定是文件找不到这种明显的报错而是会把这个情况看作签名配置不完整直接抛出无法签名、请提供密码。所以当你排查报错时一定要先确认Project Keystore指向的文件确实存在、并且有读取权限。文件没了密码填得再正确也没用。2.3 用keytool验证keystore和密码你现在需要的是快速定位问题到底密码错了还是keystore文件坏了。这时候请出JDK自带的keytool工具它可以查看keystore的信息并验证密码。在命令行执行keytool -list -v -keystore your-release.keystore -storepass 你的密码如果你的密码正确且keystore文件正常命令会输出这个keystore里所有密钥的信息包括别名、有效期、指纹等。如果密码错误keytool会直接提示keystore password was incorrect。这是一条非常有效的分界线keytool能读出来说明文件和主密码没问题那问题就集中在Key Alias和Key Password上如果keytool都报密码错误那就是keystore文件或主密码的问题。提示keytool一般位于JDK的bin目录下。如果你在命令行直接输入keytool提示找不到命令需要先配置Java环境变量或者输入JDK bin目录的完整路径。3. 命令行和CI环境下的签名信息从哪来如果说编辑器里的问题还算直观那命令行构建时的签名报错才是真正让团队头疼的。尤其是现在越来越多的团队用Jenkins、GitLab CI或GitHub Actions做自动化打包签名信息怎么安全地传给Unity构建进程是个需要认真设计的问题。3.1 构建脚本里如何设置签名参数在命令行调用Unity打包时通常会执行一个自定义的C#构建脚本BuildScript。这个脚本里如果要用自定义签名必须显式指定签名信息否则Unity不知道你的签名密码是什么。一个最基础的签名设置代码是这样的using UnityEditor; using UnityEditor.Build.Reporting; using UnityEngine; public class CustomAndroidBuilder { public static void BuildAndroid() { // 指定签名文件名路径相对于项目根目录 PlayerSettings.Android.keystoreName keystore/release.keystore; // keystore 主密码 PlayerSettings.Android.keystorePass 你的store密码; // 密钥别名 PlayerSettings.Android.keyaliasName your_alias; // 密钥密码 PlayerSettings.Android.keyaliasPass 你的key密码; var options new BuildPlayerOptions { scenes new[] { Assets/Scenes/Main.unity }, locationPathName Build/output.apk, target BuildTarget.Android, options BuildOptions.None }; BuildReport report BuildPipeline.BuildPlayer(options); if (report.summary.result ! BuildResult.Succeeded) { throw new System.Exception(Android build failed: report.summary); } } }然后命令行执行/path/to/Unity -batchmode -quit -projectPath /path/to/project -executeMethod CustomAndroidBuilder.BuildAndroid -logFile build.log这里有个关键点在BuildPlayer执行时Unity会读取上述四个PlayerSettings字段。只要其中任何一个为空、或者内容和实际keystore对不上就会触发签名报错。3.2 环境变量注入的正确姿势在CI环境里直接把密码写死在构建脚本里显然不安全——仓库权限一旦泄露所有密码跟着暴露。正确的做法是从环境变量读取让CI平台的Secret配置来管理敏感信息。把上面的脚本改一下public static void BuildAndroid() { var storePass System.Environment.GetEnvironmentVariable(KEYSTORE_PASS); var keyPass System.Environment.GetEnvironmentVariable(KEY_PASS); var keystorePath System.Environment.GetEnvironmentVariable(KEYSTORE_PATH); var alias System.Environment.GetEnvironmentVariable(KEY_ALIAS); if (string.IsNullOrEmpty(storePass) || string.IsNullOrEmpty(keyPass) || string.IsNullOrEmpty(keystorePath) || string.IsNullOrEmpty(alias)) { throw new System.Exception(Missing environment variables for Android signing.); } PlayerSettings.Android.keystoreName keystorePath; PlayerSettings.Android.keystorePass storePass; PlayerSettings.Android.keyaliasName alias; PlayerSettings.Android.keyaliasPass keyPass; // ... BuildPlayer ... }然后在CI里配置好这些环境变量再执行构建。这样密码不会出现在代码仓库里而且便于在不同项目之间复用签名信息。提示在本地跑export KEYSTORE_PASSxxx之后再执行Unity命令行可以模拟CI环境。这也是排查为什么CI报错但本地编辑器不报错的一个好办法——先确认环境变量是否真的传进去了。3.3 迁移项目、换电脑、升级Unity后的经典事故除了CI我还得专门提醒一种常见场景你把项目从一个电脑拷贝到另一个电脑或从Unity旧版本升到新版本。重新打开项目后编辑器里的签名配置可能还在但密码字段有时会被清空。这是Unity升级过程中的一个已知问题因为PlayerSettings.asset在升级时某些字段会被重置为默认值。遇到这种情况你不需要重建keystore只需要重新在Publishing Settings里输入密码即可。但如果是自动化构建就麻烦一些构建机上的Unity升级后之前由旧版本Unity写入的签名配置可能失效你必须在构建脚本里重新确认签名信息或者让CI流程每次构建前重置一遍签名配置。我还遇到过一种情况项目从Windows开发机迁到macOS构建机keystore文件路径格式发生变化Windows用C:\macOS用/Users/路径没改导致文件找不到最终报的还是签名密码错误。这种路径变了却只盯着密码字段的排查方向会让人绕很大一个弯。4. keystore文件本身的问题怎么判断是密码错还是文件坏了密码填了路径也对可构建还是报错。这时候就必须进一步检查keystore文件本身。这一节给你一个相对完整的判断流程。4.1 常见文件问题清单keystore这个文件本身也会出各种问题我列一个排查清单文件损坏拷贝过程中文件不完整或者被修改过导致keytool无法读取。已经过期keystore里的密钥有效期过了签名时就会失败虽然报错不一定直接说expired但最终结果也会表现为无法签名。文件权限问题在CI机器上Unity进程没有读取该文件的权限会间接导致签名失败。被Git忽略但本地有很多人项目里配置了.gitignore忽略keystore文件换到新机器后文件缺失构建进入签名环节报错。路径中包含中文或空格某些工具链对中文和空格路径处理不好这也会导致签名工具找不到文件。4.2 密码真的忘了吗先试试这些默认值如果这个keystore是Unity自动生成的debug.keystore那密码就是固定的android无需纠结。很多人不知道这一点拿自己的自定义密码去试debug.keystore当然怎么试都不对。如果你不确定当前用的keystore到底是哪一个可以先去下面位置看看WindowsC:\Users\你的用户名\.android\debug.keystoremacOS/Linux~/.android/debug.keystore如果确认用的是Unity自动生成的debug签名那Keystore Password和Key Password都填androidKey Alias一般是androiddebugkey。这里直接给出一套适用于debug签名的标准配置省得你到处翻文档字段Debug签名默认值Keystore PasswordandroidKey AliasandroiddebugkeyKey Passwordandroid4.3 重新生成keystore的正确流程如果确认keystore文件已经损坏、密码彻底遗忘、或者密钥过期那就必须重新生成一个keystore。这个过程用keytool完成keytool -genkeypair -v -keystore release.keystore -alias your_alias -keyalg RSA -keysize 2048 -validity 20000执行时会让填一些组织信息比如姓名、组织、城市、国家等。最重要的是设置两个密码keystore主密码和别名密码这两个都可以设置成同一个也可以不同。-validity 20000表示有效天数20000天大约54年对移动应用来说足够长了。生成之后马上用前面提到的方法验证一遍keytool -list -v -keystore release.keystore -storepass 你的密码确认能正常读取后再填到Unity的Publishing Settings里。注意重新生成keystore是一个非常重大的决定。因为Android应用升级时必须用同一个签名如果你已经发布过正式包重新生成keystore会导致后续包无法覆盖安装只能卸载旧包再装新包用户数据也会丢失。所以只要是已上线的应用优先想尽办法找回旧keystore而不是生成新的。5. 快速止损debug签名与自定义签名的切换策略排查归排查如果项目上线时间紧张很多时候你需要的是先能把包打出来。这时候就必须权衡要不要先用debug签名顶一下还是必须用自定义签名。5.1 什么时候可以直接用debug签名debug签名适用于所有不需要发布到应用市场的场景内部测试、QA验收、给开发同事做联调、或者只是想看一下某个功能在这台设备上的运行效果。它也能正常安装到手机上不会因为签名是debug而拒绝安装。在Unity里只要不勾选Custom Keystore默认就会使用debug签名。换句话说你取消勾选Custom Keystore密码报错立刻消失构建可以正常走完。注意如果之前用的是debug签名并且已经用debug签名安装过某个版本那后续用真正release签名安装时系统会因为签名不一致要求先卸载旧包。所以debug签名包可以用于测试但千万别拿它上架或作为正式包分发给用户。5.2 切换签名的注意事项从自定义签名切回debug签名或者反过来对Unity工程来说都只是一次配置修改。但有几个坑值得注意Android系统不会让你静默覆盖签名不一致的包。必须先卸载旧版本再安装新版本否则会报INSTALL_FAILED_UPDATE_INCOMPATIBLE。微信开放平台、支付宝等第三方SDK的回调配置在部分场景下会校验应用签名。切换签名后相关功能可能失效需要同步更新对应平台的签名配置。如果项目里有带签名的自定义权限sharedUserId换签名后权限校验会失败可能导致两个App之间无法拉起或共享数据。5.3 一套安全的签名信息管理方案既然签名信息这么重要又这么容易丢我在这里给出一套我实践中用着比较顺的管理方案供参考keystore文件放在项目外的独立目录比如/secure/keys/不要提交进Git仓库也不放进Unity工程目录。这样即使项目克隆到任意机器也不会把签名文件带走。在CI平台的Secret配置里保存三个值keystore的base64内容、store密码、key密码。构建时先解密出keystore文件再传给Unity构建脚本。所有密码写入团队密码管理器至少要有两个负责人知道。避免某位同事离职后密码无人知晓。每次改签名配置前先写一个构建脚本测试打包确认签名信息有效后再提交代码降低CI失败概率。这套方案牺牲了一点便利性但能在密码丢失和信息泄露两个方向上都增加保障比起把密码写在PlayerSettings里一存就是几个月要稳妥得多。6. 围绕这个报错的其他变体与我的排查心得你可能以为签名报错就这一种形态但我实际踩过的坑里有一些和Unable to sign the application; please provide passwords!同源但表现略有差异的情况这里做个汇总。6.1 变体一当前用户目录是中文名导致无法创建debug.keystore这种场景比想象中常见。Windows用户名如果是中文Unity自动创建debug.keystore时会因为路径编码问题失败。表现是你没勾选Custom Keystore但打包时依然出现签名报错日志里还会夹杂一些奇怪的路径乱码。解决方案是不改系统用户名的情况下手动到C:\Users\中文名\.android\下创建一个debug.keystore或者干脆改用自定义签名并指定一个纯英文路径下的keystore文件。6.2 变体二多个Unity版本并存导致签名配置状态异常本机装了Unity 2020和Unity 2021两个版本项目在一个版本里配置好签名切到另一个版本打开后PlayerSettings.asset中签名配置读取异常打包时出现签名报错。这种情况下重新在对应Unity版本里打开Publishing Settings把密码重新填一遍即可。出现这个问题的根本原因是两个版本对PlayerSettings.asset中签名相关字段的序列化方式有细微差别属于Unity不同版本间的兼容问题。6.3 变体三Build Report日志里提示签名失败但编辑器不报错使用BuildPipeline.BuildPlayer调用打包时有时错误不在Unity日志的红色级别显示而是藏在BuildReport的日志文件里。如果你用命令行打包且加了-logFile参数记得打开日志文件搜索sign关键字不要只看控制台输出。我见过有人盯着命令窗口看了半天结果错误只是被重定向到了日志文件里。6.4 我个人常用的两分钟排查套路如果你现在正在被这个报错折磨直接按下面这个顺序来90%的情况能快速定位打开Publishing Settings确认是否勾选了Custom Keystore。没勾选那问题大概率在默认debug.keystore上直接用keytool去读一下系统默认位置的debug.keystore确认文件存在且密码是android。勾选了确认keystore文件路径存在、可读。然后重新输入一遍四个字段尤其是两个密码和别名。仍然报错用keytool直接在命令行验证keystore区分是文件、主密码还是别名密码的问题。CI环境在构建脚本里加打印把环境变量是否读到的信息输出到日志中确认Unity构建进程确实拿到了密码。最后再多说一句真心话签名密码这种事一定要在项目一开始就建立制度而不是等报错出现了才开始回忆。把密码写在一张便利贴上、贴在显示器边框的人我见过好几个了——包括当年刚入行的我自己。等你被这个报错折磨过几次就会明白给keystore做备份、把密码交给信任的管理员比什么都重要。项目可以重构代码可以重写唯独发布签名这个密钥丢了就是真丢了。
返回列表