第五十三章. 签名插件

Chapter 53. The Signing Plugin

签名插件添加了对构建的文件和工件进行数字签名的功能。这些数字签名可以用于证明谁构建了这个签名所连接的工件,以及诸如签名什么时候生成的其他信息。
The signing plugin adds the ability to digitally sign built files and artifacts. These digital signatures can then be used to prove who built the artifact the signature is attached to as well as other information such as when the signature was generated.

签名插件当前只支持 PGP 签名(这是发布到 Maven 中央仓库所需的签名格式)。
The signing plugin currently only provides support for generating PGP signatures (which is the signature format required for publication to the Maven Central Repository).

53.1. 用法

53.1. Usage

要使用 Signing 插件,请在构建脚本中包含:
To use the Signing plugin, include in your build script:

示例 53.1. 使用 Signing 插件 - Example 53.1. Using the Signing plugin

build.gradle

apply plugin: 'signing'

53.2. 签名凭证

53.2. Signatory credentials

要创建 PGP 签名,你需要一个密钥对(有关使用 GnuPG 工具创建密钥对的介绍可以在 GnuPG HOWTOs中找到)。你需要向签名插件提供你的密钥信息,即如下三项事情:
In order to create PGP signatures, you will need a key pair (instructions on creating a key pair using the GnuPG tools can be found in the GnuPG HOWTOs). You need to provide the signing plugin with your key information, which means three things:

  • 公钥 ID(一个 8 位字符的十六进制字符串)。

    The public key ID (an 8 character hexadecimal string).

  • 包含私钥的密钥环文件的绝对路径。

    The absolute path to the secret key ring file containing your private key.

  • 用来保护你的私钥的密码。

    The passphrase used to protect your private key.

这些事项必须分别作为属性项目 signing.keyIdsigning.passwordsigning.secretKeyRingFile 提供。考虑到这些值的个人及私人性质,较好的做法是把它们存储在用户 gradle.properties 文件中(第 14.2 节,《Gradle 属性和系统属性》中所述)。
These items must be supplied as the property projects signing.keyId, signing.password and signing.secretKeyRingFile respectively. Given the personal and private nature of these values, a good practice is to store them in the user gradle.properties file (described in Section 14.2, “Gradle properties and system properties”).

signing.keyId=24875D73
signing.password=secret
signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg

如果在用户的 gradle.properties 文件中指定的信息不适合你的环境,你可以读取需要的这些信息并手动设置项目属性。
If specifying this information in the user gradle.properties file is not feasible for your environment, you can source the information however you need to and set the project properties manually.

import org.gradle.plugins.signing.Sign

gradle.taskGraph.whenReady { taskGraph ->
    if (taskGraph.allTasks.any { it instanceof Sign }) {
        // Use Java 6's console to read from the console (no good for a CI environment)
        Console console = System.console()
        console.printf "\n\nWe have to sign some things in this build.\n\nPlease enter your signing details.\n\n"

        def id = console.readLine("PGP Key Id: ")
        def file = console.readLine("PGP Secret Key Ring File (absolute path): ")
        def password = console.readPassword("PGP Private Key Password: ")

        allprojects { ext."signing.keyId" = id }
        allprojects { ext."signing.secretKeyRingFile" = file }
        allprojects { ext."signing.password" = password }

        console.printf "\nThanks.\n\n"
    }
}

53.3. 指定要签名的内容

53.3. Specifying what to sign

除了配置内容要如何签名(即签名配置),你还必须指定要签名哪些文件。Signing 插件提供了一个 DSL,能够让你指定应签名的任务和配置。
As well as configuring how things are to be signed (i.e. the signatory configuration), you must also specify what is to be signed. The Signing plugin provides a DSL that allows you to specify the tasks and/or configurations that should be signed.

53.3.1. 签名配置

53.3.1. Signing Configurations

想对配置中的工件进行签名是很常见的情况。例如,Java 插件配置了要构建的 jar,并把这个 jar 文件添加到 archives 配置。通过使用 Signing DSL,你可以指定这个配置的所有工件都应该被签名。
It is common to want to sign the artifacts of a configuration. For example, the Java plugin configures a jar to built and this jar artifact is added to the archives configuration. Using the Signing DSL, you can specify that all of the artifacts of this configuration should be signed.

示例53.2. 签署配置 - Example 53.2. Signing a configuration

build.gradle

signing {
    sign configurations.archives
}

这将在你的项目中创建一个名为“signArchives”的(Sign类型的)任务,这个任务会构建所有archives的artifacts(如果需要),然后为它们生成签名。签名文件将和正在签名的工件放在一起。
This will create a task (of type Sign) in your project named “signArchives”, that will build any archives artifacts (if needed) and then generate signatures for them. The signature files will be placed alongside the artifacts being signed.

