CLI tool
dotnet/sign avatar
dotnet/sign

Sign CLI keeps the private key in Azure Key Vault and sends only the digest

Code Signing CLI tool supporting Authenticode, NuGet, VSIX, and ClickOnce

585 stars120 forksC#MIT

At a glance

What is it?
Sign CLI is the MIT licensed .NET Foundation tool for signing Windows artifacts, NuGet packages, VSIX and ClickOnce manifests with keys held in an Azure Key Vault HSM, so CI never touches the private key. It suits a release pipeline on Windows that signs many artifact types, and the current version is RSA and Azure Key Vault only.
Who is it for?
Sign CLI fits a Windows release pipeline that signs several artifact types with a key that must never leave an HSM, since the tool computes the digest locally and sends only that to Azure Key Vault for signing. It does not fit a team that wants ECDSA, a different cloud provider, or signing on Linux, because the current version is limited to RSA and Azure Key Vault, and Authenticode signing of executables and libraries is only possible on Windows at this time.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The point is that the private key never leaves the vault

Sign CLI is a code signing command line tool supporting Authenticode, NuGet, VSIX and ClickOnce, and the project states its aim plainly: making it easier to integrate secure code signing into a CI pipeline by using cloud-based hardware security module protected keys.

It is part of the .NET Foundation and operates under their code of conduct, and it is licensed under MIT.

The mechanism is the interesting part, and the design section is unusually explicit about it. Given an initial file path or glob pattern, the tool recursively searches directories and containers to find signable files and containers. For each signable artifact, it uses an implementation of System.Security.Cryptography.RSA that delegates the signing operation to Azure Key Vault.

Then the detail that matters: the tool computes a digest, that is a hash, of the to-be-signed content and submits the digest, not the original content, to Azure Key Vault for digest signing. The returned raw signature value is then incorporated in whatever signature format is appropriate for the file type.

The last sentence of that paragraph is the point of the whole tool: signable content is not sent to Azure Key Vault.

So the private key stays in the vault, the artifact stays in your build, and only a hash crosses the network. That is what makes it usable in a pipeline that handles proprietary binaries.

Installing it as a dotnet tool, on Windows

The prerequisites are specific and they narrow who can use the tool.

You need an up-to-date x64 based version of Windows currently in mainstream support, the .NET 8 SDK or later, and the Microsoft Visual C++ 14 runtime. The Windows requirement is not incidental, and the documentation explains why later: signing .exe and .dll files and other Authenticode file types is only possible on Windows at this time. The recommended solution is to build on one agent and sign on another, using jobs or stages where the signing steps run on Windows, and to run code signing on a separate stage so secrets are not exposed to the build stage.

The install itself is a single dotnet tool command, run in a command prompt in the directory you want the tool installed into.

code
dotnet tool install --tool-path . --prerelease sign

Note the --prerelease flag, which is consistent with the release list: the tagged versions are v0.9.1-beta.26475.3 from 2026-09-26, v0.9.1-beta.26431.1 from 2026-08-31 and v0.9.1-beta.26371.2 from 2026-08-03.

Those build numbers are the daily build identifiers that .NET repositories use for pre-release builds, and they are also the version of the tool you get from the prerelease channel. To run the tool you execute sign from the same directory, because --tool-path put it there rather than on your global path.

The latest version is published on NuGet.org as the package sign.

Nine file types, three of them through other tools

The supported file types list is long, and three of the entries are delegating to other tools rather than implementing the signature format itself.

The base list covers .msi, .msp, .msm, .cab, .dll, .exe, .appx, .appxbundle, .msix, .msixbundle, .sys, .vxd, .ps1 and .psm1, plus any portable executable file, the last of which is handled via AzureSignTool. Then there is .vsix for Visual Studio extensions, ClickOnce .application and .vsto handled via Mage, and .nupkg for NuGet packages.

So the Authenticode formats, the VSIX format, the ClickOnce manifests and the NuGet package format are four different signature formats, and the tool has to know which one to write.

The order of signing matters and the documentation explains it. Some artifacts are containers that contain other signable file types, and a NuGet package frequently contains .dll files. The tool signs all files inside-out, starting with the most nested files and then the outer files, ensuring everything is signed in the correct order.

That inside-out rule is not a detail you can skip. A container whose contents are unsigned, or signed after the container, is a container that fails validation on the machine that installs it, and the failure looks like a trust problem rather than an ordering problem.

ClickOnce has its own rules about what must be present

The ClickOnce section is the longest part of the documentation and the easiest to get wrong.

Generally you will want to sign an entire package and all its contents, that is the deployment manifest, which is a .application or .vsto, the application manifest, which is a .exe.manifest or .dll.manifest, and the underlying .exe and .dll files themselves. To do this you ensure the entire contents of the package are available, that is the whole publish folder from your build, and you pass the deployment manifest as the file to sign. The rest of the files are detected and signed in the proper order automatically.

There is a second case: re-signing just the deployment manifest, for example to change the Deployment URL while leaving the rest of the contents the same. You pass the deployment manifest as the file to sign as in the case above, but do not have the rest of the files present on disk alongside it. The tool detects that they are missing and just updates the signature on the deployment manifest.

The conditions on that second case are hard limits. The documentation says this is strictly for re-signing an already-signed deployment manifest, you cannot have a signed deployment manifest that points to an unsigned application manifest, and you must take care to sign all manifests with the same certificate otherwise the application will not install.

There is also a filter parameter to use with the file list, and the documentation gives the shape of it.

