Signing configs with gradle android
Signing apps
- Android apps required to digitally signed with a certificate before installing 
- Android identifies author of the app using this certificate, 
- This certificate need not to be signed by certificate authority(you can use self signed certificate) 
- There are two types of signing modes - debug modeand- release mode
- In debug mode, you sign your app with a debug certificate generated by the Android SDK tools. This certificate has a private key with a known password 
- This certificate locates on - $HOME/.android/debug.keystore
- In release mode you sign the app with your own certificate (actually you have to generate your own key store) 
- More information about signing android application 
 http://developer.android.com/tools/publishing/app-signing.html
Signing config
- In - gradlebased android projects, the signing configuration should be specified in the gradle build scripts
- Following are the details that need to be specify in gradle build script 
- keystore(location)
- keystore password
- key alias name
- key password
- The store type
- In debug version you don't need to specify any of these details, it automatically takes debug certificate details 
- But build system does not sign the release version unless you explicitly define a signing configuration for the build, so in release type you have to specify above details in gradle build file 
- Rest of this article gives you detailed information about specifying release signing config 
Directory structure
- Following is my project structure

- I have senzapp underScoreproject, I'm define signing configs inbuild.gradlefile insenzapp(since it is my main app)
Simply define release signing configs
- First copy release key-storein tosenzapp

- Then define key-store details in build.gradlefile(it need to add underandroidsection in build file)
android {
    ...
    signingConfigs {
        release {
            storeFile file("release-key.keystore")
            storePassword 'passwotd'
            keyAlias 'alias'
            keyPassword 'password'
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
        debug {
            debuggable true
        }
    }
}
...- Now execute following command from command line
./gradlew assembleRelease- It will generate release apk - - Score/senz/build/apk/senz-release.apk
- In above signing config I have defined all key-store and key passwords in my gradle file, it is not a good security practice 
- To solve the security issue, you can specify the build process prompt you for these passwords(If you are building from command line) 
Prompt password with gradle build file
- You can get the passwords from System.console()
storePassword System.console().readLine("\nKeystore password: ")
keyPassword System.console().readLIne("\nKey password: ")- So build file would be looks like below
android {
    ...
    signingConfigs {
        release {
            storeFile file("release-key.keystore")
            storePassword System.console().readLine("\nKeystore password: ")
            keyAlias 'alias'
            keyPassword System.console().readLIne("\nKey password: ")
        }
    }
    ...
}
...- When you build from command line it will prompt for the passwords

- There are several issues with this approach also
Problem 1 - Password will prompt every time
- This password will be prompt for every time, 
- For an instance if you clean the build - ./gradlew clean, it will ask for password

- To solve this issue we can use gradle - TaskExecutionGraph
 http://www.gradle.org/docs/current/javadoc/org/gradle/api/execution/TaskExecutionGraph.html
- By using the taskGraph we can restrict the password prompt for specific build task 
- So we can restrict the password prompting when only - assembleRelease
gradle.taskGraph.whenReady { taskGraph ->
    if(taskGraph.hasTask(':senz:assembleRelease')) {
        password = System.console().readPassword("\nEnter password: ")
        password = new String(password)
        if(password.size() <= 0) {
            throw new InvalidUserDataException("Empty password")
        }
        // set signing config key passwords
        android.signingConfigs.release.storePassword = password
        android.signingConfigs.release.keyPassword = password
    }
}
android {
    ...
    signingConfigs {
        release {
            storeFile file("release-key.keystore")
            storePassword ''
            keyAlias 'alias'
            keyPassword ''
        }
    }
    ...
}- This will ask for a password only assembleReleaseand assign the password into signing configs passwords(I'm using one password for both key-store and key so I can have same password for both values)

- In this code we are using - System.console().readPassword()instead of- readLine(),- readPassword()functions returns a char array. So need to convert it to String, otherwise build will fail
- SignConfigs.releasepasswords set to empty since we are read it via- taskGraph
android {
    ...
    signingConfigs {
        release {
            storeFile file("release-key.keystore")
            storePassword ''
            keyAlias 'alias'
            keyPassword ''
        }
    }
    ...
}Problem 2 - No Console
- If you run above build in Android Studio there is no System.console(), it will return null 
- So build will fail on AndroidStudio 
- In here instead of getting password from console we can prompt it from dialog when not having the console(actually when running the release build from AndroidStudio) 
- Following code will do it via - Groovy SwingBuilder
import groovy.swing.SwingBuilder
...
gradle.taskGraph.whenReady { taskGraph ->
    if(taskGraph.hasTask(':senz:assembleRelease')) {
        def password = ""
        if (System.console() == null) {
            new SwingBuilder().edt {
                dialog(modal: true,
                        title: "Enter password",
                        alwaysOnTop: true,
                        resizable: false,
                        locationRelativeTo: null,
                        pack: true,
                        show: true
                ) {
                    vbox {
                        label(text: "Enter password: ")
                        input = passwordField()
                        button(defaultButton: true, 
                                    text: 'OK', 
                                    actionPerformed: {
                            password = input.password;
                            dispose();
                        })
                    }
                }
            }
        } else {
            password = System.console().readPassword("\nEnter password: ")
            password = new String(password)
        }
        if (password.size() <= 0) {
            throw new InvalidUserDataException("Empty password")
        }
    }
}
android {
...
}- More about Groovy SwingBuilder 
 http://groovy.codehaus.org/GUI+Programming+with+Groovy
 http://groovy.codehaus.org/Swing+Builder
- Please note that, in mac above code can be raise an exception 
Error:(10) java.awt.AWTError: Toolkit not found: apple.awt.CToolkit
> Toolkit not found: apple.awt.CToolkit- I have uploaded my complete build.gradefile in to github https://github.com/erangaeb/dev-notes/blob/master/gradle/Score/senz/build.gradle
Reference
- http://developer.android.com/sdk/installing/studio-build.html
- http://developer.android.com/tools/publishing/app-signing.html
- http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Signing-Configurations
- https://www.timroes.de/2013/09/22/handling-signing-configs-with-gradle/
- https://www.timroes.de/2014/01/19/using-password-prompts-with-gradle-build-files/
- http://gmariotti.blogspot.com/2013/10/common-tips-about-gradle.html
Written by eranga bandara
Related protips
2 Responses
It's not good idea to store your keystore in project directory - you can use Gradle to fetch it from outside filesystem (outside of project directory of course)
 
Very good Post!
Yes agree with above comment. For Security reasons against reverse engineering it's not good practice to package your key in your project.
You can save your key in any directory in your computer, then point to it from gradle.
example:
 storeFile file("/Users/AndroidStudioProjects/Release_keys/My-release-key.jks")

 
 
 
