Publish Maven bits to Maven Central: Code Signing with GnuPG

In this series of posts, we discuss how to publish Maven bits to Maven Central using the standard Maven deploy plugin. This is an eight step process to set up and execute, and we discussed the first two steps in the first post of the series and steps three to five in the previous post.

In this post, we discuss the last three steps with particular focus on how to do the code signing with GnuPG in an automated multi-module Maven build.

Step 6: Configure GnuPG for artifact signing

Neither Maven nor Nexus can properly handle GnuPG subkeys as signing keys. Using a subkey for signing is actually a very good security best practice, because it allows protecting the main key in automated code signing setups. Unfortunately, due to the limitations imposed by Maven and Nexus, if you are used to subkeys signing, then you must change your habit to make GnuPG work with Maven.

It also seems to be impossible to specify the singing key in the Maven GnuPG plugin. Consequently, you must delete not only all of your signing subkeys from the primary (master) key, but also all the other secret (master) keys you may have on your key ring. Once you deleted all secret keys save one, and all signing subkeys of that remaining primary key, the build should produce valid signatures.

For the basic Maven configuration, add the following section to the parent POM, preferably in a dedicated profile so that the signing is only executed when needed (e.g. on a centralized build server):


As GnuPG keys are usually protected by a passphrase, it is required to somehow inject the signing key’s passphrase into the Maven build. There are three common options for this:

  1. The passphrase can be stored in a settings file.
    This is really a bad idea, in particular as Maven requires using the master key, and is incapable of operating on an easily revocable subkey.
  2. The passphrase can be submitted as a parameter when starting the build.
    Use the -Dgpg.passphrase=keyPassphrase parameter when starting the Maven build. This is a reasonably safe option on single users systems (e.g. on dedicated build servers), but may allow users on multi-user systems to steal the passphrase from the process list. For multi-user systems, the parameter can point to an environment variable so that the passphrase does not show up in the process list (this is really a must on multi-user systems).
  3. Provide the passphrase during the build when it is needed
    The plugin will ask for the passphrase if it cannot find it in a file or as a parameter. This is the most secure option, but it may prevent build automation if not properly configured. By default, GnuPG uses its agent to retrieve the password, and the default setup is an interactive prompt. Depending on how the pinentry component of the agent is configured, it can retrieve the password interactively or non-interactively e.g. from a password vault.

Step 7: Build and deploy to staging

Build the binaries according to the project’s release process. Assuming that all the release related plugin confirmations are in the “release” profile, such a build could look similar like this:

    mvn versions:set -DnewVersion=1.0
    mvn versions:commit
    mvn clean deploy site -P release

The build instructions (and POM configuration in the underlying project) of Mike’s utility library give an example on how to configure an enterprise style Maven multi-module build and set it up to automatically deploy to Sonatype Nexus.

Step 8: Release the deployment to Maven Central

As indicated earlier, the automated deployment to OSSRH with the standard Maven Deploy plugin does not automatically release the deployed artifacts to Maven Central. While this process can be automated, I generally prefer to review the build results once more before releasing them to the public.

Sonatype describes their release process in detail. Once the deployment has been released, Sonatype will do an (automated) evaluation of the staging repository contents, and promote to Maven Central if everything is okay. Otherwise, the process will fail and the (then obsolete) staging repository can be “Dropped”.