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 mode
andrelease 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
gradle
based android projects, the signing configuration should be specified in the gradle build scriptsFollowing 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
senz
app underScore
project, I'm define signing configs inbuild.gradle
file insenz
app(since it is my main app)
Simply define release signing configs
- First copy release
key-store
in tosenz
app
- Then define key-store details in
build.gradle
file(it need to add underandroid
section 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.htmlBy 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
assembleRelease
and 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 ofreadLine()
,readPassword()
functions returns a char array. So need to convert it to String, otherwise build will failSignConfigs.release
passwords set to empty since we are read it viataskGraph
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+BuilderPlease 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.grade
file 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")