示例 53.3. 签署配置的输出 - Example 53.3. Signing a configuration output

gradle signArchives 的输出结果
Output of gradle signArchives

> gradle signArchives
:compileJava
:processResources
:classes
:jar
:signArchives

BUILD SUCCESSFUL

Total time: 1 secs

53.3.2. 签名任务

53.3.2. Signing Tasks

在某些情况下,你需要签名的工件可能不在配置中。在这种情况下,你可以直接对生成要签名的工件的任务进行签名。
In some cases the artifact that you need to sign may not be part of a configuration. In this case you can directly sign the task that produces the artifact to sign.

示例 53.4. 签名任务 - Example 53.4. Signing a task

build.gradle

task stuffZip (type: Zip) {
    baseName = "stuff"
    from "src/stuff"
}

signing {
    sign stuffZip
}

这将在你的项目创建一个名为“signStuffZip”的(Sign类型的)任务,这个任务会构建输入任务的归档(如果需要)并对其进行签字。签名文件将和正在签名的工件放在一起。
This will create a task (of type Sign) in your project named “signStuffZip”, that will build the input task's archive (if needed) and then sign it. The signature file will be placed alongside the artifact being signed.

示例 53.5. 签名任务的输出 - Example 53.5. Signing a task output

gradle signStuffZip 的输出结果
Output of gradle signStuffZip

> gradle signStuffZip
:stuffZip
:signStuffZip

BUILD SUCCESSFUL

Total time: 1 secs

一个“可签名”的任务,必须输出某种类型的归档。这样的任务是 TarZipJarWarEar 任务。
For a task to be “signable”, it must produce an archive of some type. Tasks that do this are the Tar, Zip, Jar, War and Ear tasks.

53.3.3. 条件签名

53.3.3. Conditional Signing

常见的用法模式是只在特定条件下才签名构建工件。例如,你可能不想对非发布版的工件进行签名。要实现这个目的,你可以指定只在一定条件下才需要签名。
A common usage pattern is to only sign build artifacts under certain conditions. For example, you may not wish to sign artifacts for non release versions. To achieve this, you can specify that signing is only required under certain conditions.

示例 53.6. 条件签名 - Example 53.6. Conditional signing

build.gradle

version = '1.0-SNAPSHOT'
ext.isReleaseVersion = !version.endsWith("SNAPSHOT")

signing {
    required { isReleaseVersion && gradle.taskGraph.hasTask("uploadArchives") }
    sign configurations.archives
}

在此示例中,只有在我们构建发布版本并且要发布它时,才需要签名。由于我们正在检查任务图以确定是否要发布,因此必须将 signing.requiree 属性设置为一个闭包,以延迟求值。更多信息请参阅 SigningExtension.setRequired()
In this example, we only want to require signing if we are building a release version and we are going to publish it. Because we are inspecting the task graph to determine if we are going to be publishing, we must set the signing.required property to a closure to defer the evaluation. See SigningExtension.setRequired() for more information.

53.4. 发布签名

53.4. Publishing the signatures

当通过 Signing DSL 指定要签名的内容时,会自动将生成的签名工件添加到 signaturesarchives 依赖配置中。这意味着,如果你想要将签名随工件一起上传到分发仓库,正常来讲,你只需要简单地执行 uploadArchives 任务就可以了。
When specifying what is to be signed via the Signing DSL, the resultant signature artifacts are automatically added to the signatures and archives dependency configurations. This means that if you want to upload your signatures to your distribution repository along with the artifacts you simply execute the uploadArchives task as normal.

53.5. 签名 POM 文件

53.5. Signing POM files

在将工件签名部署签名到 Maven 仓库时,你还希望对发布的 POM 文件也进行签名。签名插件添加了一个 signing.signPom() (请参见: SigningExtension.signPom())方法,这个方法可以在你的上传任务配置的 beforeDeployment() 块中使用。
When deploying signatures for your artifacts to a Maven repository, you will also want to sign the published POM file. The signing plugin adds a signing.signPom() (see: SigningExtension.signPom()) method that can be used in the beforeDeployment() block in your upload task configuration.

示例 53.7. 为部署签名 POM - Example 53.7. Signing a POM for deployment

build.gradle

uploadArchives {
    repositories {
        mavenDeployer {
            beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
        }
    }
}

当不需要签名,以及由于缺少配置(即没有签名证书)而不能签名 POM 时,signPom() 方法将不执行任何操作,并且不会有提示。
When signing is not required and the POM cannot be signed due to insufficient configuration (i.e. no credentials for signing) then the signPom() method will silently do nothing.