APK必须使用与先前版本相同的证书进行签名。

244

我之前将我的应用程序上传到Google Play(当时称为Android Market)。

今天我更新了这个应用程序,但是我删除了之前的密钥库并创建了一个新的密钥库。
在上传时,它显示APK必须使用与之前版本相同的证书签名:

上传失败

您上传的APK与先前的APK使用不同的证书签名。您必须使用相同的证书。

您现有的APK使用以下指纹的证书进行签名:
[ SHA1: 89:2F:11:FE:CE:D6:CC:DF:65:E7:76:3E:DD:A7:96:4F:84:DD:BA:33 ]
而您上传的APK所使用的证书具有以下指纹:
[ SHA1: 20:26:F4:C1:DF:0F:2B:D9:46:03:FF:AB:07:B1:28:7B:9C:75:44:CC ]

但是我没有这个证书,并且不想删除并重新发布应用程序,因为它拥有活跃用户。

我该如何使用新证书对应用程序进行签名?


34
我有一个不同的问题:我尝试升级一个应用程序,但它一直显示这个错误。事实是,我从来没有改变过密钥库!!!我该怎么办?!? - Mariux
你是如何解决的? - Elizabeth
@int_32 你是怎么解决的? - Amit Sharma
16个回答

213

没有什么。阅读文档:在Android市场上发布更新

在上传更新的应用程序之前,请确保已经增加了清单文件中 element 的 android:versionCode 和 android:versionName 属性。此外,包名必须相同且.apk 必须使用相同的私钥签名。如果包名和签名证书与现有版本不匹配,则市场将视其为新应用程序,并不会向用户提供更新。


17
好的回答。我从来没有意识到如果密钥丢失了,应用程序就无法更新。一定要记得备份密钥并放在安全的地方。 - Peter Knego
19
我通常会将密钥库文件存储在SVN中。创建一个名为credential的新文件夹,与trunk/tag/branches同级,并将密钥库文件存储在其中。还要添加一个新的.txt文件,指示密钥库文件的位置。密钥库和源代码一样重要。一旦丢失(或忘记密码),您就会很糟糕。 - Krishnabhadra
48
请不要像@Krishnabhadra所说的那样,将您的密钥库密码(或任何密码)提交到源代码控制中。请保持密钥库和密码分开,并确保密码安全。 - Christopher Orr
2
什么?!但是它刚刚告诉我我的密钥太旧了,所以我删掉了它并创建了一个新的,现在我又遇到这个问题了!? - user156888
2
@iwayneo 构建系统可能会告诉你你的 debug 密钥太旧了,但是这在 release 密钥中发生的可能性非常小,因为 Google Play 应该会拒绝在 2033 年 10 月之前过期的密钥。 - Christopher Orr
显示剩余4条评论

145

你是否错误地使用了调试密钥进行签名?

Google Play不允许您发布使用调试密钥库签名的应用程序。如果您尝试上传这样的APK,Google Play将会提示“您上传的APK是在调试模式下签名的。您需要以发布模式签署您的APK。”

但是,如果您尝试上传一个使用调试密钥库签名的更新,您将不会看到此消息;Google Play将显示与SHA1指纹相关的问题中显示的消息。

因此,首先请检查是否错误地使用了调试密钥来签署应用。


如何检查所使用的签名密钥?

从APK中获取信息

您可以使用Java keytool命令来检查原始APK和更新APK使用的证书:

keytool -printcert -jarfile original.apk
keytool -printcert -jarfile update.apk

这将向您显示有关APK签名方式的详细信息,例如:

Owner: CN=My App, O=My Company, L=Somewhere, C=DE
Issuer: CN=My App, O=My Company, L=Somewhere, C=DE
Serial number: 4790b086
Valid from: Mon Nov 11 15:01:28 GMT 2013 until: Fri Mar 29 16:01:28 BST 2041
Certificate fingerprints:
  MD5:  A3:2E:67:AF:74:3A:BD:DD:A2:A9:0D:CA:6C:D4:AF:20
  SHA1: A6:E7:CE:64:17:45:0F:B4:C7:FC:76:43:90:04:DC:A7:84:EF:33:E9
  SHA256: FB:6C:59:9E:B4:58:E3:62:AD:81:42:...:09:FC:BC:FE:E7:40:53:C3:D8:14:4F
         Signature algorithm name: SHA256withRSA
         Version: 3

需要注意的重要部分 - 每个APK的 SHA1 指纹值、Owner 身份值以及 Valid from/until 日期。


如果 keytool 命令无法工作(-jarfile 选项需要 Java 7),您可以通过 jarsigner 命令获取更基本的信息:

