Masuk APK tanpa memasukkan info keystore di build.gradle

152

Saya mencoba mengatur proses penandatanganan sehingga kata sandi dan kata sandi kunci keystore tidak disimpan dalam file proyek build.gradle.

Saat ini saya memiliki yang berikut di build.gradle:

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

    buildTypes {
        release {
            signingConfig signingConfigs.release            
        }
    }
}

Ini berfungsi dengan baik tetapi saya tidak harus meletakkan nilai untuk storePassword, dan keyPassworddi repositori saya. Saya lebih suka untuk tidak menaruh storeFiledan di keyAliassana juga.

Apakah ada cara untuk mengubah build.gradlesehingga akan mendapatkan kata sandi dari beberapa sumber eksternal (seperti file yang hanya berada di komputer saya)?

Dan tentu saja, yang diubah build.gradleharus dapat digunakan di komputer lain (bahkan jika komputer tidak memiliki akses ke kata sandi).

Saya menggunakan Android Studio dan di Mac OS X Maverics jika itu penting.

Bobrovsky
sumber
"Dan tentu saja, build.gradle yang diubah harus dapat digunakan di komputer lain (bahkan jika komputer tidak memiliki akses ke kata sandi)" - jika data tidak ada build.gradle, Anda harus memiliki sesuatu selain build.gradle, apakah itu adalah penyesuaian ke variabel lingkungan (per satu jawaban), file properti (per jawaban lain), atau cara lain. Jika Anda tidak ingin memiliki hal-hal di luar build.gradle, maka menurut definisi semua informasi penandatanganan harus ada di dalam buid.gradle .
CommonsWare
2
@CommonsWare Anda benar. Tapi saya tidak memberi tahu saya ingin memiliki sesuatu yang ketat di dalam build.gradle. Dan saya memberi tahu bahwa build.gradle dapat memperoleh kata sandi dari beberapa sumber eksternal (seperti file yang hanya ada di komputer saya
Bobrovsky
Saya telah menandai sebagai duplikat dari stackoverflow.com/questions/18328730/… , berdasarkan meta.stackoverflow.com/questions/311044/…
user2768

Jawaban:

120

Yang menyenangkan tentang Groovy adalah Anda dapat dengan bebas mencampurkan kode Java, dan cukup mudah dibaca menggunakan file kunci / nilai java.util.Properties. Mungkin ada cara yang bahkan lebih mudah menggunakan Groovy idiomatik, tetapi Java masih cukup sederhana.

Buat keystore.propertiesfile (dalam contoh ini, di direktori root proyek Anda di sebelah settings.gradle, meskipun Anda dapat meletakkannya di mana pun Anda suka:

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

Tambahkan ini ke Anda 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']
        }
    }
}
Scott Barta
sumber
29
Saya harus menghapus tanda kutip dari keystore.properties
Jacob Tabak
6
Itu tidak menghasilkan versi yang ditandatangani untuk saya dengan plugin versi 0.9. +. Apa yang harus saya lakukan dengan blok SignConfigs dan item buildTypes.release.signingConfig? Hapus mereka?
Fernando Gallego
1
Tampaknya menetapkan storeFile ke nilai yang valid (mis. storeFile file('AndroidManifest.xml')) Dan kemudian menimpanya menyebabkan proses penandatanganan berlangsung.
miracle2k
5
Bangunan menghasilkan kesalahan di Error:(24, 0) Could not find property 'android' on root project 'RootProjectName'mana baris 24 adalah yang dengan blok if. Menambahkan apply plugin: 'com.android.application'ke root build.gradle juga membuat build gagal. Apa yang saya lakukan salah?
PhilLab
2
Ini tidak berfungsi pada tahun 2018. Ini harus dihentikan? Terus galat melempar ituCould not get unknown property 'android' for root project
Neon Warge
106

Atau, jika Anda ingin menerapkan jawaban Scott Barta dengan cara yang lebih mirip dengan kode grader yang dibuat secara otomatis, Anda dapat membuat keystore.propertiesfile di folder root proyek Anda:

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

dan modifikasi kode gradle Anda ke:

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

    ...

}

Anda dapat menyimpan file properti ini di root modul Anda, dalam hal ini hanya menghilangkan rootProject, dan Anda juga dapat memodifikasi kode ini untuk memiliki beberapa set properti untuk keystores dan alias kunci yang berbeda.

CurlyCorvus
sumber
7
Bagus sekali. Saya dulu if ( keystorePropertiesFile.exists() )memastikan file itu ada sebelum mencoba untuk mendapatkan atribut dan mencoba untuk masuk.
Joshua Pinter
Dan jangan lupa untuk menambahkan .txtekstensi di akhir keystore.propertiesfile.
Levon Petrosyan
11
Anda seharusnya tidak memerlukan .txtekstensi pada keystore.propertiesfile.
Matt Zukowski
2
Sepertinya info ini ditambahkan di sini - developer.android.com/studio/publish/…
Vadim Kotov
36

