Firma APK senza inserire le informazioni sul keystore in build.gradle


152

Sto cercando di impostare il processo di firma in modo che la password del keystore e la password della chiave non vengano archiviate nel build.gradlefile del progetto .

Attualmente ho il seguente in build.gradle:

android {
    ...
    signingConfigs {
        release {
            storeFile file("my.keystore")
            storePassword "store_password"
            keyAlias "my_key_alias"
            keyPassword "key_password"
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release            
        }
    }
}

Funziona perfettamente, ma non devo inserire i valori per storePassword, e keyPasswordnel mio repository. Preferirei non mettere storeFilee keyAliasnemmeno lì.

C'è un modo per alterare in build.gradlemodo che ottenga le password da una fonte esterna (come un file che risiede solo sul mio computer)?

E, naturalmente, l'alterato build.gradledovrebbe essere utilizzabile su qualsiasi altro computer (anche se il computer non ha accesso alle password).

Sto usando Android Studio e in Mac OS X Maverics se è importante.


"E, naturalmente, il build.gradle modificato dovrebbe essere utilizzabile su qualsiasi altro computer (anche se il computer non ha accesso alle password)" - se i dati non sono presenti build.gradle, dovrai avere qualcosa di diverso da build.gradle, indipendentemente dal fatto che è un adattamento alle variabili di ambiente (per una risposta), un file delle proprietà (per un'altra risposta) o altri mezzi. Se non sei disposto ad avere cose al di fuori di build.gradle, allora per definizione tutte le informazioni sulla firma devono essere dentro buid.gradle .
CommonsWare,

2
@CommonsWare Hai ragione. Tuttavia, non ho detto che volevo avere qualcosa di strettamente all'interno di build.gradle. E ho detto che build.gradle poteva ottenere password da una fonte esterna (come un file che risiede solo sul mio computer
Bobrovsky,


Risposte:


120

La cosa bella di Groovy è che puoi mescolare liberamente il codice Java, ed è abbastanza facile da leggere in un file chiave / valore usando java.util.Properties. Forse c'è un modo ancora più semplice di usare Groovy idiomatico, ma Java è ancora piuttosto semplice.

Crea un keystore.propertiesfile (in questo esempio, nella directory principale del tuo progetto accanto settings.gradle, anche se puoi metterlo dove preferisci:

storePassword=...
keyPassword=...
keyAlias=...
storeFile=...

Aggiungi questo al tuo 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']
        }
    }
}

29
Ho dovuto rimuovere le citazioni dal mio keystore.properties
Jacob Tabak,

6
Non genera la versione firmata per me con il plugin versione 0.9. +. Cosa dovrei fare con il blocco signingConfigs e l'elemento buildTypes.release.signingConfig? rimuovili?
Fernando Gallego,

1
Sembra che l'impostazione storeFile su qualsiasi valore valido (ad es. storeFile file('AndroidManifest.xml')) E successivamente la sua sostituzione provochi il processo di firma.
miracle2k,

5
La costruzione genera un errore in Error:(24, 0) Could not find property 'android' on root project 'RootProjectName'cui la linea 24 è quella con il blocco if. L'aggiunta apply plugin: 'com.android.application'a root build.gradle fa fallire anche la compilazione. Che cosa sto facendo di sbagliato?
PhilLab,

2
Questo non funziona nell'anno 2018. Deve essere deprecato? Continuava a lanciare l'errore cheCould not get unknown property 'android' for root project
Neon Warge il

107

In alternativa, se si desidera applicare la risposta di Scott Barta in un modo più simile al codice di graduazione generato automaticamente, è possibile creare un keystore.propertiesfile nella cartella principale del progetto:

storePassword=my.keystore
keyPassword=key_password
keyAlias=my_key_alias
storeFile=store_file  

e modifica il tuo codice gradle per:

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

    ...

}

Puoi archiviare questo file delle proprietà nella radice del tuo modulo, nel qual caso basta ometterlo rootProjecte puoi anche modificare questo codice per avere diversi insiemi di proprietà per diversi keystore e alias chiave.


7
Funziona alla grande. if ( keystorePropertiesFile.exists() )Prima di tentare di ottenere gli attributi e provare a firmare, ero sicuro che il file fosse presente.
Joshua Pinter,

E non dimenticare di aggiungere l' .txtestensione alla fine del keystore.propertiesfile.
Levon Petrosyan,

