Reputation: 14236
I am trying to setup signing process so that keystore, password, and key password are not stored in the project's build.gradle
file.
Currently I have the following in the build.gradle
:
android {
...
signingConfigs {
release {
storeFile file("my.keystore")
storePassword "store_password"
keyAlias "my_key_alias"
keyPassword "key_password"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
It works perfectly fine but I must not put the values for the storePassword
, and keyPassword
in my repository. I would prefer to not put storeFile
and keyAlias
there either.
Is there a way to alter the build.gradle
so that it will obtain passwords from some external source (like a file that resides on my computer only)?
And of course, the altered build.gradle
should be usable on any other computer (even if the computer doesn't have access to passwords).
I am using Android Studio and in Mac OS X Maverics if it does matter.
Upvotes: 201
Views: 118556
Reputation: 55517
After reading a few links:
http://blog.macromates.com/2006/keychain-access-from-shell/ https://www.thoughtworks.com/en-gb/insights/blog/signing-open-source-android-apps-without-disclosing-passwords
Since you are using Mac OSX, you can use the Keychain Access to store your passwords.
Then in your gradle scripts:
/* Get password from Mac OSX Keychain */
def getPassword(String currentUser, String keyChain) {
def stdout = new ByteArrayOutputStream()
def stderr = new ByteArrayOutputStream()
exec {
commandLine 'security', '-q', 'find-generic-password', '-a', currentUser, '-gl', keyChain
standardOutput = stdout
errorOutput = stderr
ignoreExitValue true
}
//noinspection GroovyAssignabilityCheck
(stderr.toString().trim() =~ /password: '(.*)'/)[0][1]
}
Use like this:
getPassword(currentUser, "Android_Store_Password")
/* Plugins */
apply plugin: 'com.android.application'
/* Variables */
ext.currentUser = System.getenv("USER")
ext.userHome = System.getProperty("user.home")
ext.keystorePath = 'KEY_STORE_PATH'
/* Signing Configs */
android {
signingConfigs {
release {
storeFile file(userHome + keystorePath + project.name)
storePassword getPassword(currentUser, "ANDROID_STORE_PASSWORD")
keyAlias 'jaredburrows'
keyPassword getPassword(currentUser, "ANDROID_KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
Upvotes: 25
Reputation: 24472
This is another answer for Kotlin build scripts (build.gradle.kts) different from Anand Damodaran's answer.
It tries to read from local.properties file, falling back to the OS environment variables. It can be especially useful in CIs like GitHub Actions (you can create environment secrets in your repository settings).
Note that I'm using Kotlin 1.6.10 and Gradle 7.4.2 and Android Gradle Plugin (AGP) 7.0.4.
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
// ...
val environment = System.getenv()
fun getLocalProperty(key: String) = gradleLocalProperties(rootDir).getProperty(key)
fun String.toFile() = File(this)
android {
signingConfigs {
create("MySigningConfig") {
keyAlias = getLocalProperty("signing.keyAlias") ?: environment["SIGNING_KEY_ALIAS"] ?: error("Error!")
storeFile = (getLocalProperty("signing.storeFile") ?: environment["SIGNING_STORE_FILE"] ?: error("Error!")).toFile()
keyPassword = getLocalProperty("signing.keyPassword") ?: environment["SIGNING_KEY_PASSWORD"] ?: error("Error!")
storePassword = getLocalProperty("signing.storePassword") ?: environment["SIGNING_STORE_PASSWORD"] ?: error("Error!")
enableV1Signing = true
enableV2Signing = true
}
}
buildTypes {
release {
signingConfig = signingConfigs["MySigningConfig"]
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
}
As said, you can either have a local.properties file at the root of your project with values for the properties:
signing.keyAlias=My key
signing.keyPassword=zyxwvuts
signing.storePassword=abcdefgh
signing.storeFile=C\:\\Users\\Mahozad\\keystore.jks
... or you can set/create environment variables on your OS; for example to create an environment variable called SIGNING_KEY_ALIAS
run:
setx SIGNING_KEY_ALIAS "My key"
export SIGNING_KEY_ALIAS="My key"
Upvotes: 0
Reputation: 11
STORE_FILE = app.keystore
KEY_ALIAS = app_alias
STORE_PASSWORD = your_password
KEY_PASSWORD = your_password
app/build.gradle.kts
add...
android {
...
signingConfigs {
create("release") {
// Load keystore
val keystoreProperties = Properties().apply{
load(File("keystore.properties").reader())
}
storeFile = File(keystoreProperties.getProperty("STORE_FILE"))
storePassword = keystoreProperties.getProperty("STORE_PASSWORD")
keyAlias = keystoreProperties.getProperty("KEY_ALIAS")
keyPassword= keystoreProperties.getProperty("KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
Upvotes: 1
Reputation: 900
Unless you are using CI tools storing your credentials in cleartext in a text file is a bad idea, even if the file is not under version control.
I know this is common practice (even official docs mention it) but you wouldn't even think of storing the password of your personal email in a textfile, why would you do it with your app(s)?
My approach is to have a simple command that queries both passwords and builds the release while keeping my build.gradle as clean as possible. I.e.:
Key store password:
Key password:
I had a look at other answers but they either a) store credentials as cleartext b) didn't work or c) overcomplicate stuff.
Add this signing config to build.gradle:
android {
...
signingConfigs {
release {
storeFile file(findProperty('keystore_path') ?: 'default path')
storePassword findProperty('keystore_pw')
keyAlias findProperty('key_alias')
keyPassword findProperty('key_pw')
}
}
...
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}
Run this command in the project dir (edit /PATH/TO/KEYSTORE.jks
and KEY_ALIAS
):
echo -n Key store password: && read -s storepw && echo && \
echo -n Key password: && read -s keypw && echo && \
./gradlew assembleRelease -Pkeystore_path='/PATH/TO/KEYSTORE.jks' -Pkeystore_pw=$storepw -Pkey_alias='KEY_ALIAS' -Pkey_pw=$keypw
Upvotes: 1
Reputation: 2346
Inspired from https://stackoverflow.com/a/33218300/1673761 with some improvements
android
folder for React Native):app.keystore
keystore.properties
.gitignore
keystore.properties
addSTORE_FILE=app.keystore
KEY_ALIAS=app_alias
STORE_PASSWORD=your_password
KEY_PASSWORD=your_password
app/build.gradle
add// Load keystore
def keystoreProperties = new Properties()
try {
def keystorePropertiesFile = rootProject.file("keystore.properties");
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
} catch(IOException e) {
// We don't have release keys, ignoring
}
...
android {
...
signingConfigs {
release {
if (keystoreProperties['STORE_FILE']) {
storeFile rootProject.file(keystoreProperties['STORE_FILE'])
storePassword keystoreProperties['STORE_PASSWORD']
keyAlias keystoreProperties['KEY_ALIAS']
keyPassword keystoreProperties['KEY_PASSWORD']
}
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
PS: Edits to improve the groovy logic are welcome
Upvotes: 5
Reputation: 221
My password contained a special character which dollar sign $ and I had to escape that in gradle.properties file. After that, signing worked for me.
Upvotes: 0
Reputation: 13154
This question has received many valid answers, but I wanted to share my code which may be useful for library maintainers, because it leaves the original build.gradle
quite clean.
I add a folder to the module directory which I gitignore
. It looks like this:
/signing
/keystore.jks
/signing.gradle
/signing.properties
keystore.jks
and signing.properties
should be self explanatory. And signing.gradle
looks like this:
def propsFile = file('signing/signing.properties')
def buildType = "release"
if (!propsFile.exists()) throw new IllegalStateException("signing/signing.properties file missing")
def props = new Properties()
props.load(new FileInputStream(propsFile))
def keystoreFile = file("signing/keystore.jks")
if (!keystoreFile.exists()) throw new IllegalStateException("signing/keystore.jks file missing")
android.signingConfigs.create(buildType, {
storeFile = keystoreFile
storePassword = props['storePassword']
keyAlias = props['keyAlias']
keyPassword = props['keyPassword']
})
android.buildTypes[buildType].signingConfig = android.signingConfigs[buildType]
And the original build.gradle
apply plugin: 'com.android.application'
if (project.file('signing/signing.gradle').exists()) {
apply from: 'signing/signing.gradle'
}
android {
compileSdkVersion 27
defaultConfig {
applicationId ...
}
}
dependencies {
implementation ...
}
As you can see, you don't have to specify the buildTypes at all, if user has access to a valid signing
directory, he just puts it in the module and he can build a valid signed release application, otherwise it just works for him like it would normally do.
Upvotes: 8
Reputation: 80010
The nice thing about Groovy is that you can freely mix Java code, and it's pretty easy to read in a key/value file using java.util.Properties
. Perhaps there's an even easier way using idiomatic Groovy, but Java is still pretty simple.
Create a keystore.properties
file (in this example, in the root directory of your project next to settings.gradle
, though you can put it wherever you like:
storePassword=...
keyPassword=...
keyAlias=...
storeFile=...
Add this to your build.gradle
:
allprojects {
afterEvaluate { project ->
def propsFile = rootProject.file('keystore.properties')
def configName = 'release'
if (propsFile.exists() && android.signingConfigs.hasProperty(configName)) {
def props = new Properties()
props.load(new FileInputStream(propsFile))
android.signingConfigs[configName].storeFile = file(props['storeFile'])
android.signingConfigs[configName].storePassword = props['storePassword']
android.signingConfigs[configName].keyAlias = props['keyAlias']
android.signingConfigs[configName].keyPassword = props['keyPassword']
}
}
}
Upvotes: 144
Reputation: 23173
For the ones looking to put their credentials in an external JSON file and read that from the gradle this is what I did:
my_project/credentials.json:
{
"android": {
"storeFile": "/path/to/acuity.jks",
"storePassword": "your_store_password",
"keyAlias": "your_android_alias",
"keyPassword": "your_key_password"
}
}
my_project/android/app/build.gradle
// ...
signingConfigs {
release {
def credsFilePath = file("../../credentials.json").toString()
def credsFile = new File(credsFilePath, "").getText('UTF-8')
def json = new groovy.json.JsonSlurper().parseText(credsFile)
storeFile file(json.android.storeFile)
storePassword = json.android.storePassword
keyAlias = json.android.keyAlias
keyPassword = json.android.keyPassword
}
...
buildTypes {
release {
signingConfig signingConfigs.release //I added this
// ...
}
}
}
// ...
}
The reason I chose a .json
file type, and not a .properties
file type (as in the accepted answer), is because I wanted to also store other data (other custom properties I needed) to that same file (my_project/credentials.json
), and still have gradle parse the signing information from within that file as well.
Upvotes: 15
Reputation: 3232
It is possible to take any existing Android Studio gradle project and build/sign it from the command line without editing any files. This makes it very nice for storing your project in version control while keeping your keys and passwords separate and not in your build.gradle file:
./gradlew assembleRelease -Pandroid.injected.signing.store.file=$KEYFILE -Pandroid.injected.signing.store.password=$STORE_PASSWORD -Pandroid.injected.signing.key.alias=$KEY_ALIAS -Pandroid.injected.signing.key.password=$KEY_PASSWORD
Upvotes: 21
Reputation: 3076
Alternatively, if you want to apply Scott Barta's answer in a way more similar to the auto generated gradle code, you can create a keystore.properties
file in your project root folder:
storePassword=my.keystore
keyPassword=key_password
keyAlias=my_key_alias
storeFile=store_file
and modify your gradle code to:
// Load keystore
def keystorePropertiesFile = rootProject.file("keystore.properties");
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
...
android{
...
signingConfigs {
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
...
}
You can store this properties file in the root of your module, in which case just omit rootProject
, and you can also modify this code to have several sets of properties for different keystores and key aliases.
Upvotes: 154
Reputation: 29783
The accepted answer use a file to controls which keystore to use to sign the APK that resides in the same root folder of project. When we using vcs like Git, could be a bad thing when we forget to add the properties file to ignore list. Because we will disclose our password to the world. The problems still persist.
Instead making properties file in the same directory within our project, we should make it outside. We make it outside by using gradle.properties file.
Here the steps:
1.Edit or create gradle.properties on your root project and add the following code, remember to edit the path with your own:
AndroidProject.signing=/your/path/androidproject.properties
2.Create androidproject.properties in /your/path/ and add the following code to it, don't forget to change /your/path/to/android.keystore to your keystore path:
STORE_FILE=/your/path/to/android.keystore
STORE_PASSWORD=yourstorepassword
KEY_ALIAS=yourkeyalias
KEY_PASSWORD=yourkeypassword
3.In your app module build.gradle (not your project root build.gradle) add the following code if not exist or adjust to it:
signingConfigs {
release
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
4.Add the following code below the code in step 3:
if (project.hasProperty("AndroidProject.signing")
&& new File(project.property("AndroidProject.signing").toString()).exists()) {
def Properties props = new Properties()
def propFile = new File(project.property("AndroidProject.signing").toString())
if(propFile.canRead()) {
props.load(new FileInputStream(propFile))
if (props!=null && props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
} else {
println 'androidproject.properties found but some entries are missing'
android.buildTypes.release.signingConfig = null
}
} else {
println 'androidproject.properties file not found'
android.buildTypes.release.signingConfig = null
}
}
This code will search for AndroidProject.signing property in gradle.properties from step 1. If the property found, it will translate property value as file path which pointing to androidproject.properties that we create in step 2. Then all the property value from it will be used as signing configuration for our build.gradle.
Now we don't need to worry again of risk of exposing our keystore password.
Read more at Signing Android apk without putting keystore info in build.gradle
Upvotes: 10
Reputation: 39553
The easiest way is to create a ~/.gradle/gradle.properties
file.
ANDROID_STORE_PASSWORD=hunter2
ANDROID_KEY_PASSWORD=hunter2
Then your build.gradle
file can look like this:
android {
signingConfigs {
release {
storeFile file('yourfile.keystore')
storePassword ANDROID_STORE_PASSWORD
keyAlias 'youralias'
keyPassword ANDROID_KEY_PASSWORD
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
Upvotes: 44
Reputation: 824
You can request passwords from the command line:
...
signingConfigs {
if (gradle.startParameter.taskNames.any {it.contains('Release') }) {
release {
storeFile file("your.keystore")
storePassword new String(System.console().readPassword("\n\$ Enter keystore password: "))
keyAlias "key-alias"
keyPassword new String(System.console().readPassword("\n\$ Enter keys password: "))
}
} else {
//Here be dragons: unreachable else-branch forces Gradle to create
//install...Release tasks.
release {
keyAlias 'dummy'
keyPassword 'dummy'
storeFile file('dummy')
storePassword 'dummy'
}
}
}
...
buildTypes {
release {
...
signingConfig signingConfigs.release
}
...
}
...
This answer previously appeared: https://stackoverflow.com/a/33765572/3664487
Upvotes: 0
Reputation: 22661
This is how I do it. Use Environment Variables
signingConfigs {
release {
storeFile file(System.getenv("KEYSTORE"))
storePassword System.getenv("KEYSTORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
Upvotes: 22