簽署外掛增加了數位簽署已建置檔案和構件的功能。這些數位簽名可以用來證明誰建置了附加簽名的構件,以及其他資訊,例如簽名何時產生。

簽署外掛目前僅提供產生 OpenPGP 簽名 的支援(這是發布到 Maven Central Repository 所需的簽名格式)。

用法

要使用簽署外掛,請在您的建置腳本中包含以下內容

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

簽署人憑證

為了建立 OpenPGP 簽名,您需要一個金鑰對(關於使用 GnuPG 工具 建立金鑰對的說明可以在 GnuPG HOWTOs 中找到)。您需要向簽署外掛提供您的金鑰資訊,這表示三件事

  • 公開金鑰 ID(keyId 的最後 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-armored 金鑰

在某些設定中,使用環境變數傳遞用於簽署的私密金鑰和密碼更容易。例如,當使用 CI 伺服器簽署構件時,安全地提供金鑰環檔案通常很麻煩。另一方面,大多數 CI 伺服器都提供安全儲存環境變數並將其提供給建置的方法。使用以下設定,您可以使用 ORG_GRADLE_PROJECT_signingKeyORG_GRADLE_PROJECT_signingPassword 環境變數分別傳遞私密金鑰(以 ascii-armored 格式)和密碼

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-armored OpenPGP 子金鑰

為了防止共享主金鑰並確保其安全,也可以使用記憶體內 ascii-armored 子金鑰。使用記憶體內 ascii-armored 金鑰和子金鑰之間的主要區別在於,也必須指定金鑰識別符。使用以下設定,您可以使用 ORG_GRADLE_PROJECT_signingKeyIdORG_GRADLE_PROJECT_signingKeyORG_GRADLE_PROJECT_signingPassword 環境變數分別傳遞金鑰識別符、私密金鑰(以 ascii-armored 格式)和密碼

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 子金鑰。只需在 signing.keyId 屬性中指定子金鑰 ID 作為值即可。

使用 gpg-agent

預設情況下,簽署外掛使用基於 Java 的 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
}

這將在您的專案中建立一個名為 signMavenJavaPublication 的任務(類型為 Sign),它將建置發布中包含的所有構件(如果需要),然後為它們產生簽名。簽名檔案將與正在簽署的構件一起放置。

範例:簽署發布輸出

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
}

這將在您的專案中建立一個名為 signRuntimeElements 的任務(類型為 Sign),它將建置任何 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
}

這將在您的專案中建立一個名為 signStuffZip 的任務(類型為 Sign),它將建置輸入任務的封存檔(如果需要),然後對其進行簽署。簽名檔案將與正在簽署的構件一起放置。

範例:簽署任務輸出

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 任務,例如透過使用 onlyIf() 方法附加謂詞,如下例所示

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 相依性配置中。