Cara termudah adalah membuat ~/.gradle/gradle.propertiesfile.

ANDROID_STORE_PASSWORD=hunter2
ANDROID_KEY_PASSWORD=hunter2

Maka build.gradlefile Anda dapat terlihat seperti ini:

android {
    signingConfigs {
        release {
            storeFile file('yourfile.keystore')
            storePassword ANDROID_STORE_PASSWORD
            keyAlias 'youralias'
            keyPassword ANDROID_KEY_PASSWORD
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}
Dan Fabulich
sumber
1
Haruskah saya gitignore ~ ​​/ .gradle / gradle.properties?
vzhen
Instruksi lengkapnya juga dalam bentuk dokumentasi asli.
Pencilcheck
23

Setelah membaca beberapa tautan:

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

Karena Anda menggunakan Mac OSX, Anda dapat menggunakan Akses Keychain untuk menyimpan kata sandi Anda.

Bagaimana cara menambahkan kata sandi di Akses Keychain

Kemudian dalam skrip gradle Anda:

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

Gunakan seperti ini:

getPassword (pengguna saat ini, "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
        }
    }
}
Jared Burrows
sumber
2
walaupun jawaban Anda hanya berlaku untuk Mac OSX, saya sangat menyukainya! Perhatikan bahwa tautan kedua yang Anda berikan berisi solusi cadangan untuk platform lain, jika seseorang perlu menerapkan dukungan multi-platform.
Delblanco
Bisakah Anda memberikan solusi yang sama untuk linux dan windows juga? Terima kasih.
Jay Mungara
18

Beginilah cara saya melakukannya. Gunakan Variabel Lingkungan

  signingConfigs {
    release {
        storeFile file(System.getenv("KEYSTORE"))
        storePassword System.getenv("KEYSTORE_PASSWORD")
        keyAlias System.getenv("KEY_ALIAS")
        keyPassword System.getenv("KEY_PASSWORD")
    }
Madhur Ahuja
sumber
3
Sayangnya, ini membutuhkan lingkungan sistem yang harus dibuat untuk setiap proyek di setiap komputer . Kalau tidak, saya mendapatkan kesalahan berikutNeither path nor baseDir may be null or empty string. path='null'
Bobrovsky
@ Bobrovsky Saya tahu pertanyaan ini dijawab, tetapi Anda dapat menggunakan variabel lingkungan sistem atau file gradle.properties. Anda mungkin ingin menggunakan file gradle.properties. Anda dapat menggunakannya untuk beberapa proyek.
Jared Burrows
3
ini tidak berfungsi di MacOSX kecuali Anda menjalankan Android Studio dari baris perintah.
Henrique de Sousa
Saya setuju dengan semua hal di atas. Saya memiliki konfigurasi yang sama dan Anda tidak dapat mengompilasinya di Android studio. Anda perlu menjalankan dari baris perintah agar ini berfungsi. Saya mencari cara yang lebih baik, sehingga saya tidak perlu mengomentari baris ini ketika saya menjalankan di Android Studio.
Sayooj Valsan
@OBrovsky: Apakah ini berfungsi pada windows ?. Haruskah kita menyebutkan semua ini di lingkungan sistem?
DKV
12

Dimungkinkan untuk mengambil proyek gradasi Android Studio yang ada dan membangun / menandatanganinya dari baris perintah tanpa mengedit file apa pun. Ini membuatnya sangat bagus untuk menyimpan proyek Anda dalam kontrol versi sambil menjaga kunci dan kata sandi Anda terpisah dan tidak dalam file build.gradle Anda:

./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
Wayne Piekarski
sumber
9

Jawaban yang diterima menggunakan file untuk mengontrol keystore mana yang akan digunakan untuk menandatangani APK yang berada di folder root proyek yang sama. Ketika kita menggunakan vcs seperti Git , bisa jadi hal yang buruk ketika kita lupa menambahkan file properti ke daftar abaikan. Karena kami akan mengungkapkan kata sandi kami kepada dunia. Masalahnya masih ada.

Alih-alih membuat file properti di direktori yang sama dalam proyek kami, kita harus membuatnya di luar. Kami membuatnya di luar dengan menggunakan file gradle.properties.

Berikut langkah-langkahnya:

1. Edit atau buat gradle.properties pada proyek root Anda dan tambahkan kode berikut, ingatlah untuk mengedit lintasan dengan Anda sendiri:

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

2.Buat properti androidproject.prop di / path / Anda / dan tambahkan kode berikut ke dalamnya, jangan lupa untuk mengubah /your/path/to/android.keystore ke jalur keystore Anda:

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

3.Dalam build.gradle modul aplikasi Anda (bukan build.gradle root proyek Anda) tambahkan kode berikut jika tidak ada atau sesuaikan dengannya:

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

4.Tambahkan kode berikut di bawah kode pada langkah 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  
     }  
   }  