11
Non dovresti avere bisogno di .txtun'estensione nel keystore.propertiesfile.
Matt Zukowski,

2
Sembra che queste informazioni siano state aggiunte qui - developer.android.com/studio/publish/…
Vadim Kotov

36

Il modo più semplice è creare un ~/.gradle/gradle.propertiesfile.

ANDROID_STORE_PASSWORD=hunter2
ANDROID_KEY_PASSWORD=hunter2

Quindi il tuo build.gradlefile può apparire così:

android {
    signingConfigs {
        release {
            storeFile file('yourfile.keystore')
            storePassword ANDROID_STORE_PASSWORD
            keyAlias 'youralias'
            keyPassword ANDROID_KEY_PASSWORD
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

1
Dovrei gitignore ~ ​​/ .gradle / gradle.properties?
vzhen,

Le istruzioni complete sono anche nella documentazione nativa di reazione.
Pencilcheck,

23

Dopo aver letto alcuni link:

http://blog.macromates.com/2006/keychain-access-from-shell/ http://www.thoughtworks.com/es/insights/blog/signing-open-source-android-apps-without-disclosing- Le password

Dato che stai utilizzando Mac OSX, puoi utilizzare Accesso Portachiavi per memorizzare le tue password.

Come aggiungere la password in Accesso Portachiavi

Quindi nei tuoi script gradle:

/* 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]
}

Usa così:

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

2
anche se la tua risposta si applica solo a Mac OSX, mi piace molto! Si noti che il secondo collegamento fornito contiene una soluzione di backup per altre piattaforme, nel caso in cui qualcuno debba implementare il supporto multipiattaforma.
Delblanco,

Potete per favore fornire la stessa soluzione anche per Linux e Windows? Grazie.
Jay Mungara,

18

Questo è come lo faccio. Usa variabili d'ambiente

  signingConfigs {
    release {
        storeFile file(System.getenv("KEYSTORE"))
        storePassword System.getenv("KEYSTORE_PASSWORD")
        keyAlias System.getenv("KEY_ALIAS")
        keyPassword System.getenv("KEY_PASSWORD")
    }

3
Sfortunatamente, ciò richiede la creazione di ambienti di sistema per ciascun progetto su ciascun computer . Altrimenti ricevo il seguente erroreNeither path nor baseDir may be null or empty string. path='null'
Bobrovsky il

@Bobrovsky So che questa domanda ha una risposta, ma puoi usare le variabili di ambiente di sistema o il file gradle.properties. Probabilmente vuoi usare il file gradle.properties. Puoi usarlo per più progetti.
Jared Burrows,

3
questo non funziona su MacOSX se non esegui Android Studio dalla riga di comando.
Henrique de Sousa,

Sono d'accordo con tutto quanto sopra. Ho la stessa configurazione e non puoi compilarla in Android Studio. È necessario eseguire dalla riga di comando affinché funzioni. Sto cercando un modo migliore, in modo da non dover commentare queste righe quando corro in Android Studio.
Sayooj Valsan,

@Bobrovsky: funziona su Windows ?. Dobbiamo menzionare tutto ciò negli ambienti di sistema?
DKV,

12

È possibile prendere qualsiasi progetto esistente di Android Studio Gradle e crearlo / firmarlo dalla riga di comando senza modificare alcun file. Questo lo rende molto piacevole per la memorizzazione del progetto nel controllo della versione mantenendo le chiavi e le password separate e non nel file build.gradle:

./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

9

La risposta accettata utilizza un file per controllare quale keystore utilizzare per firmare l'APK che risiede nella stessa cartella principale del progetto. Quando utilizziamo vcs come Git , potrebbe essere una brutta cosa quando dimentichiamo di aggiungere il file delle proprietà per ignorare l'elenco. Perché riveleremo la nostra password al mondo. I problemi persistono ancora.

Invece di creare un file delle proprietà nella stessa directory all'interno del nostro progetto, dovremmo farlo all'esterno. Lo facciamo fuori usando il file gradle.properties.

Ecco i passaggi:

1.Modifica o crea gradle.properties sul tuo progetto root e aggiungi il seguente codice, ricordati di modificare il percorso con il tuo:

AndroidProject.signing=/your/path/androidproject.properties  

2.Creare androidproject.properties in / your / path / e aggiungi il seguente codice, non dimenticare di cambiare /your/path/to/android.keystore nel percorso del tuo keystore:

STORE_FILE=/your/path/to/android.keystore  
STORE_PASSWORD=yourstorepassword  
KEY_ALIAS=yourkeyalias  
KEY_PASSWORD=yourkeypassword  

3.Nel modulo app build.gradle (non il root build.gradle del progetto) aggiungere il codice seguente se non esiste o modificarlo:

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

4. Aggiungere il seguente codice sotto il codice nel passaggio 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  
     }  
   }  

Questo codice cercherà la proprietà AndroidProject.signing in gradle.properties dal passaggio 1 . Se la proprietà viene trovata, tradurrà il valore della proprietà come percorso del file che punta a androidproject.properties che creiamo nel passaggio 2 . Quindi tutto il valore della proprietà da esso verrà utilizzato come configurazione della firma per il nostro build.gradle.

Ora non dobbiamo preoccuparci di nuovo del rischio di esporre la nostra password del keystore.

Maggiori informazioni su Firma dell'apk Android senza inserire le informazioni sul keystore in build.gradle


Questo funziona bene per me. solo per sapere perché stanno usando il file storeFile (System.getenv ("KEYSTORE"))
DKV il

9

Per quelli che cercano di mettere le loro credenziali in un file JSON esterno e di leggere che dal grado questo è quello che ho fatto:

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

Il motivo per cui ho scelto un .jsontipo di file, e non un .propertiestipo di file (come nella risposta accettata), è perché volevo anche archiviare altri dati (altre proprietà personalizzate di cui avevo bisogno) nello stesso file ( my_project/credentials.json) e avere ancora l'analisi gradle firma anche le informazioni dall'interno di quel file.


Sembra la soluzione migliore per me.
Aspirante Dev

4

Questa domanda ha ricevuto molte risposte valide, ma volevo condividere il mio codice che può essere utile per i manutentori della biblioteca , perché lascia l'originale build.gradleabbastanza pulito .

Aggiungo una cartella alla directory del modulo che ho gitignore. Sembra così:

/signing
    /keystore.jks
    /signing.gradle
    /signing.properties

keystore.jkse signing.propertiesdovrebbe essere autoesplicativo. E si signing.gradlepresenta così:

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]

E l'originale 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 ...
}

Come puoi vedere, non devi assolutamente specificare buildTypes, se l'utente ha accesso a una signingdirectory valida , la inserisce nel modulo e può creare un'applicazione di rilascio firmata valida, altrimenti funziona solo per lui come normalmente farebbe.


Mi piace molto questa soluzione. Nota, tuttavia, apply fromdovrebbe venire dopo il androidblocco
mgray88

0

Puoi richiedere le password dalla riga di comando:

...

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
  }

  ...
}

...

Questa risposta era precedentemente apparsa: https://stackoverflow.com/a/33765572/3664487


Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. - Dalla recensione
mkobit,

1
@mkobit, questo è un link ai contenuti su Stack Overflow! Potrei, ovviamente, copiare e incollare il contenuto collegato, ma ciò porta a contenuti duplicati. Quindi, presumo e mi correggo se sbaglio, pubblicare un link è la soluzione migliore. Qualsiasi argomento secondo cui "la pagina collegata cambia" dovrebbe essere respinto, sulla base del fatto che anche il contenuto qui potrebbe cambiare. Consiglio vivamente contro la tua soluzione per eliminare! Perché il contenuto collegato offre una soluzione eccellente.
user2768

Bene, penso che il problema sia che questa è ancora una risposta "solo link". Penso che la soluzione sia di pubblicarlo come commento, contrassegnare la domanda come duplicato o scrivere qui una nuova risposta che risolva il problema.
mkobit,

Le risposte "solo link" dovrebbero essere incoraggiate in alcuni casi. Tuttavia, ho seguito i tuoi consigli e i contenuti duplicati. (Il contenuto duplicato è chiaramente problematico, poiché alcuni contenuti potrebbero essere aggiornati, mentre il contenuto rimanente potrebbe non esserlo.)
user2768

In effetti, ho appena aggiornato la risposta e il contenuto duplicato sta causando problemi! Se esiste una politica contro le risposte solo link, allora dovrebbe essere adattato per considerare tali casi d'angolo.
user2768

0

La mia password conteneva un carattere speciale il cui simbolo di dollaro $ e dovevo scappare dal file gradle.properties. Successivamente, la firma ha funzionato per me.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.