skip to content

How do `<trusted-keys>` / `<trusted-key>` and per-artifact `<pgp>` entries express trust, and how do you scope a key to specific dependencies?

level: middleimportance: should knowfreq 40%

answer

  1. <trusted-key> global, group/name/version scope
  2. <pgp> = per-artifact trust
  3. <ignored-keys> to skip unresolvable signers
  4. least privilege = scope to group
  5. full fingerprint not short ID

basics

~10 s

A global <trusted-key id="fingerprint"> trusts that key for any artifact (optionally narrowed with group/name/version). A per-artifact <pgp value="keyId"/> under a <component> trusts a key only for that one artifact.

solid answer

~40 s

Trust in PGP keys is expressed two ways in `verification-metadata.xml`. **Globally**, under `<trusted-keys>`, each `<trusted-key id="FULL_FINGERPRINT"/>` says "any artifact signed by this key passes." You narrow that with optional attributes: `group`, `name`, `version`, `file`, `regex` — e.g. `<trusted-key id="..." group="com.fasterxml.jackson.core"/>` trusts the key only for Jackson-core artifacts. This scoping is the principle of least privilege: a publisher's key shouldn't implicitly validate an unrelated dependency. **Per-artifact**, inside a specific `<component>/<artifact>` you can add `<pgp value="LONG_KEY_ID"/>`, trusting that exact key for just that artifact, often alongside an `<ignored-keys>` entry to skip keys you can't resolve. The per-artifact form is tighter but verbose; the global scoped form is the usual choice generated by bootstrapping. Either way, prefer full 40-char fingerprints over short IDs to resist key-ID collision attacks.

code

xml · 10 lines
xml
<trusted-keys>
  <trusted-key id="3A5C9E12ABCDEF0123456789ABCDEF0123456789"
               group="com.fasterxml.jackson.core"/>
</trusted-keys>
<!-- per-artifact pin -->
<component group="org.example" name="widget" version="3.0">
  <artifact name="widget-3.0.jar">
    <pgp value="D5A1C2B3F4E5D6C7"/>
  </artifact>
</component>

go deeper

for a junior

Recognize that <trusted-key> with a fingerprint establishes who may sign; details of scoping are bonus.

for a middle

Explain global vs per-artifact trust, group/name/version scoping, and <ignored-keys>.

for a senior

Argue least-privilege scoping, fingerprint hygiene, and the distinction from <trusted-artifacts> escape hatches.

for a principal

Set conventions: how new keys are scoped and reviewed, when global trust is ever acceptable, governance of the escape-hatch elements.

## Two levels of trust Gradle lets you say *which key is allowed to vouch for which artifact*. There are two scopes. ### Global trusted keys Under the top-level `<trusted-keys>` element you list keys you trust broadly: ```xml <trusted-keys> <!-- trust this key for everything (broadest) --> <trusted-key id="D7BF96A92583B17EF2E47B23D5A1C2B3F4E5D6C7"/> <!-- trust this key only for a group (least privilege) --> <trusted-key id="3A5C9E12..." group="com.fasterxml.jackson.core"/> <!-- trust scoped to a single module + version --> <trusted-key id="9F8E7D6C..." group="org.example" name="core" version="2.1"/> </trusted-keys> ``` The `<trusted-key>` element's `id` is the key fingerprint/long ID. The optional **`group`, `name`, `version`, `file`, `regex`** attributes restrict where the key applies. With no attributes the key is trusted for *all* artifacts — convenient but broad. Scoping to a `group` matches how publishers actually sign (one org, one key, one group), and limits blast radius if a key is later found to be compromised. ### Per-artifact `<pgp>` / `<ignored-keys>` Inside a `<component>` you can pin trust to a single artifact: ```xml <component group="org.example" name="widget" version="3.0"> <artifact name="widget-3.0.jar"> <pgp value="D5A1C2B3F4E5D6C7"/> </artifact> </component> ``` The `<pgp value="..."/>` element trusts that key id for *only* this artifact. This is the most restrictive form. You'll also see **`<ignored-keys>`** with `<ignored-key id="..." reason="..."/>`, used when a signature references a key you cannot fetch or choose not to trust — Gradle then ignores that key (and typically falls back to checksum for the artifact). ### `trusted-keys` vs `trusted-artifacts` Don't confuse `<trusted-keys>` (key-based PGP trust) with `<trusted-artifacts>`, which whitelists *artifacts* (by name/regex) to skip verification entirely — that's an escape hatch, not signature trust. ### Choosing a scope | Form | Scope | Use when | |------|-------|----------| | `<trusted-key>` no attrs | all artifacts | a CA-like key you fully trust everywhere (rare) | | `<trusted-key group=...>` | one group/module | the normal case; least privilege | | `<pgp>` under artifact | one artifact | pinning a specific dependency tightly | | `<ignored-key>` | exclude a key | unresolvable/untrusted signer, fall back to checksum | ### Fingerprint hygiene Always store the **full 40-hex fingerprint**. Short (8-hex) and even long (16-hex) key IDs can be brute-forced to collide, letting an attacker craft a key whose ID matches one you trust. Bootstrapping records full fingerprints by default; preserve them.

  • What's the difference between <trusted-keys> and <trusted-artifacts>?
    <trusted-keys> establishes which PGP keys may vouch for artifacts (signature trust). <trusted-artifacts> whitelists artifacts to skip verification altogether — an escape hatch, not a key-trust mechanism.
  • When would you use <ignored-keys>?
    When a signature references a key you can't fetch from any key server, or one you deliberately won't trust. Gradle ignores that key and typically falls back to the recorded checksum for the artifact.

saying these in an interview costs you the question

  • Trusting every key globally with no group/scope (over-broad, large blast radius).
  • Confusing <trusted-artifacts> (skip verification) with <trusted-keys> (PGP trust).
  • Recording short key IDs instead of full fingerprints.

context