jarsigner -verify -verbose:summary -certs original.apk
jarsigner -verify -verbose:summary -certs update.apk

很遗憾,这里并不显示SHA1指纹,但是会展示X.509所有者身份和证书到期日期。例如:

sm  4642892 Thu Apr 17 10:57:44 CEST 2014 classes.dex (and 412 more)

    X.509, CN=My App, O=My Company, L=Somewhere, C=DE
    [certificate is valid from 11/11/13 12:12 to 29/03/41 12:12]
    [CertPath not validated: Path does not chain with any of the trust anchors]

在这种情况下,您可以忽略任何关于“CertPath未经验证”的消息,以及有关证书链或时间戳的警告。

比较APK之间的所有者、SHA1和到期值

  • 如果所有者/X.509身份值为CN=Android Debug, O=Android, C=US,则您使用了调试密钥而不是原始发布密钥签署APK

  • 如果原始APK和更新APK之间的SHA1指纹值不同,则表示您没有同时为两个APK使用相同的签名密钥

  • 如果两个APK之间的所有者/X.509身份值不同,或证书过期日期不同,则表示您没有同时为两个APK使用相同的签名密钥

请注意,即使两个证书之间的所有者/X.509值相同,这也并不意味着证书相同 - 如果其他任何内容不匹配 - 例如指纹值 - 则证书不同。


搜索原始密钥库,检查备份

如果两个APK具有不同的证书信息,则您必须找到原始密钥库,即具有Google Play(或keytool)告诉您的第一个SHA1指纹值的文件。

在计算机上搜索所有可以找到的密钥库文件和任何备份,直到找到具有正确SHA1指纹的那个文件:

keytool -list -keystore my-release.keystore

如果提示要输入密码,只需按下Enter键即可 —— 如果您只是想快速检查SHA1值,就不必输入密码。


我找不到原始密钥库

如果您找不到原始密钥库,则永远无法发布此特定应用程序的任何更新。

Android在签署应用程序页面上明确提到了这一点:

警告:将密钥库和私钥保存在安全可靠的地方,并确保您对其进行了安全备份。 如果您将应用程序发布到Google Play,然后失去了用于签署应用程序的密钥,您将无法发布任何应用程序更新,因为您必须始终使用相同的密钥签名所有版本的应用程序。

在第一个APK发布之后,所有后续发布都必须使用完全相同的密钥进行签名。


我能从原始APK中提取原始签名密钥吗?

不行,这是不可能的。 APK仅包含公共信息,而没有您的私钥信息。


我能迁移到新的签名密钥吗?

不行。 即使您找到了原始密钥库,也不能使用密钥A签署APK,然后使用密钥A和B签署下一个更新,然后仅使用密钥B签署以后的更新。

使用多个密钥对APK(或任何JAR文件)进行签名是技术上可能的,但Google Play不再接受具有多个签名的APK。

尝试这样做将导致消息“您的APK已使用多个证书签名。 请仅使用一个证书进行签名,然后重新上传它。”


我该怎么办?

您将不得不使用新的应用程序ID构建应用程序(例如,从“com.example.myapp”更改为“com.example.myapp2”),并在Google Play上创建全新的列表。

可能您还需要更改代码,以便人们可以安装新应用程序,即使他们安装了旧应用程序,例如,您需要确保没有冲突的内容提供程序。

您将失去现有的安装基础,评论等,并且必须找到一种方法让您的现有客户卸载旧应用程序并安装新版本。

再次确保您对此版本使用的密钥库和密码进行了安全备份。


我尝试了您提供的命令,用于检查调试(这正是我正在寻找的),但它返回一个错误,指出jar包包含未包括时间戳的签名。我使用了这个线程创建了我的apk:https://dev59.com/questions/1mQn5IYBdhLWcg3wt5Ag - CularBytes
@RageCompex 你是没有得到任何输出只有错误吗?当我运行该命令时,我也会收到一个时间戳警告(不是错误)。只要你得到了X.509的输出,那就足够了。 - Christopher Orr
是的,我确实得到了X.509输出,所以我想这不是问题吧?[CertPath not validated: Path does not chain with any of the trust anchors]怎么样,这也不是问题吗?我在CN看到了我的名字,所以我想我已经正确签名了 :) - CularBytes
@RageCompex,这个问题已经在“检查使用的签名密钥”部分得到了回答。 - Christopher Orr
2
今天我有一次小心脏病发作。我使用了这些命令行来找出隐藏的密钥库。非常感谢你,伙计!你救了我...真的 :D - Ajeet

15

谷歌明确表示,应用程序是由用于签名的密钥来标识的。因此,如果您丢失了密钥,就需要创建一个新的应用程序。


