Signed Packages and Trust Policies

This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its dotnet CLI) and NuGet 7.9, as published in the MSBuild, .NET and NuGet documentation on learn.microsoft.com, which is the reference these pages are written and verified against. Material about .NET 11 / NuGet 8.0 is still in preview and is always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The C# Reference bibliography lists the reference material consulted while preparing these pages.

A NuGet package can carry two signatures. An author signature, added by the publisher, proves who built it; a repository signature, added by nuget.org to every package it accepts, proves where it came from. A consumer can simply let NuGet check that signed packages are intact, or go further and require that every package is signed by a signer it trusts. This page does both with NuGet 7.9.0 (.NET SDK 10.0.402) on Linux. Signing your own packages is on Signing Packages.

Inspecting signatures

dotnet nuget verify --all ~/.nuget/packages/humanizer.core/2.14.1/humanizer.core.2.14.1.nupkg
Verifying Humanizer.Core.2.14.1
Content hash: lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==

Signature type: Author
  Subject Name: CN=Humanizer (.NET Foundation), O=Humanizer (.NET Foundation), L=Redmond, S=wa, C=US, SERIALNUMBER=603 389 068
  SHA256 hash: B0F7E4AA75AD5585ADD09821B41AFE260404DC59D373477E065AED0D9F21681C
  Valid from: 03/11/2021 00:00:00 to 06/07/2024 23:59:59

Signature type: Repository
  Subject Name: CN=NuGet.org Repository by Microsoft, O=NuGet.org Repository by Microsoft, L=Redmond, S=Washington, C=US
  SHA256 hash: 5A2901D6ADA3D18260B9C6DFE2133C95D74B9EEF6AE0E5DC334C8454D1477DF4
  Valid from: 02/16/2021 00:00:00 to 05/15/2024 23:59:59

Both certificates have expired, and the package still verifies (exit code 0): the signatures carry timestamps, and a signature made while its certificate was valid stays valid. An unsigned package — Ledgerline's own, from the local feed — fails verification with exit code 1:

error: NU3004: The package is not signed.

Package signature validation failed.

What restore checks

With the default signatureValidationMode of accept, restore checks that signed packages have not been modified since signing (NU3008 if they have) and accepts unsigned packages. On Linux this verification is on by default since the .NET 8 SDK; DOTNET_NUGET_SIGNATURE_VERIFICATION=false turns it off — a detailed restore log then contained no signature lines at all. On macOS it is off by default and the documentation recommends leaving it off; on Windows it is always on.

Trusted signers

trustedSigners in nuget.config lists whose signatures are trusted. dotnet nuget trust maintains it:

Command Adds or does

trust list

Show the trusted signers.

trust author <name> <package>

Trust the author certificate that signed a local package.

trust repository <name> <package>

Trust the repository certificate on a local package (--owners restricts to nuget.org owners).

trust source <name>

Trust a source’s repository certificates, read from its service index.

trust certificate <name> <fingerprint>

Trust a certificate by fingerprint.

trust sync <name>

Refresh a repository’s certificates from its service index.

trust remove <name>

Remove a signer.

dotnet nuget trust author Humanizer ~/.nuget/packages/humanizer.core/2.14.1/humanizer.core.2.14.1.nupkg --configfile nuget.config
dotnet nuget trust source nuget.org --configfile nuget.config
dotnet nuget trust list --configfile nuget.config
Successfully added a trusted author 'Humanizer'.
Successfully added a trusted repository 'nuget.org'.
Registered trusted signers:

 1.   Humanizer [author]
      Certificate fingerprint(s):
        SHA256 - B0F7E4AA75AD5585ADD09821B41AFE260404DC59D373477E065AED0D9F21681C
 2.   nuget.org [repository]
      Service Index: https://api.nuget.org/v3/index.json
      Certificate fingerprint(s):
        SHA256 - 0e5f38f57dc1bcc806d8494f4f90fbcedd988b46760709cbeec6f4219aa6157d
        SHA256 - 5a2901d6ada3d18260b9c6dfe2133c95d74b9eef6ae0e5dc334c8454d1477df4
        SHA256 - 1f4b311d9acc115c8dc8018b5a49e00fce6da8e2855f9f014ca6f34570bc482d

trust repository nuget.org without a package failed ("Required argument missing for command: 'repository'"): it reads the certificate from a package; trust source is the form that reads it from the feed. Trusting the nuget.org repository with --owners (--owners "dotnetfoundation;microsoft") narrows it to packages from those nuget.org accounts.

Requiring trusted signatures

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

With both signers above trusted, a project referencing Humanizer.Core, Newtonsoft.Json and the unsigned Ledgerline restored the two nuget.org packages (both repository-signed by nuget.org) and rejected the third:

~/sign/S.csproj : error NU3004: Package 'Ledgerline 1.0.0' from source '~/lb/artifacts/feed': signatureValidationMode is set to require, so packages are allowed only if signed by trusted signers; however, this package is unsigned.

After dotnet nuget trust remove nuget.org, only the Humanizer author was trusted, and Newtonsoft.Json — author- signed by Json.NET and repository-signed by nuget.org, neither trusted — was rejected:

~/sign/S.csproj : error NU3034: Package 'Newtonsoft.Json 13.0.3' from source 'https://api.nuget.org/v3/index.json': This package is signed but not by a trusted signer.

The same NU3034 appeared after switching back to accept: once a trustedSigners list exists, signed packages must match it even in accept mode; accept only lets unsigned packages through. require is practical for organisations that sign their internal packages and trust nuget.org’s repository certificate (with --owners), and it blocks a tampered or substituted package even when no lock file would catch it.

Fingerprints and algorithms

trustedSigners entries store certificate fingerprints with their algorithm; dotnet nuget trust writes SHA-256 ones. On the signing side, NuGet 6.12 deprecated SHA-1 fingerprints for dotnet nuget sign/nuget sign (--certificate-fingerprint): NU3043 is a warning in .NET 9 and an error in the .NET 10 SDK (Signing Packages). When nuget.org rotates its repository certificate, dotnet nuget trust sync nuget.org adds the new fingerprint (the three above include past and current certificates).

Common mistakes

  • Expecting accept to ignore trusted signers — signed packages must still match the list.

  • Trusting the nuget.org repository without --owners under require — that trusts every package on nuget.org.

  • Forgetting trust sync after a repository certificate rotation — require starts rejecting new packages.

  • Enabling verification on macOS — documented as unsupported.