Sign APK without putting keystore info in build.gradle

AndroidAndroid StudioGradleCode SigningApk Signing

Android Problem Overview


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.

Android Solutions


Solution 1 - Android

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']
        }
    }
}

Solution 2 - Android

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.

Solution 3 - Android

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
        }
    }
}

Solution 4 - Android

After reading a few links:

http://blog.macromates.com/2006/keychain-access-from-shell/ http://www.thoughtworks.com/es/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.

How to add password in Keychain Access

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
        }
    }
}

Solution 5 - Android

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")
    }

Solution 6 - Android

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

Solution 7 - Android

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.

Solution 8 - Android

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

Solution 9 - Android

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.

Solution 10 - Android

Inspired from https://stackoverflow.com/a/33218300/1673761 with some improvements

Pros

  • keystore properties (file name, alias, passwords) are stored outside of git
  • Debug mode still works even if we don't have a keystore file or keystore.properties
  • The keystore and its properties are in the root folder side by side

In the root of your project (or in android folder for React Native):

  • Add your generated keystore file ie: app.keystore
  • create a file named keystore.properties
  • Add these two files to .gitignore

In keystore.properties add

STORE_FILE=app.keystore
KEY_ALIAS=app_alias
STORE_PASSWORD=your_password
KEY_PASSWORD=your_password

In 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

Solution 11 - Android

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

Solution 12 - Android

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.

Solution 13 - Android

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.


  1. 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
            }
        }
    }
    
  2. 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
    

Solution 14 - Android

In `keystore.properties

    STORE_FILE = app.keystore
    KEY_ALIAS = app_alias
    STORE_PASSWORD = your_password
    KEY_PASSWORD = your_password

In 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
    }
  }
}

Solution 15 - Android

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:

  • Windows Command Prompt: setx SIGNING_KEY_ALIAS "My key"
  • Linux Terminal: export SIGNING_KEY_ALIAS="My key"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBobrovskyView Question on Stackoverflow
Solution 1 - AndroidScott BartaView Answer on Stackoverflow
Solution 2 - AndroidSilverCorvusView Answer on Stackoverflow
Solution 3 - AndroidDan FabulichView Answer on Stackoverflow
Solution 4 - AndroidJared BurrowsView Answer on Stackoverflow
Solution 5 - AndroidMadhur AhujaView Answer on Stackoverflow
Solution 6 - AndroidWayne PiekarskiView Answer on Stackoverflow
Solution 7 - AndroidSudoPlzView Answer on Stackoverflow
Solution 8 - Androidישו אוהב אותךView Answer on Stackoverflow
Solution 9 - AndroidMichał KlimczakView Answer on Stackoverflow
Solution 10 - AndroidabumalickView Answer on Stackoverflow
Solution 11 - Androiduser2768View Answer on Stackoverflow
Solution 12 - AndroidYogendra GhatpandeView Answer on Stackoverflow
Solution 13 - AndroidMattSchmattView Answer on Stackoverflow
Solution 14 - AndroidAnand DamodaranView Answer on Stackoverflow
Solution 15 - AndroidMahozadView Answer on Stackoverflow