Android 签署 APK 而不将密钥库信息放入 build.gradle

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20562189/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:02:48  来源:igfitidea点击:

Sign APK without putting keystore info in build.gradle

androidgradleandroid-studiocode-signing

提问by Bobrovsky

I am trying to setup signing process so that keystore password and key password are notstored in the project's build.gradlefile.

我正在尝试设置签名过程,以便密钥库密码和密钥密码存储在项目build.gradle文件中。

Currently I have the following in the build.gradle:

目前我有以下内容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 notput the values for the storePassword, and keyPasswordin my repository. I would prefer to not put storeFileand keyAliasthere either.

它工作得很好,但我不能storePassword, 和的值keyPassword放在我的存储库中。我也不想把storeFile和放在keyAlias那里。

Is there a way to alter the build.gradleso that it will obtain passwords from some external source (like a file that resides on my computer only)?

有没有办法改变build.gradle它,以便它从某些外部来源(例如仅驻留在我的计算机上的文件)获取密码?

And of course, the altered build.gradleshould be usable on any other computer (even if the computer doesn't have access to passwords).

当然,更改后的内容build.gradle应该可以在任何其他计算机上使用(即使该计算机无法访问密码)。

I am using Android Studio and in Mac OS X Maverics if it does matter.

如果确实重要,我正在使用 Android Studio 和 Mac OS X Maverics。

采纳答案by Scott Barta

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.

Groovy 的好处在于您可以自由混合 Java 代码,并且使用java.util.Properties. 也许使用惯用的 Groovy 有更简单的方法,但 Java 仍然非常简单。

Create a keystore.propertiesfile (in this example, in the root directory of your project next to settings.gradle, though you can put it wherever you like:

创建一个keystore.properties文件(在此示例中,在项目的根目录中 旁边settings.gradle,但您可以将其放在任何您喜欢的位置:

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

Add this to your build.gradle:

将此添加到您的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']
        }
    }
}

回答by CurlyCorvus

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.propertiesfile in your project root folder:

或者,如果您想以更类似于自动生成的 gradle 代码的方式应用 Scott Barta 的答案,您可以keystore.properties在项目根文件夹中创建一个文件:

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

and modify your gradle code to:

并将您的gradle代码修改为:

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

您可以将此属性文件存储在模块的根目录中,在这种情况下只需省略rootProject,您还可以修改此代码以针对不同的密钥库和密钥别名设置多组属性。

回答by Dan Fabulich

The easiest way is to create a ~/.gradle/gradle.propertiesfile.

最简单的方法是创建一个~/.gradle/gradle.properties文件。

ANDROID_STORE_PASSWORD=hunter2
ANDROID_KEY_PASSWORD=hunter2

Then your build.gradlefile can look like this:

然后你的build.gradle文件看起来像这样:

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

回答by Jared Burrows

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

http://blog.macromates.com/2006/keychain-access-from-shell/ http://www.thoughtworks.com/es/insights/blog/signing-open-source-android-apps-without-disclosure-密码

Since you are using Mac OSX, you can use the Keychain Access to store your passwords.

由于您使用的是 Mac OSX,您可以使用 Keychain Access 来存储您的密码。

How to add password in Keychain Access

如何在钥匙串访问中添加密码

Then in your gradle scripts:

然后在你的 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]
}

Use like this:

像这样使用:

getPassword(currentUser, "Android_Store_Password")

getPassword(当前用户,“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
        }
    }
}

回答by Madhur Ahuja

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

回答by Wayne Piekarski

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:

可以使用任何现有的 Android Studio gradle 项目并从命令行构建/签署它,而无需编辑任何文件。这使得将您的项目存储在版本控制中非常好,同时将您的密钥和密码分开而不是在您的 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

回答by ??? ???? ????

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

接受的答案使用一个文件来控制使用哪个密钥库来签署驻留在项目的同一根文件夹中的 APK。当我们使用像Git这样的vcs时,当我们忘记将属性文件添加到忽略列表时可能是一件坏事。因为我们会向全世界公开我们的密码。问题仍然存在。

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.

而不是在我们的项目中的同一目录中制作属性文件,我们应该把它放在外面。我们通过使用 gradle.properties 文件将其放在外面。

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:

1.在你的根项目上编辑或创建gradle.properties并添加如下代码,记得用自己的路径编辑:

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:

2.在/your/path/中创建androidproject.properties并添加如下代码,不要忘记将/your/path/to/android.keystore改成你的keystore路径:

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:

3.在你的应用模块build.gradle(不是你的项目根build.gradle)中添加以下代码,如果不存在或调整它:

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:

4.在第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.propertiesfrom 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.

该代码将搜索AndroidProject.signing物业gradle.properties第1步。如果找到该属性,它会将属性值转换为指向我们在第 2 步中创建的 androidproject.properties 的文件路径。然后它的所有属性值将用作我们的 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

不将密钥库信息放入 build.gradle 的情况下签署 Android apk阅读更多信息

回答by SudoPlz

For the ones looking to put their credentials in an external JSON fileand read that from the gradle this is what I did:

对于那些希望将他们的凭据放在外部JSON 文件中并从 gradle 中读取的人,这就是我所做的:

my_project/credentials.json:

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

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 .jsonfile type, and not a .propertiesfile 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.

我选择.json文件类型而不是.properties文件类型(如已接受的答案中)的原因是因为我还想将其他数据(我需要的其他自定义属性)存储到同一个文件 ( my_project/credentials.json),并且仍然让 gradle 解析也从该文件中签署信息。

回答by Micha? K

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.gradlequite clean.

这个问题已经收到了许多有效的答案,但我想分享我的代码,这可能对库维护者有用,因为它使原始代码build.gradle非常干净

I add a folder to the module directory which I gitignore. It looks like this:

我在模块目录中添加了一个文件夹gitignore。它看起来像这样:

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

keystore.jksand signing.propertiesshould be self explanatory. And signing.gradlelooks like this:

keystore.jks并且signing.properties应该是不言自明的。而且signing.gradle是这样的:

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

和原来的 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 signingdirectory, 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.

如您所见,您根本不必指定 buildTypes,如果用户有权访问有效signing目录,他只需将其放入模块中,他就可以构建一个有效的签名发布应用程序,否则它只适用于他它通常会这样做。

回答by Yogendra Ghatpande

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.

我的密码包含一个特殊字符,它是美元符号$,我必须在 gradle.properties 文件中转义它。在那之后,签名对我有用。