code
**/ProjectAddIn1.*
**/setup.exe

Least privilege for the signing identity

The best practices section is a security checklist, and it is more specific than most.

Create a ServicePrincipal with minimum permissions, and note that you do not need to assign any subscription-level roles to this identity, only access to Key Vault is required. That is the key point: the signing identity can be scoped to one vault and nothing else, without touching a subscription.

Inside the vault you follow the Azure Key Vault best practices, and the Premium SKU is required for code signing certificates to meet key storage requirements. If you use Azure role-based access control, the signing account needs the Key Vault Reader and Key Vault Crypto User roles. If you use access policies instead, the access policy needs key permissions for Cryptographic Operations, specifically Sign, and Key Management Operations, specifically Get, which the documentation notes is only for the public key and not the private key, plus certificate permissions for Certificate Management Operations and Get.

The remaining items are process rules rather than configuration. Isolate signing operations in a separate leg of your build pipeline. Ensure that this CLI and all input and output files are in a directory under your control. Execute the CLI as a standard user, since elevation is not required. And for GitHub Actions, use OIDC authentication from your GitHub Action to Azure, which avoids a long lived secret in the runner.

The build variables the pipeline needs are listed as well: Tenant Id, Client Id or Application Id, Key Vault Url, Certificate Id, and then either a Client Secret for Azure DevOps Pipelines or a Subscription Id for GitHub Actions.

The certificate itself has to be an RSA-HSM key

The last section of the documentation is about creating the certificate, and it starts with a constraint that explains the RSA-only design.

Code signing certificates must use the RSA-HSM key type to ensure the private keys are stored in a FIPS 140-2 compliant manner. While you can import, as the sentence begins, the point stands: the key type is not a preference.

This is where the design limitation meets the platform. The documentation says that while the current version is limited to RSA and Azure Key Vault, it is desirable to support ECDSA and other cloud providers in the future.

So there are two constraints on the key, and they are the same constraint seen from two sides. The tool implements digest signing through an RSA implementation, and Azure Key Vault is the only backend wired up. A team whose certificate authority issues ECDSA code signing certificates, or that wants to keep its keys in a different HSM, has to wait or to write something else.

That is a reasonable boundary for a 0.9.x tool, and it is the kind of thing to check before wiring it into a release pipeline, because the certificate is usually chosen long before the signing tool is.

The repository is set up for a large .NET project

The tree shows a repository that is used to contributing back to.NET, not just to consuming a tool.

There is sign.sln at the root, plus src/ and test/ directories, and the directory structure for a multi-project.NET repository with the shared configuration files: Directory.Build.props, Directory.Build.targets and Directory.Packages.props, along with a SdkTools.props and a global.json that pins the SDK. NuGet.Config,.editorconfig and.gitattributes are present, and.config/ and.github/ hold the tooling and workflow configuration.

There is an eng/ directory and a scripts/ directory, which in the.NET layout usually hold the build engine glue and the release scripts, and a.vsts-ci.yml alongside a.vsts-pr.yml for Azure DevOps pipelines, the build and the pull request validation.

The security and governance files are notable: SECURITY.md, triage-policy.md and CODE-OF-CONDUCT.md, plus THIRD-PARTY-NOTICES.txt and LICENSE.txt, and an es-metadata.yml.

The sample workflows are the practical entry point. The documentation points at docs/azdo-build-and-sign.yml for Azure DevOps and docs/gh-build-and-sign.yml for GitHub Actions, so the recommended way to use the tool is to copy one of those pipelines rather than to write your own from scratch.

Editorial conclusion

Sign CLI fits a Windows release pipeline that signs several artifact types with a key that must never leave an HSM, since the tool computes the digest locally and sends only that to Azure Key Vault for signing. It does not fit a team that wants ECDSA, a different cloud provider, or signing on Linux, because the current version is limited to RSA and Azure Key Vault, and Authenticode signing of executables and libraries is only possible on Windows at this time. Before adopting it, confirm your Key Vault is on the Premium SKU, since the documentation says that is required for code signing certificates, create the service principal with only Key Vault access, and read the ClickOnce section closely, because re-signing only a deployment manifest has conditions that will break the install if the manifests end up signed with different certificates.

Frequently asked questions

How do I install Sign CLI?

Run dotnet tool install --tool-path . --prerelease sign in a command prompt in the directory where you want the tool, then execute sign from that same directory. The prerequisites are a supported x64 Windows, the .NET 8 SDK or later, and the Visual C++ 14 runtime.

Does Sign CLI send my files to Azure Key Vault?

No. The tool computes a digest of the to-be-signed content and submits only the digest to Azure Key Vault for digest signing, so signable content never leaves your build. The raw signature that comes back is then written into whichever signature format the file type needs.

Which file types can Sign CLI sign?

Authenticode types including.msi,.cab,.dll,.exe,.appx,.msix,.sys,.ps1 and any portable executable, plus.vsix,.nupkg, and ClickOnce.application and.vsto files. The PE handling is done via AzureSignTool, and ClickOnce via Mage.

What permissions does the Sign CLI identity need?

The documentation says to create a ServicePrincipal with minimum permissions and notes that no subscription-level roles are needed, only access to Key Vault. Inside the vault you need the Key Vault Reader and Key Vault Crypto User roles, or the equivalent access policy with Sign and Get key permissions, and the vault must be on the Premium SKU.

Official sources

  1. dotnet/sign on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dotnet-sign.svg)](https://hysenlabs.com/projects/dotnet-sign)

Was this useful?