Debugging Android Keystore Signing
// <![CDATA[ function styleCode() { if (typeof disableStyleCode != 'undefined') { return; } var a = false; $('code').each(function() { if (!$(this).hasClass('prettyprint')) { $(this).addClass('prettyprint'); a = true; } }); if (a) { prettyPrint(); } } $(function() {styleCode();}); // ]]>
Recently, the Android developers at Cotap began seeing this error when installing alpha and beta versions of the app:
INSTALL_FAILED_DUPLICATE_PERMISSION perm=com.cotap.xxx.permission.C2D_MESSAGE pkg=com.cotap.xxxx
At first I chalked it up to some bug in Android 5.0, since it started showing up only in L and above. However, one of our team members, Lillian Michalek, pointed out that it was only happening in builds I shipped. It turns out this was due to a change in the way Android 5.0 handles custom permissions. See an explanation from CommonsWear. At this point I knew there had to be more going on. Clearly, I should not be getting that exception because we were all using the same keystore to sign the apps built in our particular machines.
As a sanity check, I bumped the version number and built a production version of the app. Lo and behold, I could not install it on my phone. Meaning that, if I pushed that to the Play Store, customers would not be able to update their app. I asked Lillian to run the same experiment, building on her machine, and installing it on her phone. She had no issues.
Problem:
Although I had the same keystore on my local machine as Lillian, the app was being signed by a different keystore, with the same alias and password. This happened because, as part of reinstalling my development environment, I innocently ran the following keytool command before dropping our shared keystore into my machine:
$ keytool -genkey -v -keystore keystore_name.keystore -alias keystore_alias -keyalg RSA -keysize 2048 -validity 365
You can learn more about the Java Keytool here. The command above created a key that had the same alias and password as the one in our project. Worse, even though I was telling it to use a keystore in our project via the Gradle build, this was being overridden by the one I had created above.
signingConfigs { release { storeFile file("keystore_name.keystore")
keyAlias "keystore_alias" storePassword some_password keyPassword some_other_password } }
To prove I was using another keystore to sign my version of the apps I needed to dig deeper into what was being signed by what. These two answers to the same question listed the steps needed to open an apk and retrieve the .RSA file in order to see what keys were being used to sign the app. As you can see in the image below, Lillian had a different certificate.
Another giveaway in hindsight: since we are using a shared certificate in our project, the keytool command should not be aware of it. So if I ran the following command, I should not see anything:
$ keytool -list -keystore keystore_name.keystore
Instead there was a certificate with that name as a result of my previous “keytool -genkey” command.
Another way to see which flavor of the apps were signed with what certificate, you can use the following Gradle command, also from Stack Overflow:
$ ./gradlew signingReport
This will give you an output showing you what keystore is being used for which app:
Solution:
To right this wrong I deleted the keystore that the keytool had created, and made sure to drop the right shared keystore in my local project. To delete a keystore you can use the following command:
$ keytool -delete -alias keystore_alias -keystore keystore_name.keystore
I got that in Stack Ov… jk! This site lists common Keytool commands. After that, all was groovy.
Bonus!
I know no one checks their production keystore into git. Right?!?! Here is how we save our keystore password in the Mac Keychain.
Put your keystore in your favorite directory. Here is our signingConfigs in the build.gradle file:
signingConfigs { release { storeFile file("keystore_name.keystore")
keyAlias "keystore_alias" storePassword getPassword('ANDROID_KEYSTORE_PASSWORD') keyPassword getPassword('ANDROID_KEY_PASSWORD') } }
Now use this method to pull the password from the Keychain. Notice it will prompt you in the command line if it can’t find it:
def getPassword(sysvar) { String pw = System.getenv(sysvar) if (pw == null) { def command = """security find-generic-password -w -a whatever_you_want""" def proc = command.execute() proc.waitFor()
if (proc.exitValue() == 0) { pw = proc.in.text.trim() } else if (System.console() != null) { pw = new String(System.console().readPassword("\n\$ Enter keystore password: ")) } } return pw }
Lastly, add your password to the Keychain. Below are the 4 steps: Now your password is automatically entered when building! Enjoy!
Now your password is automatically entered when building! Enjoy!














