Open Source Security

NuGet Signed Packages Verification

NuGet supports signed packages — author signatures, repository signatures, and verification modes. A practical guide to enforcing it properly.

Nayan Dey
Senior Security Engineer
5 min read

NuGet has supported package signing since 2018, but enforcement of it in consumer environments remains patchy in 2024. Most .NET shops run with signatureValidationMode at its default, which is effectively "accept everything that restores cleanly." That leaves signing as a nice-to-have in theory and a nothing in practice. The cost to move to real enforcement is modest, the posture improvement is real, and the specific gotchas are well-understood. This post is the guide I give customers when they decide to turn signing enforcement on.

Two signature types exist

NuGet distinguishes author signatures (applied by the package author using their own certificate) from repository signatures (applied by NuGet.org or a private registry on top of whatever the author signed). A package on NuGet.org typically has both: the author signed with their cert when they pushed, and NuGet.org countersigned to attest the package was accepted into the registry.

This matters because enforcement policies can require one, the other, or both:

  • Require author signature only: package must be signed by its author.
  • Require repository signature only: package must have come from a trusted registry.
  • Require both: stricter, catches packages that bypass the registry.

Most production policies require repository signing (proof of provenance) and accept that author signing is opt-in per upstream author.

How verification mode actually works

The setting in NuGet.Config:

<config>
  <add key="signatureValidationMode" value="require" />
</config>

With this set:

  • accept (default): all packages accepted, signatures not required.
  • require: packages must have a valid signature matching the configured trusted signers.

Trusted signers are declared in the same file:

<trustedSigners>
  <repository name="nuget.org" serviceIndex="https://api.nuget.org/v3/index.json">
    <certificate fingerprint="0E5F38F57DC1BCC806D8494F4F90FBCEDD988B46760709CBEEC6F4219AA6157D"
                 hashAlgorithm="SHA256"
                 allowUntrustedRoot="false" />
    <owners>Microsoft;dotnetfoundation</owners>
  </repository>
</trustedSigners>

The owners element restricts to specific publishers. Without it, any package from that repository is accepted. With it, only Microsoft- and .NET-Foundation-owned packages pass — which is too narrow for most real projects but illustrates the shape.

What happens when enforcement trips

Packages that fail verification cause dotnet restore to error out:

error NU3004: The package is not signed.
error NU3012: Package signature validation failed. The package's signature is invalid.

The common failure modes in practice:

  1. Legacy unsigned packages from smaller open-source projects that never adopted signing.
  2. Package owner name mismatches where the configured <owners> list doesn't include an author you actually use.
  3. Certificate expiration, where an older package was signed with a cert that has since expired. NuGet has countersigning mechanisms to handle this but they require timestamps that older packages may lack.

The first failure is the important one. It forces a decision for every unsigned package: accept it (add to an allowlist), fork it (publish a signed copy internally), or replace it.

The enterprise rollout pattern that works

Four phases over 6–12 weeks:

Phase 1 — Audit mode. Turn on signatureValidationMode=require in a branch, run CI, collect every failure. Don't enforce in main.

Phase 2 — Categorize. Each failure is (a) a legitimate signing gap (accept with allowlist entry), (b) a replaceable package (plan removal), or (c) a package worth forking (plan internal republish).

Phase 3 — Narrow enforcement. Merge a NuGet.Config that requires signing, with explicit allowlist entries for the known unsigned packages. Restores now succeed because the allowlist catches the known gaps.

Phase 4 — Ratchet down. Over time, narrow the allowlist as packages adopt signing upstream or are replaced. The goal is a short, documented allowlist that only contains exceptions that have a written justification.

This pattern avoids the two common failure modes: flipping enforcement on and breaking all CI overnight (phase 1 pattern), or enforcing without an allowlist and living with constant friction (phase 3 without phase 2).

Private feeds and signing

For enterprise private feeds (Artifactory, Nexus, GitHub Packages, Azure DevOps Artifacts), the registry can re-sign packages on ingest or pass through author signatures. The specific behavior depends on the feed:

  • Artifactory: supports both pass-through and registry-level signing. Configure explicitly.
  • Nexus: pass-through by default; registry-level signing available as a plugin.
  • Azure Artifacts: handles Microsoft ecosystem signing transparently.
  • GitHub Packages: repository-scoped, inherits GitHub token-based auth.

The implication for cross-feed consumption: a package pulled through a private feed may lose its NuGet.org repository signature if the feed doesn't pass it through. Plan for this in the trustedSigners configuration.

Certificate trust decisions

allowUntrustedRoot="false" is the correct default. Setting it to true accepts self-signed or non-chain-validated certificates, which defeats most of the purpose. The one case where you might want to: your organization has its own CA for signing internal packages. In that case, install the CA in the system trust store (rather than in NuGet config) so it applies to all processes.

Common mistakes

  • Forgetting timestamps. Signed packages must include a trusted timestamp; without one, the signature becomes invalid when the cert expires. NuGet 6.x handles this automatically; older tooling didn't.
  • Allowlisting packages by name instead of by owner. Owner lists scale better. Package-name allowlists age poorly.
  • Setting validation mode without trusted signers. The config must be complete, or every restore fails.

How Safeguard Helps

Safeguard's .NET support detects signature validation mode per-project, identifies packages that would fail under require mode, and helps draft the trusted-signers allowlist from the audit output. Policy gates can enforce that production releases only consume signed packages, with allowlisted exceptions tracked and reviewed. Griffin AI surfaces which unsigned packages have adopted signing in recent releases so the allowlist can tighten over time without manual tracking. For teams rolling out NuGet signature enforcement, Safeguard compresses the multi-phase adoption into a platform-managed workflow.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.