Where to Store Android KeyStore File in CI/CD Cycle?

Where to Store Android KeyStore File in CI/CD Cycle?

https://medium.com/@farukcankaya/where-to-store-android-keystore-file-in-ci-cd-cycle-2365f4e02e57

@farukcankaya/ciappsigningexample

In the previous post, I mentioned Android Automated Build with Circle CI. When I wrote that post, we stored keystore(.jks) file as is in version control system. I know it’s huge security risk but it was the fastest way to run CI process and repository was private. In this post, I’ll talk about app signing process and how and why we secure keystore files in CI.

What is the keystore file?

Android requires that all APKs be digitally signed with a certificate before they can be installed. In this way, Android ensures that any future updates to your app are authentic and come from the original author. The key used to create this certificate is called the app signing key. This app signing key is stored in a binary file named keystore.

If you don’t do anything for signing, Android Studio automatically creates the debug keystore in $HOME/.android/debug.keystore, and sets the keystore and credentials. Keystore files are different for each machine. Let’s say, you create an APK in machine A and install it to your phone. Then, clone the project to machine B and create an APK from same project. You cannot update the application on your phone with APK generated from machine B. Because, APK is signed with a different certificate, keystore files under $HOME/.android directory are different on machine A and B. If you develop android project with a team, it’s a problem. In addition, you need to use same keystore file if you work with Google Map APIs, cause we generates fingerprint from keystore files. Google Map APIs ensures that requests are come from authentic author in that way. To use same keystore for all team members or CI tools, we add sigingconfigs to app level .gradle file:

android {
    ...
    signingConfigs {
        release {
            keyAlias 'keyAlias'
            keyPassword 'keyPassword'
            storeFile 'keystore.jks'
            storePassword 'storePassword'
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            ...
        }
    }
    ...
}

If you don’t have keystore file, you can create one with following instructionshere. You will define keyAlias, keyPassword and storePassword yourself while creating keystore.

Now, we use single keystore file to sign our APK digitally. If you lost this keystore file or forget the credentials, you cannot update your application anymore. On the other hand, if an attacker stoles your keystore file, s/he can update your application with a totally different one. Therefore, you should not push keystore files to version control system and you should not share it with anyone. Also, you need to hide keystore credentials from signing config in gradle file :)

How we can secure keystore file and credentials?

As we learned, Android application needs to be signed with a keystore in building process. Also, we shouldn’t store keystore file and credentials in version control system. We can hide credentials with creating keystore.properties file and using credentials from this file in signing config part like this:

def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
    ...
    
    signingConfigs {
        release {
            keyAlias keystoreProperties['releaseKeyAlias']
            keyPassword keystoreProperties['releaseKeyPassword']
            storeFile file(rootDir.getCanonicalPath() + '/' + keystoreProperties['releaseKeyStore'])
            storePassword keystoreProperties['releaseStorePassword']
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
    
    ...
}

keystore.properties contains keystore credentials:

releaseKeyAlias=Prod
releaseKeyPassword=12345678
releaseKeyStore=release.keystore.jks
releaseStorePassword=12345678

In the siging config, release.keystore.jks keystore file is used but we will not store it the vcs. So, we need to generate keystore file before building android application in continuous integration.

Since, Google App Signing announced in this article, if you distribute your application to Google Play Store you don’t need to keystore file. Google secures keystore file instead of us. But still, we need to manage keystore files ourselves while building application for development. So, we can think app signing as in two parts: continuous integration and continuous deployment.


Continuous Integration

In this part, if you distribute your apk on different channel like Fabric, you need the keystore file in building process in CI or in your local machine.

Signing an app when you manage your own app signing key(Original: https://goo.gl/7K1p9A)

Developer has keystore file and app signing keys that are visible in build.gradle. I’ve listed three solutions to secure our keystore file:

  • Encode keystore file as an environment variable:
    Encode keystore(.jks) file as base64
    openssl base64 -A -in keystore.jks
    This command will give the keystore file as base-64 encoded string. You can add this string to your CI tools as an environment variable. Then, you can decode this string to keystore file again before building your android project with command below:
    echo $DEBUG_KEYSTORE_BASE64 | base64 --decode > keystore.jks

    You can find detailed implementation here:

farukcankaya/ciappsigningexample
Contribute to farukcankaya/ciappsigningexample development by creating an account on GitHub.github.com

  • Store encrypted keystore file in version control system:
    First, create a secret key for yourself:
    openssl enc -aes-256-cbc -k ciappsigningexample-secret -P -md sha1
    It generates saltkey and iv. We’ll use 256-bit secret key.
    Then, encrypt your keystore file with your 256-bit secret key:
    openssl aes-256-cbc -e -in debug.keystore.jks -out debug.keystore.jks.encrypted -k 99924EB44...
    It will generate debug.keystore.jks.encrypted file and you can push this file to git. To decrypt your encrypted keystore in CI, you need to add your 256-bit secret key to CI as an environment. Then you can decrypt keystore file with using this secret key in pre-build step with following command:
    openssl aes-256-cbc -d -in debug.keystore.jks.encrypted -k $DEBUG_ENCRYPT_SECRET_KEY >> debug.keystore.jks

    You can find detailed implementation here:

farukcankaya/ciappsigningexample
Contribute to farukcankaya/ciappsigningexample development by creating an account on GitHub.github.com

  • Download keystore from external source like AWS S3, Google Drive
    If you don’t want to store keystore file in CI tools or version control system, you can download keystore file to project directory on pre-build step in CI. You need to take API key from file storage service to authorize your download request. Then, store API key as an environment variable in CI and you can download keystore file in pre-build step in CI. File storage services generally provide secure transfer protocol which means files are downloaded from url starts with https:// but I’ll recommend that store keystore file encrypted. After download your encrypted keystore, you can decrypt it and sign your APK like mentioned above.

Continuous Deployment

In this part, best thing is using App Signing by Google Play. With Google App Signing, you don’t have to keep the keystore file and app signing keys. Google manages and protects your app’s signing key for you and uses it to sign your APKs for distribution. When using App Signing by Google Play, two keys are used: the app signing key and the upload key. You just use the upload key to upload APKs to Google Play Store.

Signing an app with App Signing by Google Play (Original: https://goo.gl/7K1p9A)

Keystore file is stored and secured in Google play. Your APKs will be signed by Google Play with app signing key and published to users. Even if you lost your upload key you can contact with Google and you can update your application after validating your account. The other way, when you manage your signing keys yourself like in development part, if you lost the keystore file or forgot your keystore password, you cannot update your application anymore.

References

If you have any comment, question, or recommendation, feel free to drop them below. And, follow me on twitter @farukcnky

猜你喜欢

转载自blog.csdn.net/ultrapro/article/details/84333522