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

资讯详情

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

mac终端生成Android证书完整指南:keytool命令详解与实操

mac终端生成Android证书完整指南:keytool命令详解与实操 1. 为什么建议在mac终端下生成Android证书做Android开发这些年我越来越习惯在终端里完成签名证书的生成。你可能会问Android Studio里不是有图形化的Generate Signed Bundle/APK向导吗为什么不直接点界面说实话图形界面有它的优势但终端方式在很多场景下更符合我的工作流。先说清楚一个概念Android证书本质上就是一个数字签名文件它用来证明你的APK/AAB确实由你本人或你的组织发布同时也保证应用包在传输过程中没有被篡改。没有签名的应用无法安装到Android设备上即使能装也会被系统直接拦下来。尤其是你打算上架应用商店、接入微信支付/支付宝、或者做企业内部分发的时候签名文件就是你的“身份证”丢了、密码忘了、有效期过了都会带来非常头疼的问题。回到“为什么在终端生成”这个问题。我个人的体会是第一命令行的方式完全可控每一步做了什么、所有参数的含义、生成的产物在哪里都是一目了然的第二方便自动化。如果公司有CI/CD流水线你总不能让打包机开着Android Studio去点按钮还是得靠命令行来生成和管理证书第三格式化输出。终端工具可以结合keytool、openssl这些命令把证书信息完整导出来方便排查问题。还有一个很实际的原因很多人会遇到“Android Studio签名向导填写完信息后点击OK却发现按钮是灰的”这种反人类交互也不知道自己填的OU、CN、LOCALITY到底该写什么。而在终端里每个提示都清清楚楚就算填错了也知道问题出在哪一步。这篇内容就是一套完整的、从零开始的mac终端生成Android证书实操我会把每一步的命令、参数、背后逻辑、以及我踩过的坑都写清楚。无论你用的是Intel芯片的旧Mac还是Apple Silicon的新机器流程基本一致区别仅在JDK的安装方式上。2. 环境准备先搞定JDK和keytool工具2.1 检查本机是否已有JDKkeytool是JDK自带的工具所以要生成Android证书第一步就是确认你的mac上有没有装JDK。打开终端快捷键Command 空格输入“终端”回车执行java -version如果看到类似下面的输出说明JDK已经装好了openjdk version 17.0.8 2023-07-18 OpenJDK Runtime Environment Homebrew (build 17.0.80) OpenJDK 64-Bit Server VM (build 17.0.80, mixed mode, sharing)如果提示command not found: java或者根本没反应那就需要手动安装JDK。还有一个比较隐蔽的情况你电脑上装了多个JDK版本但终端用的不是你想用的那个。这种情况在mac上特别常见尤其是之前装过Android Studio自带JBRJetBrains Runtime的机器。建议用下面这个命令查看系统里所有JDK的安装路径/usr/libexec/java_home -V输出结果会列出所有JDK及对应路径比如Matching Java Virtual Machines (3): 17.0.8 (x86_64) Oracle Corporation - Java SE 17 /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home 11.0.20 (x86_64) Oracle Corporation - Java SE 11 /Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home 1.8.0_391 (x86_64) Oracle Corporation - Java SE 8 /Library/Java/JavaVirtualMachines/jdk-1.8.0.jdk/Contents/Home2.2 安装JDK的几种方式mac上安装JDK我最推荐的方式是Homebrew因为后续升级、卸载都方便。如果你还没装Homebrew先执行/bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)装好Homebrew后可以选择安装OpenJDKbrew install openjdk17这里多说一句为什么推荐OpenJDK 17而不是8或11因为目前主流Android Gradle Plugin版本7.x/8.x对JDK 11和17都有很好的支持而JDK 8已经比较老新版本的AGP用JDK 8编译时偶尔会出一些兼容警告。当然如果你维护的是老项目AGP版本还停留在4.x甚至更低那就装JDK 8更省事。用Homebrew安装的OpenJDK默认不会被加到JAVA_HOME环境变量里。你需要执行下面的命令做一下软链接或者直接配置环境变量。我个人喜欢直接配置环境变量因为更直观echo export JAVA_HOME$(/usr/libexec/java_home -v 17) ~/.zshrc echo export PATH$JAVA_HOME/bin:$PATH ~/.zshrc source ~/.zshrc然后再次执行java -version确认生效。如果你不想用Homebrew也可以直接从Oracle官网下载JDK的dmg安装包安装过程就是标准的mac软件安装流程一路下一步就行。装完后同样可以用/usr/libexec/java_home -V验证。2.3 确认keytool可用JDK装好之后keytool自然就在$JAVA_HOME/bin目录下而JAVA_HOME已经加入了PATH所以直接执行keytool -help如果输出一大段关于keytool各参数的解释说明工具已经可用了。这里想提醒一个关键点终端里执行keytool必须确保使用的是你刚配置的JDK路径而不是系统自带的那个。有个检查方法which keytool正常情况下会输出类似/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/bin/keytool这样的完整路径。如果输出的是/usr/bin/keytool也别慌因为macOS的/usr/bin下有可能有指向系统默认JDK的符号链接并不一定代表有问题。只要java -version显示的版本是你期望的那个keytool也会跟着走。3. 正式生成Android签名证书3.1 理解证书、密钥库和别名三者的关系在输入生成命令之前我想先理清三个基础概念不然你可能会在后续的使用中犯迷糊。密钥库Keystore这是一个包含一个或多个密钥对的容器文件后缀名通常为.jksJava KeyStore或.keystore。你可以把它理解成一个保险柜里面可以放多把钥匙。密钥对Key Pair由公钥和私钥组成。公钥用来验证签名私钥用来生成签名。在Android签名体系里私钥必须严格保密泄漏了就等于签名体系崩溃。别名Alias密钥库中每一条密钥对的唯一标识。就好比你在保险柜里给每把钥匙贴了一张标签取用时通过标签来找对应钥匙。一个密钥库文件里可以存放多个别名下的多份密钥。实际开发中可能一个.jks文件里同时存了release正式版和debug调试版两个别名这在多环境构建时很有用。3.2 用keytool生成新证书先到你想存放证书文件的目录比如~/keystoresmkdir -p ~/keystores cd ~/keystores然后执行生成命令。这是最常用的一条keytool -genkeypair -v \ -keystore release.jks \ -alias myapp \ -keyalg RSA \ -keysize 2048 \ -validity 10000 \ -dname CNMyApp, OUMobile Team, OMyCompany, LBeijing, STBeijing, CCN我一条条拆开讲-genkeypair生成密钥对这是Java 7之后替代-genkey的新参数。-vverbose模式输出详细过程。建议加上否则终端卡在“正在生成…”的时候你根本不知道它在干嘛。-keystore release.jks指定密钥库文件名。文件名随意但建议用release/debug/xxx这样有业务含义的名称。-alias myapp别名后面在Gradle或命令行中签名时需要用到。-keyalg RSA密钥算法Android推荐RSA。-keysize 2048密钥长度。2048位是当前的安全底线1024已经不被推荐。-validity 10000证书有效期单位是天。10000天大约是27年。Google Play要求证书有效期到2033年10月22日之后所以10000天是行业里比较通用的值。-dname CNMyApp, OUMobile Team, OMyCompany, LBeijing, STBeijing, CCN证书持有者信息。这个参数可以不写然后keytool会交互式地逐项询问你也可以一步到位直接写在命令行里。执行时终端会要求你设置密钥库密码和密钥密码Enter keystore password: Re-enter new password: Enter key password for myapp (RETURN if same as keystore password):注意终端输入密码时不会显示任何字符也不会显示星号这是正常的。如果密钥密码直接回车表示和密钥库密码相同。我建议两者保持一致避免后续构建时还要单独配置一个密钥密码省得麻烦。3.3 交互式填写证书信息如果没写-dname你会看到一串交互式提问What is your first and last name? [Unknown]: MyApp What is the name of your organizational unit? [Unknown]: Mobile Team What is the name of your organization? [Unknown]: MyCompany What is the name of your City or Locality? [Unknown]: Beijing What is the name of your State or Province? [Unknown]: Beijing What is the two-letter country code for this unit? [Unknown]: CN Is CNMyApp, OUMobile Team, OMyCompany, LBeijing, STBeijing, CCN correct? [no]: yes这里容易被忽视的一点是这些信息一旦写入证书以后没法直接修改。你只能重新生成一份证书。所以填的时候要慎重别随便敲几个字母应付了事。尤其是你准备上架应用市场时证书里的组织信息最好和你的公司信息保持一致。3.4 常见参数误区与建议网上很多教程会在-keyalg RSA后面加上-sigalg SHA256withRSA这个参数其实可以不加。因为keytool会根据-keyalg RSA自动选择合适的默认签名算法通常是SHA256withRSA已经是安全的了。另外我看到有同学用-keysize 4096。理论上更安全但实际在Android签名场景中2048位已满足所有商店的要求而4096位会让签名运算稍微变慢、APK体积也会大一点点。所以除非你所在的团队有更严格的安全规范否则2048够用。还有个坑**在旧版本JDK8及之前中-validity参数如果超过某个非常大的值可能会被拒绝提示“validity is too large”之类的问题。**但实际上10000天远没有触发这个限制所以不用担心。命令执行完成后目录下会出现一个release.jks文件。你可以用下面的命令查看它的基本信息ls -lh release.jks输出的文件大小通常在1到2KB之间。如果只有几百字节或者提示文件损坏那就得重新生成。4. 密钥库格式与转换JKS还是PKCS124.1 为什么推荐PKCS12相信不少同学注意到过JDK 8及以前版本生成的文件默认是.jks格式但从JDK 9开始keytool默认生成的就是PKCS12格式了只是文件名习惯上仍保留.jks后缀。这两个格式的区别在于JKS是Sun公司私有的格式只能由Java生态的工具操作而PKCS12是国际标准格式能被OpenSSL、Android工具链、各种编程语言库广泛支持。如果你以后要做自动化构建、对接第三方服务、或者在Windows/Linux上管理同一个密钥库PKCS12会省心很多。所以如果你的JDK版本是9可以不操心格式问题因为默认已经是PKCS12了。如果是从旧项目里继承的.jks文件建议做一次迁移keytool -importkeystore \ -srckeystore old_keystore.jks \ -srcstoretype JKS \ -srcstorepass 你的旧密码 \ -destkeystore new_keystore.p12 \ -deststoretype PKCS12 \ -deststorepass 你的新密码执行完成后新文件new_keystore.p12就是PKCS12格式的密钥库。你还可以用keytool -list验证一下迁移结果keytool -list -v -keystore new_keystore.p12 -storepass 你的新密码只要能看到别名、有效期、指纹等信息迁移就成功了。4.2 查看证书指纹签名文件生成后查看指纹是一个常规动作。因为你要去一些平台比如微信开放平台、高德地图配置App的签名信息时经常需要提供MD5、SHA1或SHA256指纹。命令如下keytool -list -v -keystore release.jks -alias myapp -storepass 你的密码输出中会包含一段类似这样的内容Certificate fingerprints: SHA1: 12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0:12:34:56:78 SHA256: AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89这里特别提醒一下**一个App在不同的签名文件下会有不同的指纹而指纹又直接关联到微信/支付宝/地图等SDK的授权校验。**如果你换了一个新的签名文件去打包就必须同步更新各开放平台上的签名信息否则会出现“客户端签名不正确”之类的错误。这个问题的排查有时候能折腾一整天。5. 学以致用把证书配置到Android项目里证书生成不是终点最终目标是让打包工具能用到它。目前Android项目主流的构建工具是Gradle所以这里展示两种配置方式一种适合单模块项目一种适合多模块或自动化构建场景。5.1 直接在build.gradle中配置打开android/app/build.gradle或项目主模块的build.gradle在android节点下加入android { signingConfigs { release { storeFile file(/Users/你的用户名/keystores/release.jks) storePassword 你的密码 keyAlias myapp keyPassword 你的密码 } } buildTypes { release { signingConfig signingConfigs.release minifyEnabled true proguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro } } }把密码直接写进build.gradle有个显而易见的好处简单。坏处同样明显如果项目代码放在Git仓库里相当于把密码也提交进去了。所以这种方式我只建议在本地个人项目里用。5.2 用gradle.properties和CI环境变量管理密码更稳妥的做法是把密钥信息放在~/.gradle/gradle.properties全局配置不会提交到Git或者项目根目录的gradle.properties如果项目是闭源的中RELEASE_STORE_FILE/Users/你的用户名/keystores/release.jks RELEASE_STORE_PASSWORD你的密码 RELEASE_KEY_ALIASmyapp RELEASE_KEY_PASSWORD你的密码然后在build.gradle里改成signingConfigs { release { storeFile file(RELEASE_STORE_FILE) storePassword RELEASE_STORE_PASSWORD keyAlias RELEASE_KEY_ALIAS keyPassword RELEASE_KEY_PASSWORD } }如果你的CI服务器比如Jenkins、GitLab CI需要构建可以在CI的环境变量里注入同名变量Gradle会优先读取环境变量。这样密码就不会落到任何Git仓库中。5.3 用apksigner验证签名打包完成或者拿到别人给你的APK怎么确认它用的什么证书签名用Android SDK的apksigner。这个工具在Android SDK的build-tools目录下如果配好了环境变量可以直接执行apksigner verify --verbose --print-certs app-release.apk输出中会打印证书的SHA256指纹以及签名算法版本v1/v2/v3。如果你发现指纹和keytool -list里的不一致那说明APK不是用对应的证书签的基本可以断定是配置错了。6. 常见问题与排查技巧实录6.1 执行keytool报“command not found”这个是最常见的问题。原因一般是JDK没有安装或者JAVA_HOME没有正确配置。先按前面说的java -version和/usr/libexec/java_home -V检查确认JDK存在之后强制指定完整路径执行keytool$JAVA_HOME/bin/keytool -genkeypair -v ...如果$JAVA_HOME也是空的就找到JDK实际安装路径再执行。以Homebrew安装的OpenJDK 17为例路径通常是/usr/local/opt/openjdk17/bin/keytoolIntel Mac或/opt/homebrew/opt/openjdk17/bin/keytoolApple Silicon。6.2 报“java.lang.Exception: Key pair not generated, alias already exists”这说明密钥库里已经存在相同别名的密钥对。有两种处理方式一是换一个别名二是删除旧别名重新生成会丢失原密钥确认旧证书确实不再需要再操作keytool -delete -alias myapp -keystore release.jks -storepass 你的密码6.3 密码忘了怎么办坦白说密码忘记基本无解。Android签名证书一旦生成密钥库密码和密钥密码都无法通过任何官方途径找回。唯一能做的就是用备份恢复。所以生成证书的当天就应该做好备份把.jks文件拷贝到至少两处安全的地方把密码记到密码管理器中。有个朋友曾经历过一个惨痛教训公司App上线两年后负责签名证书的老员工离职了没交接密码。后来App需要更新但安装包签名对不上只能想办法联系平台客服走应用迁移流程中间耗费了极大的沟通成本。这件事之后我给团队立了一个规矩所有证书密码必须同时存在密码管理器里并且至少要两个负责人知道。6.4 证书有效期判断想查看证书什么时候过期用keytool -list -v -keystore release.jks -alias myapp -storepass 你的密码输出中会有类似Valid from: Mon Jan 01 10:00:00 CST 2024 until: Sun Jun 15 10:00:00 CST 2051注意until对应的日期就是过期日。Android官方和Google Play要求证书有效期至少到2033年10月22日所以你要确保那一刻晚于这个日期。如果发现证书的有效期不够了解决办法只有重新生成一个新证书并做好应用更新时的签名衔接。这里有个关键点Android的签名体系不允许应用在更新时无缝切换证书。如果旧版本是A证书签的新版本突然用B证书签系统会直接拒绝安装不卸载旧App的前提下。所以要么长期维护同一套证书要么在早期规划好长有效期并妥善保藏。6.5 zsh环境下JAVA_HOME不生效macOS默认Shell在较新版本中已经切换为zsh很多教程还在教你改~/.bash_profile结果当然不生效。务必检查~/.zshrc或~/.zprofile中是否有相关配置。改完配置后执行source ~/.zshrc或者直接重开一个终端窗口。6.6 keytool生成时中文乱码如果你在-dname或交互式提问中填入了中文字符部分终端可能会出现乱码但这通常不影响最终证书内容。要避免显示层面的乱码可以把终端字符编码设置为UTF-8一般默认就是。如果实在不放心证书信息里的组织名/城市名用英文拼音也完全可以Android商店并不会因此拒绝你的应用。7. 一个更“现代”的选择Android Studio与终端结合虽然这篇文章的主题是终端但我也想说一个常见场景。很多开发者的实际工作流是这样的先用Android Studio里的Build Generate Signed Bundle/APK生成一次证书拿到.jks文件后后续的构建流程切回终端Gradle。这个流程没有什么问题Android Studio生成证书时本质上还是调用keytool只是给你包装了个图形界面。但如果你需要高度可重复的自动化或者你正在写打包脚本请一定优先掌握终端命令。终端方式不仅能生成证书还能配合openssl做更多底层的证书管理和格式转换比如导出公钥给服务端同学做校验。熟练之后你会发现终端的速度远比图形界面快——你可能只需要几分钟就能完成证书生成、指纹提取、Gradle配置对接三件事。我在实际项目中甚至把整个流程写成了一个Shell脚本。每次新项目需要签名证书时只需要执行一条命令并传入项目名和包名脚本就能自动完成目录创建、keytool生成、指纹输出。这里分享一个简单的版本供你参考#!/bin/bash # gen_android_keystore.sh # 用法: ./gen_android_keystore.sh 你的应用名 APP_NAME$1 OUTPUT_DIR$HOME/keystores KEYSTORE_FILE$OUTPUT_DIR/${APP_NAME}.jks ALIAS${APP_NAME} mkdir -p $OUTPUT_DIR keytool -genkeypair -v \ -keystore $KEYSTORE_FILE \ -alias $ALIAS \ -keyalg RSA \ -keysize 2048 \ -validity 10000 \ -dname CN${APP_NAME}, OUMobile Team, OMyCompany, LBeijing, STBeijing, CCN \ -storepass 你的默认密码 \ -keypass 你的默认密码 echo 证书已生成: $KEYSTORE_FILE keytool -list -v -keystore $KEYSTORE_FILE -alias $ALIAS -storepass 你的默认密码 | grep -A2 Certificate fingerprints实际生产环境中建议把“你的默认密码”改成动态生成的随机强密码保存到密码管理器里。自动化流程顺带打印出指纹信息能省掉很多后续手工查证的步骤。最后再分享一个小技巧如果你有多个项目共存尽量让密钥库文件的命名与项目名对应并且每把密钥只用在一个正式App上。Google Play有类似的签名规则如果你想做应用内更新、多APK支持或者Play App Signing一把密钥只对应一个Application ID是更不容易出错的管理方式。这个决策在你创建证书的时候就该想清楚而不是等上线了再反悔重签。
返回列表