skip to content

How do you sign release artifacts for Maven Central, and how is signing typically wired into the build?

level: middleimportance: must knowfreq 48%

answer

  1. maven-gpg-plugin sign goal
  2. binds to verify phase
  3. detached .asc per file
  4. publish public key to keyserver
  5. passphrase via -D or env in CI

basics

~20 s

Use the maven-gpg-plugin. It runs in the verify phase, creates a detached .asc signature for each artifact (jar, sources, javadoc, pom) using your GPG key, and your public key must be on a public keyserver so Sonatype can verify it.

solid answer

~40 s

Central requires every published file to carry a detached PGP/GPG signature (`.asc`). The standard tool is the **maven-gpg-plugin**, whose `sign` goal binds to the `verify` phase and signs all attached artifacts — main jar, `-sources.jar`, `-javadoc.jar`, and the `.pom`. You generate a GPG key pair, publish the **public** key to a keyserver (e.g. keyserver.ubuntu.com) so Sonatype can validate signatures, and keep the private key + passphrase secret. To avoid signing on every developer build, the gpg plugin is placed in a `release` profile. The passphrase is supplied non-interactively in CI via `-Dgpg.passphrase=...` or the gpg-agent/`--pinentry-mode loopback`. Modern setups often use an in-memory key file in CI rather than the local keyring. If a `.asc` is missing or the public key isn't findable, the deployment is rejected.

code

xml · 18 lines
xml
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-gpg-plugin</artifactId>
  <version>3.2.4</version>
  <executions>
    <execution>
      <id>sign-artifacts</id>
      <phase>verify</phase>
      <goals><goal>sign</goal></goals>
      <configuration>
        <gpgArguments>
          <arg>--pinentry-mode</arg>
          <arg>loopback</arg>
        </gpgArguments>
      </configuration>
    </execution>
  </executions>
</plugin>

go deeper

for a junior

Knows artifacts must be GPG-signed and that maven-gpg-plugin does it.

for a middle

Can wire the sign goal into the verify phase and explain the public/private key split.

for a senior

Handles non-interactive CI signing, key rotation/expiry, and profile activation cleanly.

for a principal

Establishes key management/rotation policy and secret handling across the org's release pipelines.

## Why signing Public artifacts must be tamper-evident. Central requires a **detached GPG signature** (`file.asc`) for *every* uploaded file so consumers (and Sonatype) can verify the artifact was produced by the key owner and not altered. ## The key pair - Generate: `gpg --gen-key` (or `--full-generate-key`). - Publish the **public** key so signatures can be verified: `gpg --keyserver keyserver.ubuntu.com --send-keys <KEY_ID>` - Never share the **private** key or passphrase. ## Wiring into the build Use **maven-gpg-plugin**. Its `sign` goal binds to `verify` and signs each attached artifact, emitting `.asc` files alongside them. Put it in a `release` profile so normal `mvn install` builds aren't blocked by signing. ```xml <profile> <id>release</id> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <id>sign-artifacts</id> <phase>verify</phase> <goals><goal>sign</goal></goals> </execution> </executions> </plugin> </plugins> </build> </profile> ``` ## Passphrase handling - **Local/interactive:** gpg-agent prompts. - **CI/non-interactive:** pass `-Dgpg.passphrase=...` (from a secret) and often configure `<gpgArguments><arg>--pinentry-mode</arg><arg>loopback</arg></gpgArguments>`. The newer plugin also reads `MAVEN_GPG_PASSPHRASE` / `MAVEN_GPG_KEY` env vars. - Store the key id, passphrase, and ASCII-armored private key as CI secrets. ## Verifying yourself ```bash gpg --verify widget-core-1.2.0.jar.asc widget-core-1.2.0.jar ``` ## Failure modes - Public key not on a reachable keyserver → validation fails. - Expired key. - Signing only the jar but not sources/javadoc/pom (the plugin handles all attached artifacts automatically, so disabling attachment breaks it).

  • Why put the gpg plugin in a profile instead of the main build?
    So everyday builds (install/test) don't require the private key or prompt for a passphrase; signing only runs during the explicit release flow.
  • How do you pass the GPG passphrase in CI without a TTY?
    Provide it via -Dgpg.passphrase or the MAVEN_GPG_PASSPHRASE env var and use --pinentry-mode loopback so gpg doesn't try to open an interactive prompt.

saying these in an interview costs you the question

  • Thinking only the main jar needs signing
  • Committing the private key or passphrase to the repo
  • Forgetting to publish the public key so Sonatype can't verify

context