1
@sports 他们确实警告你。请注意大红色的警告信息:http://developer.android.com/tools/publishing/app-signing.html#release-mode - Christopher Orr
2
@运动 在任何情况下,您可以在同一开发者账户上发布多个应用程序,因此无需再次付费。 - Christopher Orr

12
我刚刚突然遇到了这个问题,我真的不认为我改变了任何东西。
然而,Build => Clean Project解决了它。

1
嗯,我已经花了一周的时间,尽可能地做了一切。现在是时候说“WTF”了,但这是唯一有用的方法。(我也尝试过清除缓存,但没有帮助...)谢谢。 - Upsilon42
1
在疯狂了一个小时后,这解决了问题。 - Boris Legovic
2
我不认为这有关联。 - Infinite Loops
非常感谢!我意外地使用了不同的密钥库文件和凭据生成了一个已签名的apk并上传。即使上传了具有正确密钥库的apk,仍然会出现相同的错误。经过一个小时的创建新版本、清除缓存、重启android studio和电脑,这最终解决了问题。 - Arun
经过许多试错,我得出了这个答案,并神奇地为我工作了,但仍在寻找如何做到的方法! - iHulk
你怎么在VSCode上实现呢?我觉得我也遇到了类似的问题。 - Try2prog

7
今天我遇到了同样的问题,不幸的是,我的密钥库文件中有两个别名。 enter image description here

5

我强烈推荐使用Keystore Explorer (https://keystore-explorer.org/),它可以让你无需上传到Google Play就能够访问你的密钥库。这样你就可以排除密码输入错误的问题。


5

我找到了这个问题的答案。经过漫长的搜索,我终于破解了密钥和密码。我忘记了我的密钥和别名还有jks文件,但幸运的是我知道我放在里面的一堆密码。但是对我来说,找到正确的组合是最艰巨的任务。

解决方法 - 下载这个 - Keytool IUI版本2.4.1插件 enter image description here

现在弹出窗口并显示别名..如果您的jks文件正确.. 右键单击别名,然后点击“查看证书链”.. 它将显示SHA1密钥..将此密钥与您在上传apk时获得的密钥进行匹配...

如果匹配,则您拥有正确的jks文件和别名..

现在我有一堆密码要匹配.. enter image description here

现在进入此屏幕,输入相同的jks路径..和密码(在您拥有的密码中)在“证书文件”中输入任何路径

如果屏幕显示任何错误,则密码不匹配..如果没有显示任何错误,则表示您拥有正确的jks文件,正确的别名和密码() 现在,您可以在Play商店上传您的apk :)


你丢失了私钥,然后通过这种方式检索回来了吗?如果是肯定的,你能否提供下载该工具的链接?另外我该如何打开这个应用程序? - LS_
非常好用,完美地达到了我的目的。感谢分享。 - Prajwal Kulkarni

4
请检查您的android/app/build.gradle文件。
android{
    ...
    buildTypes {
        release {
        // signingConfig signingConfigs.debug
           signingConfig signingConfigs.release
        }
    }
    ...
}

在发布应用程序包之前,应该启用signingConfig signingConfigs.release这一行,而不是signingConfig signingConfigs.debug这一行。
我犯了这个愚蠢的错误。
我通过使用debug sign in config发布更新的应用程序,在其他设备上测试我的更新应用程序,当我准备发布更新时,我没有更改配置并发布了具有调试配置的捆绑包。
在摸索了大约一个小时后,我意识到我一直使用这个debug config,然后我将其更改为release mode,现在问题已经解决了。

这可能对大家没有帮助,但在更新React Native版本后,许多文件发生了变化,这就是我的问题所在。更新将“signingConfigs.release”覆盖为“signingConfigs.debug”。 - Ebrahim Sayed
我已经更新了React Native和所有的包,但忘记再次更改这行代码。这就是我的问题所在。非常感谢! - Mahdieh Shavandi

3
如果您有之前的apk备份文件,则可以使用jarSigner从该apk中提取证书,然后使用该密钥或使用keytool克隆该证书,这可能会有所帮助... 有用的链接是jarsigner文档keytool文档

5
请告诉我们如何使用jarSigner从该apk中提取证书? - Rubycon
16
那样做无法恢复您需要重新签名apk所需的私钥。 - botteaap
Apk 必须使用相同的私钥进行签名。 - om252345

1

我的【愚蠢】错误是我使用了app-debug.apk文件而不是app-release.apk文件。 在生成签名APK时,您需要在“Build Variants”框架中选择“release”。 app-release.apk文件应位于项目根目录下的“app\release”文件夹中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接