Skip to content

Add --allow-untrusted-root flag to nuget sign and dotnet nuget sign - #7201

Merged
kartheekp-ms merged 5 commits into
NuGet:devfrom
elantiguamsft:feature/allow-untrusted-signing
Mar 23, 2026
Merged

kartheekp-ms merged 5 commits into
NuGet:devfrom
elantiguamsft:feature/allow-untrusted-signing

Conversation

@elantiguamsft

@elantiguamsft elantiguamsft commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Feature

Fixes:
NuGet/Home#14798

Description

Add an --allow-untrusted-root / -AllowUntrustedRoot flag to dotnet nuget sign and nuget.exe sign that allows signing with certificates whose root CA is not in a trusted root store.

Problem

When signing NuGet packages with certificates issued by a root CA that isn't installed in LocalMachine\Root or CurrentUser\Root, CertificateChainUtility.GetCertificateChain fails with an UntrustedRoot chain status error. Installing root CAs into the trusted root store requires admin elevation (triggering a UAC prompt or failing in non-elevated CI containers), which is a significant friction point for build pipelines and developer machines.

Solution

Add a new allowUntrustedRoot parameter that moves UntrustedRoot from the error status flags to the warning status flags in GetChainStatusFlags. This uses the same mechanism NuGet already applies to self-issued certificates (see CertificateChainUtility.cs line 155), extending it to CA-issued certificates when explicitly opted in.

The flag is threaded through the full stack:

Layer Change
NuGet.Packaging SignPackageRequest.AllowUntrustedRoot property; new GetCertificateChain overload accepting bool allowUntrustedRoot
NuGet.Commands SignArgs.AllowUntrustedRoot; CertificateSourceOptions.AllowUntrustedRoot; wired through CertificateProvider.IsValid() so certs with untrusted roots aren't filtered out during store discovery
NuGet.CommandLine (nuget.exe) -AllowUntrustedRoot option
NuGet.CommandLine.XPlat (dotnet nuget sign) --allow-untrusted-root option

Key design decisions

  • Opt-in, defaults to false — no behavioral change unless explicitly enabled
  • Works on all TFMs including net472 — no dependency on X509ChainTrustMode.CustomRootTrust (.NET 5+)
  • Follows existing precedent — identical to how NuGet already handles UntrustedRoot for self-issued certificates

PR Checklist

@elantiguamsft
elantiguamsft requested a review from a team as a code owner March 9, 2026 16:40
@elantiguamsft
elantiguamsft requested review from nkolev92 and zivkan March 9, 2026 16:40
@dotnet-policy-service dotnet-policy-service Bot added the Community PRs created by someone not in the NuGet team label Mar 9, 2026
elantiguamsft added a commit to elantiguamsft/Home that referenced this pull request Mar 10, 2026
Add a new --allow-untrusted-signing flag to nuget sign and dotnet nuget sign
that allows signing NuGet packages with certificates whose root CA is not
installed in a trusted root certificate store. When this flag is set, the
UntrustedRoot X509 chain status is treated as a warning instead of a fatal
error. The certificate chain is still fully built and validated for structure.

Implementation PR: NuGet/NuGet.Client#7201

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martinrrm
martinrrm previously approved these changes Mar 17, 2026

@martinrrm martinrrm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, fyi @dtivel

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an opt-in capability for nuget sign and dotnet nuget sign to proceed with signing when the signing certificate’s chain reports UntrustedRoot, by downgrading that specific chain status from error to warning when explicitly enabled.

Changes:

  • Add AllowUntrustedRoot plumbing through SignArgs/SignPackageRequest and certificate discovery/validation so store filtering doesn’t exclude untrusted-root certs.
  • Introduce a new CertificateChainUtility.GetCertificateChain overload that accepts allowUntrustedRoot and adjusts chain status severity accordingly.
  • Add unit/functional tests plus CLI help text and localization updates for the new option.

Reviewed changes

