Đăng nhập APK mà không đưa thông tin kho khóa vào build.gradle


152

Tôi đang cố gắng thiết lập quy trình ký để mật khẩu lưu trữ khóa và mật khẩu khóa không được lưu trong build.gradletệp của dự án .

Hiện tại tôi có những điều sau đây build.gradle:

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

    buildTypes {
        release {
            signingConfig signingConfigs.release            
        }
    }
}

Nó hoạt động hoàn toàn tốt nhưng tôi không được đặt các giá trị cho storePasswordkeyPasswordtrong kho lưu trữ của mình. Tôi muốn không đặt storeFilekeyAliasở đó một trong hai.

Có cách nào để thay đổi build.gradleđể nó sẽ lấy được mật khẩu từ một số nguồn bên ngoài (như một tệp chỉ nằm trên máy tính của tôi) không?

Và tất nhiên, build.gradlecó thể sử dụng thay đổi trên bất kỳ máy tính nào khác (ngay cả khi máy tính không có quyền truy cập vào mật khẩu).

Tôi đang sử dụng Android Studio và trong Mac OS X Maverics nếu có vấn đề.


"Và tất nhiên, bản build.gradle đã thay đổi sẽ có thể sử dụng được trên bất kỳ máy tính nào khác (ngay cả khi máy tính không có quyền truy cập mật khẩu)" - nếu dữ liệu không có trong build.gradle, bạn sẽ phải có thứ gì đó ngoài build.gradle, cho dù đó là sự điều chỉnh các biến môi trường (mỗi câu trả lời), tệp thuộc tính (mỗi câu trả lời khác) hoặc một số phương tiện khác. Nếu bạn không muốn có những thứ bên ngoài build.gradle, thì theo định nghĩa, tất cả các thông tin ký phải ở bên trong buid.gradle .
CommonsWare

2
@CommonsWare Bạn nói đúng. Mặc dù vậy, tôi không nói rằng tôi muốn có bất cứ thứ gì nghiêm ngặt trong build.gradle. Và tôi đã nói rằng build.gradle có thể lấy mật khẩu từ một số nguồn bên ngoài (giống như một tệp chỉ nằm trên máy tính của tôi
Bobrovsky


Tôi đã gắn cờ là bản sao của stackoverflow.com/questions/18328730/ , trên cơ sở meta.stackoverflow.com/questions/311044/
Lỗi

Câu trả lời:


120

Điều thú vị về Groovy là bạn có thể tự do trộn mã Java và việc đọc tệp khóa / giá trị khá dễ dàng java.util.Properties. Có lẽ có một cách thậm chí còn dễ dàng hơn khi sử dụng Groovy thành ngữ, nhưng Java vẫn khá đơn giản.

Tạo một keystore.propertiestệp (trong ví dụ này, trong thư mục gốc của dự án của bạn bên cạnh settings.gradle, mặc dù bạn có thể đặt nó bất cứ nơi nào bạn muốn:

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

Thêm phần này vào 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
Tôi đã phải xóa các trích dẫn khỏi kho khóa của mình
Jacob Tabak

6
Nó không tạo phiên bản đã ký cho tôi với phiên bản plugin 0.9. +. Tôi phải làm gì với khối SignConfigs và mục buildTypes.release.signingConfig? loại bỏ chúng?
Fernando Gallego

1
Có vẻ như cài đặt storeFile thành bất kỳ giá trị hợp lệ nào (ví dụ storeFile file('AndroidManifest.xml')) và sau đó ghi đè lên nó sẽ khiến quá trình ký kết diễn ra.
miracle2k

5
Xây dựng kết quả trong một lỗi Error:(24, 0) Could not find property 'android' on root project 'RootProjectName'trong đó dòng 24 là dòng có khối if. Thêm apply plugin: 'com.android.application'vào root build.gradle cũng cho phép xây dựng thất bại. Tôi đang làm gì sai?
PhilLab

2
Điều này không hoạt động vào năm 2018. Điều này phải được phản đối? Giữ lỗi némCould not get unknown property 'android' for root project
neon Warge 22/12/18

106

Ngoài ra, nếu bạn muốn áp dụng câu trả lời của Scott Barta theo cách tương tự với mã lớp được tạo tự động, bạn có thể tạo một keystore.propertiestệp trong thư mục gốc của dự án:

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

và sửa đổi mã lớp của bạn thành:

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

    ...

}

Bạn có thể lưu trữ tệp thuộc tính này trong thư mục gốc của mô-đun, trong trường hợp này chỉ cần bỏ qua rootProjectvà bạn cũng có thể sửa đổi mã này để có một số bộ thuộc tính cho các kho khóa và bí danh chính khác nhau.


7
Công trình tuyệt vời. Tôi đã sử dụng if ( keystorePropertiesFile.exists() )để đảm bảo tập tin có mặt trước khi thử lấy các thuộc tính và cố gắng đăng nhập.
Joshua Pinter

Và đừng quên thêm .txtphần mở rộng vào cuối keystore.propertiestập tin.
Levon Petrosyan

11
Bạn không cần một .txtphần mở rộng trên keystore.propertiestập tin.
Matt Zukowski

2
Có vẻ như thông tin này đã được thêm vào đây - developer.android.com/studio/publish/
Kẻ

36

Cách dễ nhất là tạo một ~/.gradle/gradle.propertiestập tin.

ANDROID_STORE_PASSWORD=hunter2
ANDROID_KEY_PASSWORD=hunter2

Sau đó, build.gradletập tin của bạn có thể trông như thế này:

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

1
Tôi có nên gitignore ~ ​​/ .gradle / gradle.properies không?
vZH

Các hướng dẫn đầy đủ cũng là trong phản ứng tài liệu bản địa.
Bút chì

23

Sau khi đọc một vài liên kết:

http://blog.macromates.com/2006/keychain-access-from-shell/ http: // www. Dùtworks.com/es/insights/blog/signing-open-source-android-apps-without-discloses- mật khẩu

Vì bạn đang sử dụng Mac OSX, bạn có thể sử dụng Keychain Access để lưu trữ mật khẩu của mình.

Cách thêm mật khẩu trong Keychain Access

Sau đó, trong tập lệnh lớp của bạn:

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

Sử dụng như thế này:

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
Mặc dù câu trả lời của bạn chỉ áp dụng cho Mac OSX, tôi thực sự thích nó! Lưu ý rằng liên kết thứ hai bạn cung cấp chứa giải pháp sao lưu cho các nền tảng khác, trong trường hợp ai đó cần triển khai hỗ trợ đa nền tảng.
Delblanco

Bạn có thể vui lòng cung cấp giải pháp tương tự cho linux và windows không? Cảm ơn.
Jay Mungara

18

Đây là cách tôi làm điều đó. Sử dụng biến môi trường

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

3
Thật không may, điều này đòi hỏi môi trường hệ thống phải được tạo cho từng dự án trên mỗi máy tính . Nếu không, tôi nhận được lỗi sauNeither path nor baseDir may be null or empty string. path='null'
Bobrovsky

@Bobrovsky Tôi biết câu hỏi này đã được trả lời, nhưng bạn có thể sử dụng các biến môi trường hệ thống hoặc tệp gradle.properIES. Bạn có thể muốn sử dụng tệp gradle.properIES. Bạn có thể sử dụng nó cho nhiều dự án.
Jared Burrows

3
điều này không hoạt động trên MacOSX trừ khi bạn chạy Android Studio từ dòng lệnh.
Henrique de Sousa

Tôi đồng ý với tất cả ở trên. Tôi có cùng cấu hình và bạn không thể biên dịch cái này trong studio Android. Bạn cần chạy từ dòng lệnh để làm việc này. Tôi đang tìm kiếm một cách tốt hơn, để tôi không phải bình luận những dòng này khi tôi chạy trong Android Studio.
Sayooj Valsan

@Bobrovsky: Nó có hoạt động trên windows không?. Chúng ta có nên đề cập đến tất cả những điều này trong môi trường hệ thống?
DKV

12

Có thể lấy bất kỳ dự án phân lớp Android Studio hiện có nào và xây dựng / ký tên từ dòng lệnh mà không cần chỉnh sửa bất kỳ tệp nào. Điều này làm cho nó rất tốt để lưu trữ dự án của bạn trong kiểm soát phiên bản trong khi giữ các khóa và mật khẩu riêng biệt và không nằm trong tệp build.gradle của bạn:

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

Câu trả lời được chấp nhận sử dụng một tệp để kiểm soát kho khóa nào sẽ sử dụng để ký APK nằm trong cùng thư mục gốc của dự án. Khi chúng tôi sử dụng vcs như Git , có thể là một điều tồi tệ khi chúng tôi quên thêm tệp thuộc tính để bỏ qua danh sách. Bởi vì chúng tôi sẽ tiết lộ mật khẩu của chúng tôi với thế giới. Những vấn đề vẫn còn tồn tại.

Thay vì tạo tệp thuộc tính trong cùng thư mục trong dự án của chúng ta, chúng ta nên tạo nó bên ngoài. Chúng tôi làm cho nó ra bên ngoài bằng cách sử dụng tập tin gradle.properIES.

Đây là các bước:

1. Chỉnh sửa hoặc tạo gradle.properies trên dự án gốc của bạn và thêm mã sau đây, hãy nhớ chỉnh sửa đường dẫn với chính bạn:

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

2.Tạo androidproject.properies trong / your / path / và thêm đoạn mã sau vào đó, đừng quên thay đổi /your/path/to/android.keystore vào đường dẫn kho khóa của bạn:

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

3. Trong mô-đun ứng dụng build.gradle của bạn (không phải xây dựng gốc dự án của bạn.gradle) thêm mã sau nếu không tồn tại hoặc điều chỉnh theo nó:

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

4.Thêm đoạn mã sau bên dưới mã trong bước 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  
     }  
   }  

Mã này sẽ tìm kiếm thuộc tính AndroidProject.signing trong gradle.properations từ bước 1 . Nếu thuộc tính được tìm thấy, nó sẽ dịch giá trị thuộc tính dưới dạng đường dẫn tệp trỏ đến androidproject.properies mà chúng ta tạo ở bước 2 . Sau đó, tất cả giá trị thuộc tính từ nó sẽ được sử dụng làm cấu hình ký cho build.gradle của chúng tôi.

Bây giờ chúng tôi không cần phải lo lắng về nguy cơ lộ mật khẩu kho khóa của mình.

Đọc thêm tại Đăng ký apk Android mà không đưa thông tin kho khóa vào build.gradle


Điều này làm việc tốt cho tôi. chỉ để biết lý do tại sao họ sử dụng tệp storeFile (System.getenv ("KEYSTORE"))
DKV

9

Đối với những người đang tìm cách đặt thông tin đăng nhập của họ vào một tệp JSON bên ngoài và đọc rằng từ lớp này, đây là những gì tôi đã làm:

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

Lý do tôi chọn .jsonloại tệp chứ không phải .propertiesloại tệp (như trong câu trả lời được chấp nhận) là vì tôi cũng muốn lưu trữ dữ liệu khác (thuộc tính tùy chỉnh khác mà tôi cần) cho cùng tệp đó ( my_project/credentials.json) và vẫn phân loại phân loại ký thông tin từ trong tập tin đó là tốt.


Có vẻ như là giải pháp tốt nhất cho tôi.
Khát vọng Dev

4

Câu hỏi này đã nhận được nhiều câu trả lời hợp lệ, nhưng tôi muốn chia sẻ mã của mình có thể hữu ích cho những người bảo trì thư viện , vì nó để lại bản gốc build.gradlekhá sạch sẽ .

Tôi thêm một thư mục vào thư mục mô-đun mà tôi gitignore. Nó trông như thế này:

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

keystore.jkssigning.propertiesnên tự giải thích. Và signing.gradletrông như thế này:

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]

Và bản gốc 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 ...
}

Như bạn có thể thấy, bạn hoàn toàn không phải chỉ định buildTypes, nếu người dùng có quyền truy cập vào một signingthư mục hợp lệ , anh ta chỉ cần đặt nó vào mô-đun và anh ta có thể xây dựng một ứng dụng phát hành có chữ ký hợp lệ, nếu không nó chỉ hoạt động với anh ta như nó thường sẽ làm.


Tôi thực sự thích giải pháp này. Lưu ý, tuy nhiên, apply fromnên đến sau androidkhối
mgray88

0

Bạn có thể yêu cầu mật khẩu từ dòng lệnh:

...

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
  }

  ...
}

...

Câu trả lời này đã xuất hiện trước đây: https://stackoverflow.com/a/33765572/3664487


Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi. - Từ đánh giá
mkobit

1
@mkobit, đây là liên kết đến nội dung trên Stack Overflow! Tất nhiên tôi có thể sao chép và dán nội dung được liên kết, nhưng điều đó dẫn đến nội dung trùng lặp. Vì vậy, tôi giả sử và sửa lỗi cho tôi nếu tôi sai, đăng một liên kết là giải pháp tốt nhất. Bất kỳ đối số nào "thay đổi trang được liên kết" nên được loại bỏ, trên cơ sở nội dung ở đây cũng có thể thay đổi. Tôi mạnh mẽ đề nghị chống lại giải pháp của bạn để xóa! Bởi vì nội dung được liên kết cung cấp một giải pháp tuyệt vời.
dùng2768

Chà, tôi nghĩ vấn đề là đây vẫn là câu trả lời "chỉ liên kết". Tôi nghĩ giải pháp là đăng nó dưới dạng bình luận, đánh dấu câu hỏi dưới dạng trùng lặp hoặc viết câu trả lời mới ở đây để giải quyết vấn đề.
mkobit

Câu trả lời "chỉ liên kết" nên được khuyến khích trong một số trường hợp. Tuy nhiên, tôi đã làm theo lời khuyên của bạn và nội dung trùng lặp. (Nội dung trùng lặp rõ ràng có vấn đề, vì một số nội dung có thể được cập nhật, trong khi nội dung còn lại có thể không có.)
user2768

Thật vậy, tôi vừa cập nhật câu trả lời và nội dung trùng lặp đang gây ra vấn đề! Nếu có một chính sách chống lại các câu trả lời chỉ liên kết, thì nó nên được điều chỉnh để xem xét các trường hợp góc như vậy.
dùng2768

0

Mật khẩu của tôi chứa một ký tự đặc biệt có ký hiệu đô la $ và tôi phải thoát nó trong tệp gradle.properIES. Sau đó, ký kết làm việc cho tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.