Signing Ionic Apps for Android without Android Studio (Linux)

Overview

You’ve built your Ionic and are now wanting to package it for deployment to Android via the Google Play Console. While you can build and sign your APK in Android Studio you can also do it via the command line.

NOTE: This has only been run/tested on Linux (specifically Ubuntu 16.04 and 18.04) and assumes the following installed and configured properly:

Step 1: Building your Android APK using the Ionic CLI

To build our unsigned APK we use the the Cordova CLI (through the Ionic CLI). What this process does is first builds the Ionic project then once that is successful it triggers the Cordova process to begin compiling any native code and plugins we have then packaging it all together with our Ionic code. We also add a couple flags to our command to make sure this is a release build with all the optimizations possible so we output a small, production ready binary. (NOTE: Even though I said “production ready” and we are using cli flags to get that output, nothing should be considered production ready until it is tested).

Our flags passed into this command are:

  • --release: Gets passed to Cordova to have it generate a release build ready for the store
  • --aot: Gets passed to the Angular Compiler to make sure our HTML/TS code is compiled to an optimized and efficient output
  • --prod: Gets passed to Ionic to tell it this is a production build

For more info on the flags for this command, check out the docs here.

Once we are ready to build the application, we can run it like so

$ ionic cordova build --release --aot --prod android

Once this is complete you should get a success output message with an absolute path to the generated APK file.

Step 2: Creating a keystore to sign the APK

Before we can sign our newly generated APK file, we need to create a keystore to sign it with. Since we were able to generate the APK successfully you should already have the keytool as it will come with our Java installation. When executing this command we will pass a few arguments:

  • -genkey: Tell the keytool we want to generate a keystore
    • While genkey is still accepted and will be in future versions, Oracle notes it has been changed to genkeypair and that is the preferred usage
  • -v: Make our output verbose
    • This is NOT a required argument, but can be helpful for debugging if you run into any issues
  • -keystore: Filename to use when outputting the keystore file
  • -alias: A unique string to identify the keystore
  • -keyalg: Algorithm to use for generating the keystore
  • -keysize: Size of key to generate
  • -validity: Validity length in days of the keystore

Once we are ready to generate a keystore, we can run a command similar to this:

NOTES: - KEYSTORE_FILE_NAME and KEYSTORE_ALIAS are placeholders and should be replaced with your real values - During the generation process the keytool will ask you for a password to use with this keystore, DO NOT LOSE/FORGET THIS PASSWORD

$ keytool \
  -genkey -v \
  -keystore KEYSTORE_FILE_NAME \
  -alias KEYSTORE_ALIAS \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000

The successful output of this command should be our new, valid keystore file and we are now ready to sign our generated APK file from Step 1.

Step 3: Signing the APK

Now we have our generated APK file and keystore file, so we can sign the APK and deliver it to the store. To sign it with our keystore we can use the jarsigner. Like the keytool, this should come with your Java installation and should not require any additional installations. For this command we will pass a few arguments:

  • -verbose: Make our output verbose
    • Not required, but can be helpful for debugging
  • -sigalg SHA1withRSA: Override the default signature algorithm to SHA1withRSA to make sure our keystore is used properly since with generated it using RSA
  • -digestalg SHA1: Specify the message digest algorithm as SHA1
  • -keystore ABSOLUTE_KEYSTORE_PATH: Absolute path to our keystore to use for the signing process
  • -storepass 'KEYSTORE_PASSWORD': Password for our keystore
    • NOTE: I am only passing the password here so I can run the command without prompting for the password so this can be added in a build script if desired
  • STEP_1_GENERATED_APK_ABSOLUTE_PATH: Absolute path of our generated APK file from Step 1
  • KEYSTORE_ALIAS: Alias of our keystore from Step 2
$ jarsigner \
  -verbose \
  -sigalg SHA1withRSA \
  -digestalg SHA1 \
  -keystore ABSOLUTE_KEYSTORE_PATH \
  -storepass 'KEYSTORE_PASSWORD' \
  STEP_1_GENERATED_APK_ABSOLUTE_PATH \
  KEYSTORE_ALIAS

The successful output of this command should be a list of all the files saying it signed them and then a final jar signed. message at the end. If you got this then you are ready for the next step.

Step 4: Archive Alignment

At this point we have a signed APK file, but we still have one more step before we can publish to Google Play, and that is to use the zipalign tool to align our APK archive. For this command we will pass a few arguments:

  • -v: Verbose output
    • Not required, but can be helpful for debugging
  • 4: Integer that defines byte-alignment boundaries
    • The docs for zipalign this MUST be 4 otherwise the tool “effectivly does nothing” (their words)
  • STEP_1_GENERATED_APK_ABSOLUTE_PATH: The path to our generated (and signed in Step 3) APK
    • The jarsigner will keep the signed file in the same place, so we use the same path from Step 1
  • release.apk: The output file name for our “zipalign’d” APK
    • This does not have to be release.apk and should probably be name something that makes more sense for your project

NOTES: - zipalign might not be in your path, so you can either add it there or call it directly from the Android SDK build tools (something like $ANDROID_SDK_PATH/build-tools/$SDK_VERSION/zipalign) - If a file already exists at release.apk (or whatever output name you use) the tool WILL NOT overwrite anything but will instead fail

$ zipalign -v \
  4 \
  STEP_1_GENERATED_APK_ABSOLUTE_PATH \
  release.apk

The successful output of this command should list all the files saying they were compressed and the last line should output Verification successful.

Step 5: Uploading the APK to Google Play

At this stage we now have a properly signed and aligned APK file and are ready to upload to the store. From inside the Google Play Console you should be able to create a new release and upload this APK with your release notes then release it to the public (or your alpha/beta/internal team depending on your release type).