簽署外掛程式新增了數位簽署已建置檔案和成品的能力。這些數位簽章可用於證明建立簽章所附成品的人員,以及其他資訊,例如簽章產生時間。

簽署外掛程式目前僅提供產生 OpenPGP 簽章 的支援(這是 發布到 Maven 中央儲存庫 所需的簽章格式)。

使用方式

若要使用簽署外掛程式,請在建置指令碼中包含下列內容

build.gradle.kts
plugins {
    signing
}
build.gradle
plugins {
    id 'signing'
}

簽署者憑證

若要建立 OpenPGP 簽章,您需要一組金鑰(使用 GnuPG 工具 建立金鑰組的說明可在 GnuPG HOWTO 中找到)。您需要提供金鑰資訊給簽署外掛程式,這表示三件事

  • 公開金鑰 ID(金鑰 ID 的最後 8 個符號。您可以使用 gpg -K 取得)。

  • 包含您的私人金鑰的機密金鑰圈檔案的絕對路徑。(自 gpg 2.1 起,您需要使用指令 gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg 匯出金鑰)。

  • 用於保護您的私人金鑰的密碼。

這些項目必須分別提供為 signing.keyIdsigning.secretKeyRingFilesigning.password 屬性的值。

考量到這些值具有個人和私密性質,建議將它們儲存在使用者 Gradle 主目錄中的 gradle.properties 檔案(在 系統屬性 中說明),而不是在專案目錄中。
signing.keyId=24875D73
signing.password=secret
signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg

如果在使用者 `gradle.properties` 檔案中指定此資訊(尤其是 `signing.password`)對你的環境來說不可行,你可以透過命令列提供資訊

> gradle sign -Psigning.secretKeyRingFile=/Users/me/.gnupg/secring.gpg -Psigning.password=secret -Psigning.keyId=24875D73

使用記憶體中的 ASCII 裝甲金鑰

在某些設定中,使用環境變數來傳遞用於簽署的機密金鑰和密碼會比較容易。例如,當使用 CI 伺服器來簽署成品時,安全地提供鑰匙圈檔案通常很麻煩。另一方面,大多數 CI 伺服器提供安全儲存環境變數並將其提供給建置的方法。使用以下設定,你可以分別使用 `ORG_GRADLE_PROJECT_signingKey` 和 `ORG_GRADLE_PROJECT_signingPassword` 環境變數傳遞機密金鑰(以 ASCII 裝甲格式)和密碼

build.gradle.kts
signing {
    val signingKey: String? by project
    val signingPassword: String? by project
    useInMemoryPgpKeys(signingKey, signingPassword)
    sign(tasks["stuffZip"])
}
build.gradle
signing {
    def signingKey = findProperty("signingKey")
    def signingPassword = findProperty("signingPassword")
    useInMemoryPgpKeys(signingKey, signingPassword)
    sign stuffZip
}

使用記憶體中的 ASCII 裝甲 OpenPGP 子金鑰

為了防止共用主金鑰並保持其安全,也可以使用記憶體中的 ASCII 裝甲子金鑰。使用記憶體中的 ASCII 裝甲金鑰和子金鑰之間的主要差異在於,也需要指定金鑰識別碼。使用以下設定,你可以分別使用 `ORG_GRADLE_PROJECT_signingKeyId`、`ORG_GRADLE_PROJECT_signingKey` 和 `ORG_GRADLE_PROJECT_signingPassword` 環境變數傳遞金鑰識別碼、機密金鑰(以 ASCII 裝甲格式)和密碼

build.gradle.kts
signing {
    val signingKeyId: String? by project
    val signingKey: String? by project
    val signingPassword: String? by project
    useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
    sign(tasks["stuffZip"])
}
build.gradle
signing {
    def signingKeyId = findProperty("signingKeyId")
    def signingKey = findProperty("signingKey")
    def signingPassword = findProperty("signingPassword")
    useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
    sign stuffZip
}

使用 OpenPGP 子金鑰

OpenPGP 支援子金鑰,這些子金鑰就像一般金鑰,但它們繫結到主金鑰對。OpenPGP 子金鑰的一個特色是,它們可以獨立於主金鑰撤銷,這使得金鑰管理更為容易。可以在 Debian wiki 上閱讀關於如何在軟體開發中利用子金鑰的實際案例研究。

簽署外掛程式開箱即支援 OpenPGP 子金鑰。只要將子金鑰 ID 指定為 `signing.keyId` 屬性中的值即可。

使用 gpg-agent

預設情況下,簽署外掛程式使用 Java-based 的 PGP 實作進行簽署。不過,此實作無法使用 gpg-agent 程式管理私密金鑰。如果您想使用 gpg-agent,您可以變更簽署外掛程式使用的簽署實作

build.gradle.kts
signing {
    useGpgCmd()
    sign(configurations.runtimeElements.get())
}
build.gradle
signing {
    useGpgCmd()
    sign configurations.runtimeElements
}

這會指示簽署外掛程式使用 GnupgSignatory,而不是預設的 PgpSignatoryGnupgSignatory 依賴 gpg2 程式簽署成品。當然,這需要安裝 GnuPG。

在沒有任何進一步設定的情況下,將會使用在 PATH 上找到的 gpg (在 Windows 上:gpg.exe) 可執行檔。密碼由 gpg-agent 提供,並使用預設金鑰進行簽署。

Gnupg 簽署設定

GnupgSignatory 支援多個設定選項,用於控制呼叫 gpg 的方式。這些選項通常會在 gradle.properties 中設定

範例:設定 GnupgSignatory

gradle.properties
signing.gnupg.executable=gpg
signing.gnupg.useLegacyGpg=true
signing.gnupg.homeDir=gnupg-home
signing.gnupg.optionsFile=gnupg-home/gpg.conf
signing.gnupg.keyName=24875D73
signing.gnupg.passphrase=gradle
signing.gnupg.executable

呼叫進行簽署的 gpg 可執行檔。此屬性的預設值取決於 useLegacyGpg。如果為 true,可執行檔的預設值為「gpg」,否則為「gpg2」。

signing.gnupg.useLegacyGpg

如果使用 GnuPG 版本 1,則必須為 true,否則為 false。此屬性的預設值為 false

signing.gnupg.homeDir

設定 GnuPG 的主目錄。如果未提供,則會使用 GnuPG 的預設主目錄。

signing.gnupg.optionsFile

設定 GnuPG 的自訂選項檔案。如果未提供,則會使用 GnuPG 的預設設定檔。

signing.gnupg.keyName

應使用於簽署的金鑰 ID。如果未提供,則會使用 GnuPG 中設定的預設金鑰。

signing.gnupg.passphrase

用於解鎖密鑰的密碼。如果未提供,則會使用 gpg-agent 程式取得密碼。

所有設定屬性都是選用的。

指定要簽署的內容

除了設定簽署事物的內容(例如簽署設定)之外,您還必須指定要簽署的內容。簽署外掛程式提供一個 DSL,讓您可以指定應簽署的任務和/或設定。

簽署刊物

發布成品時,您通常會想要簽署成品,以便成品使用者可以驗證其簽章。例如,Java 外掛程式定義了一個元件,您可以使用它來定義使用Maven 發布外掛程式(或Ivy 發布外掛程式)對 Maven(或 Ivy)儲存庫發布。使用簽署 DSL,您可以指定此發布的所有成品都應該簽署。

範例 3. 簽署發布
build.gradle.kts
signing {
    sign(publishing.publications["mavenJava"])
}
build.gradle
signing {
    sign publishing.publications.mavenJava
}

這會在您的專案中建立一個任務(類型為Sign),名稱為 signMavenJavaPublication,它會建立屬於發布的所有成品(如果需要),然後為它們產生簽章。簽章檔案會放置在要簽署的成品旁邊。

範例:簽署發布輸出

gradle signMavenJavaPublication 的輸出
> gradle signMavenJavaPublication
> Task :compileJava
> Task :processResources
> Task :classes
> Task :jar
> Task :javadoc
> Task :javadocJar
> Task :sourcesJar
> Task :generateMetadataFileForMavenJavaPublication
> Task :generatePomFileForMavenJavaPublication
> Task :signMavenJavaPublication

BUILD SUCCESSFUL in 0s
9 actionable tasks: 9 executed

此外,上述 DSL 允許 sign 多個以逗號分隔的發布。或者,您可以指定 publishing.publications 來簽署所有發布,或使用 publishing.publications.matching { … } 來簽署所有符合指定謂詞的發布。

簽署組態

通常會想要簽署組態的成品。例如,Java 外掛程式會組態一個要建立的 jar,而這個 jar 成品會新增到 runtimeElements 組態。使用簽署 DSL,您可以指定此組態的所有成品都應該簽署。

範例 4. 簽署組態
build.gradle.kts
signing {
    sign(configurations.runtimeElements.get())
}
build.gradle
signing {
    sign configurations.runtimeElements
}

這會在您的專案中建立一個任務(類型為Sign),名稱為 signRuntimeElements,它會建立任何 runtimeElements 成品(如果需要),然後為它們產生簽章。簽章檔案會放置在要簽署的成品旁邊。

範例:簽署組態輸出

gradle signRuntimeElements 的輸出
> gradle signRuntimeElements
> Task :compileJava
> Task :processResources
> Task :classes
> Task :jar
> Task :signRuntimeElements

BUILD SUCCESSFUL in 0s
4 actionable tasks: 4 executed

簽署任務輸出

在某些情況下,您需要簽署的成品可能不屬於組態的一部分。在這種情況下,您可以直接簽署產生要簽署成品的任務。

build.gradle.kts
tasks.register<Zip>("stuffZip") {
    archiveBaseName = "stuff"
    from("src/stuff")
}

signing {
    sign(tasks["stuffZip"])
}
build.gradle
tasks.register('stuffZip', Zip) {
    archiveBaseName = 'stuff'
    from 'src/stuff'
}

signing {
    sign stuffZip
}

這會在您的專案中建立一個任務(類型為Sign),名稱為 signStuffZip,它會建立輸入任務的封存(如果需要),然後簽署它。簽章檔案會放置在要簽署的成品旁邊。

範例:簽署任務輸出

gradle signStuffZip 的輸出
> gradle signStuffZip
> Task :stuffZip
> Task :signStuffZip

BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed

若要讓任務可簽署,它必須產生某種類型的檔案,亦即它必須延伸 AbstractArchiveTask。執行此動作的任務為 TarZipJarWarEar 任務。

條件式簽署

常見的用法模式是僅在特定條件下才需要簽署建置成品。例如,您可能不需要為非發行版本簽署成品。若要達成此目的,您可以將條件指定為 required() 方法的引數。

build.gradle.kts
version = "1.0-SNAPSHOT"
extra["isReleaseVersion"] = !version.toString().endsWith("SNAPSHOT")

signing {
    setRequired({
        (project.extra["isReleaseVersion"] as Boolean) && gradle.taskGraph.hasTask("publish")
    })
    sign(publishing.publications["main"])
}
build.gradle
version = '1.0-SNAPSHOT'
ext.isReleaseVersion = !version.endsWith("SNAPSHOT")

signing {
    required { isReleaseVersion && gradle.taskGraph.hasTask("publish") }
    sign publishing.publications.main
}

在此範例中,我們僅在建置發行版本且準備發布時才需要簽署。由於我們會檢查任務圖表以確定是否要發布,因此我們必須將 signing.required 屬性設定為封閉函式以延後評估。請參閱 SigningExtension.setRequired(java.lang.Object) 以取得更多資訊。

如果 required 條件不成立,則僅在已設定簽署者憑證時才會簽署成品。或者,您可能想要完全略過簽署,無論是否有簽署者憑證。如果是這樣,您可以設定 Sign 任務為略過,例如透過附加謂詞,方法如下一個範例所示

build.gradle.kts
tasks.withType<Sign>().configureEach {
    onlyIf("isReleaseVersion is set") { project.extra["isReleaseVersion"] as Boolean }
}
build.gradle
tasks.withType(Sign) {
    onlyIf("isReleaseVersion is set") { isReleaseVersion }
}

發布簽章

在簽署 發布 時,產生的簽章成品會自動新增到對應的發布中。因此,當發布到儲存庫中時,例如執行 publish 任務,您的簽章會與其他成品一起發布,而不需要任何其他設定。

在簽署組態工作時,結果簽署的成品會自動新增到signatures依賴項組態中。