Kode ini akan mencari properti AndroidProject.signing di gradle.properties dari langkah 1 . Jika properti ditemukan, itu akan menerjemahkan nilai properti sebagai path file yang menunjuk ke androidproject.properties yang kita buat di langkah 2 . Maka semua nilai properti darinya akan digunakan sebagai konfigurasi penandatanganan untuk build.gradle kami.

Sekarang kita tidak perlu khawatir lagi akan risiko mengekspos kata sandi kami.

Baca lebih lanjut di Signing Android apk tanpa memasukkan informasi keystore di build.gradle

ישו אוהב אותך
sumber
Ini bekerja baik untuk saya. hanya untuk mengetahui mengapa mereka menggunakan file storeFile (System.getenv ("KEYSTORE"))
DKV
9

Bagi yang ingin memasukkan kredensial mereka dalam file JSON eksternal dan membacanya dari gradle, inilah yang saya lakukan:

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

Alasan saya memilih .jsontipe file, dan bukan .propertiestipe file (seperti pada jawaban yang diterima), adalah karena saya ingin juga menyimpan data lain (properti khusus lain yang saya butuhkan) ke file yang sama ( my_project/credentials.json), dan masih harus mengurai parse menandatangani informasi dari dalam file itu juga.

SudoPlz
sumber
Sepertinya solusi terbaik bagi saya.
Aspiring Dev
4

Pertanyaan ini telah menerima banyak jawaban yang valid, tetapi saya ingin membagikan kode saya yang mungkin berguna bagi pengelola perpustakaan , karena meninggalkan yang asli build.gradlecukup bersih .

Saya menambahkan folder ke direktori modul yang saya gitignore. Ini terlihat seperti ini:

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

keystore.jksdan signing.propertiesharus jelas. Dan signing.gradleterlihat seperti ini:

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]

Dan yang asli 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 ...
}

Seperti yang Anda lihat, Anda tidak perlu menentukan buildTypes sama sekali, jika pengguna memiliki akses ke signingdirektori yang valid , ia hanya memasukkannya ke dalam modul dan ia dapat membangun aplikasi rilis yang ditandatangani yang valid, jika tidak, itu hanya berfungsi untuknya seperti itu biasanya akan dilakukan.

Michał K
sumber
Saya sangat suka solusi ini. Namun apply fromperlu dicatat, harus datang setelah androidblok
mgray88
0

Anda dapat meminta kata sandi dari baris perintah:

...

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
  }

  ...
}

...

Jawaban ini sebelumnya muncul: https://stackoverflow.com/a/33765572/3664487

pengguna2768
sumber
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini dan memberikan tautan untuk referensi. Jawaban hanya tautan dapat menjadi tidak valid jika halaman tertaut berubah. - Dari Ulasan
mkobit
1
@mkobit, ini adalah tautan ke konten di Stack Overflow! Saya tentu saja dapat menyalin dan menempelkan konten yang ditautkan, tetapi itu mengarah pada duplikat konten. Jadi, saya berasumsi, dan memperbaiki saya jika saya salah, memposting tautan adalah solusi terbaik. Argumen apa pun yang "perubahan halaman tertaut" harus diberhentikan, dengan alasan bahwa konten di sini juga dapat berubah. Saya sangat merekomendasikan agar solusi Anda tidak dihapus! Karena konten yang tertaut memberikan solusi yang sangat baik.
user2768
Yah, saya pikir masalahnya adalah bahwa ini masih merupakan jawaban "hanya tautan". Saya pikir solusinya adalah mempostingnya sebagai komentar, menandai pertanyaan sebagai duplikat, atau menulis jawaban baru di sini yang membahas masalah tersebut.
mkobit
Jawaban "khusus tautan" harus didorong dalam beberapa kasus. Meskipun demikian, saya telah mengikuti saran Anda dan konten duplikat. (Konten duplikat jelas bermasalah, karena beberapa konten mungkin diperbarui, sedangkan konten yang tersisa mungkin tidak.)
user2768
Memang, saya baru saja memperbarui jawaban dan duplikat konten menyebabkan masalah! Jika ada kebijakan terhadap jawaban hanya tautan, maka harus disesuaikan untuk mempertimbangkan kasus sudut tersebut.
user2768
0

Kata sandi saya berisi karakter khusus yang masuk dolar $ dan saya harus menghindarinya dalam file gradle.properties. Setelah itu, penandatanganan bekerja untuk saya.

Yogendra Ghatpande
sumber