Copilot reviewed 43 out of 45 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/CertificateChainUtilityTests.cs Adds a unit test covering the allow-untrusted-root chain behavior.
test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatSignTests.cs Adds xplat arg parsing tests for the new sign option.
test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetSignCommandTest.cs Adds nuget.exe SignCommand→SignArgs mapping tests for the new flag.
src/NuGet.Core/NuGet.Packaging/Signing/Utility/CertificateChainUtility.cs Adds overload and adjusts chain-status classification based on allowUntrustedRoot.
src/NuGet.Core/NuGet.Packaging/Signing/Authoring/SignPackageRequest.cs Adds AllowUntrustedRoot and threads it into signing chain construction.
src/NuGet.Core/NuGet.Packaging/PublicAPI/net8.0/PublicAPI.Unshipped.txt Declares new public API surface (property + overload).
src/NuGet.Core/NuGet.Packaging/PublicAPI/net472/PublicAPI.Unshipped.txt Declares new public API surface (property + overload).
src/NuGet.Core/NuGet.Commands/SignCommand/SignCommandRunner.cs Passes AllowUntrustedRoot into signing request and cert discovery options.
src/NuGet.Core/NuGet.Commands/SignCommand/SignArgs.cs Adds AllowUntrustedRoot to command args model.
src/NuGet.Core/NuGet.Commands/SignCommand/CertificateProvider.cs Ensures store-discovered certs aren’t filtered out solely for UntrustedRoot when enabled.
src/NuGet.Core/NuGet.Commands/SignCommand/CertificateFindOptions.cs Adds AllowUntrustedRoot to certificate source options.
src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt Declares SignArgs.AllowUntrustedRoot public API.
src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt Declares SignArgs.AllowUntrustedRoot public API.
src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx Adds localized description string for the new xplat sign option.
src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs Adds strongly-typed accessor for the new resource string.
src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Signing/SignCommand.cs Registers the new xplat sign CLI option and maps it into SignArgs.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf Adds localization entry for the new sign option description.
src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.resx Adds localized description string for the new nuget.exe sign option.
src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.Designer.cs Adds strongly-typed accessor for the new resource string.
src/NuGet.Clients/NuGet.CommandLine/Commands/SignCommand.cs Adds the new nuget.exe sign option property and maps it into SignArgs.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hant.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hans.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.tr.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ru.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pt-BR.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pl.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ko.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ja.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.it.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.fr.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.es.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.de.xlf Adds localization entry for the new sign option description.
src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.cs.xlf Adds localization entry for the new sign option description.
Files not reviewed (2)
  • src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.Designer.cs: Language not supported
  • src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs: Language not supported

Comment thread src/NuGet.Clients/NuGet.CommandLine/Commands/SignCommand.cs
Comment thread test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/XplatSignTests.cs
elantiguamsft and others added 4 commits March 20, 2026 10:26
Add AllowUntrustedRoot property to SignArgs, CertificateSourceOptions, and
SignPackageRequest. Add a new public overload of GetCertificateChain that
accepts allowUntrustedRoot parameter, making UntrustedRoot chain status a
warning instead of an error during certificate discovery and signing chain
validation. Wire up the option through both classic nuget.exe SignCommand
(--AllowUntrustedSigning) and dotnet nuget sign (--allow-untrusted-signing).
Works on net472 without requiring CustomRootTrust.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- CertificateChainUtilityTests: verify GetCertificateChain with
  allowUntrustedRoot=true returns a chain (UntrustedRoot as warning)
- NuGetSignCommandTest: verify -AllowUntrustedSigning parses to
  SignArgs.AllowUntrustedRoot=true, and default is false
- XplatSignTests: verify --allow-untrusted-signing parses to
  SignArgs.AllowUntrustedRoot=true, and default is false

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align CLI flag naming with existing dotnet nuget trust --allow-untrusted-root
per reviewer feedback. Renames:
- dotnet CLI: --allow-untrusted-signing -> --allow-untrusted-root
- nuget.exe: -AllowUntrustedSigning -> -AllowUntrustedRoot
- Resource keys: SignCommandAllowUntrustedSigningDescription -> SignCommandAllowUntrustedRootDescription

Internal API property names (AllowUntrustedRoot on SignArgs, SignPackageRequest,
CertificateSourceOptions) are unchanged - they were already correct.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add full XML doc comments to the new GetCertificateChain overload
including param/returns/remarks/exception documentation matching the
existing overload pattern. Clarify that allowUntrustedRoot only affects
signature certificate chains (no effect for timestamp chains).

Replace var with explicit types in the AllowUntrustedRoot test per
code review feedback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@elantiguamsft
elantiguamsft force-pushed the feature/allow-untrusted-signing branch from f6f19d7 to a1cc8fb Compare March 20, 2026 17:34
@elantiguamsft elantiguamsft changed the title Add --allow-untrusted-signing flag to nuget sign and dotnet nuget sign Add --allow-untrusted-root flag to nuget sign and dotnet nuget sign Mar 20, 2026
…ledException race)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@dtivel dtivel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks, @elantiguamsft!

kartheekp-ms pushed a commit to NuGet/Home that referenced this pull request Mar 20, 2026
…14798)

* Add proposal: --allow-untrusted-signing flag for nuget sign commands

Add a new --allow-untrusted-root flag to nuget sign and dotnet nuget sign
that allows signing NuGet packages with certificates whose root CA is not
installed in a trusted root certificate store. When this flag is set, the
UntrustedRoot X509 chain status is treated as a warning instead of a fatal
error. The certificate chain is still fully built and validated for structure.

Implementation PR: NuGet/NuGet.Client#7201

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix inaccurate references to nuget verify --allow-untrusted-root flag

Replace incorrect claims about a nuget verify --allow-untrusted-root CLI
flag with accurate references to the allowUntrustedRoot attribute on
certificate elements in nuget.config's trustedSigners section.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename --allow-untrusted-signing to --allow-untrusted-root

Align CLI flag naming with existing dotnet nuget trust --allow-untrusted-root
per reviewer feedback from @kartheekp-ms. The 'signing' word is redundant
since the command context already implies signing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community PRs created by someone not in the NuGet team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants