Skip to content

ClickOnce: add ClickOnce signing algorithm spec#843

Open
dtivel wants to merge 5 commits intomainfrom
dtivel/clickonce-spec
Open

ClickOnce: add ClickOnce signing algorithm spec#843
dtivel wants to merge 5 commits intomainfrom
dtivel/clickonce-spec

Conversation

@dtivel
Copy link
Copy Markdown
Collaborator

@dtivel dtivel commented Mar 31, 2025

See #842.

This PR adds a specification for changes to the ClickOnce signing algorithm. The goal is to make ClickOnce signing simpler, more correct, more efficient, and easier to maintain over time.

Rendered view: https://github.com/dotnet/sign/blob/cee18cb08d824564dd087974d00d11dde0fa2972/docs/specs/ClickOnce-Signing-Algorithm.md

(The rendered view is best viewed in light mode, because the animated GIF, which you can see clearly here, does not display well in dark mode.)

@dtivel dtivel requested a review from a team as a code owner March 31, 2025 00:25
Comment thread docs/specs/ClickOnce-Signing-Algorithm.md
1. Copy all files from the previous step, including the application manifest file itself, to a temporary directory.
1. Sign files in the following order: files alongside the application manifest, the application manifest itself, then the deployment manifest.
1. Copy the files back.
1. If the signed deployment manifest file is a `.vsto` file, copy it to the versioned application manifest file directory and overwrite if necessary.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm debating this. I feel like Sign CLI should not do this; Sign CLI should focus on signing, not emulating behaviors of a build-time task. Users who desire this behavior --- not just for .vsto files, but any ClickOnce app --- can do the copy after signing.

In trying to provide an equivalent experience to the VSTO build-time task, I think this will set up Sign CLI to do more non-signing-related chores. This is not a sustainable direction for Sign CLI.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Since it's not called out, I wanted to mention that if you are taking an existing published application and signing it's manifest/binaries, the deployment manifest will have to be re-generated after signing the application manifest to update its assembly identity in the deployment manifest.

You can see it happening for ClickOnce apps here:
https://github.com/dotnet/msbuild/blob/014f3faab9cc9a5c3387d04190fbca51abac34e7/src/Tasks/Microsoft.Common.CurrentVersion.targets#L6336

Comment thread docs/specs/ClickOnce-Signing-Algorithm.md Outdated
Comment thread docs/specs/ClickOnce-Signing-Algorithm.md Outdated
Comment thread docs/specs/ClickOnce-Signing-Algorithm.md Outdated
Comment thread docs/specs/ClickOnce-Signing-Algorithm.md Outdated
1. Interate through both [`AssemblyReferences`](https://learn.microsoft.com/dotnet/api/microsoft.build.tasks.deployment.manifestutilities.manifest.assemblyreferences?view=msbuild-17-netcore) and [`FileReferences`](https://learn.microsoft.com/dotnet/api/microsoft.build.tasks.deployment.manifestutilities.manifest.filereferences?view=msbuild-17-netcore), manually resolve `TargetPath` property to the base path of the application manifest file.
* Note: it seems like calling `Manifest.ResolveFiles()` would resolve the full file path for every file dependency in the application manifest. However, this fails because `ResolveFiles()` assumes dependency files do not have `.deploy` file extension, but they do. We could temporarily remove the `.deploy` extension and then call `ResolveFiles()` but a user's glob patterns might filter out files based on the `.deploy` extension. The safest option is to resolve file paths ourselves.
1. Copy all files from the previous step, including the application manifest file itself, to a temporary directory.
1. Sign files in the following order: files alongside the application manifest, the application manifest itself, the deployment manifest, then `setup.exe`.
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.

@dtivel dtivel force-pushed the dtivel/clickonce-spec branch from e9d0f84 to 608a5d3 Compare April 27, 2025 22:47

### Rollout strategy

The new ClickOnce signing algorithm is **opt-in**. Users must pass the `--use-new-clickonce-signing` flag to enable the proposed behavior. Without this flag, Sign CLI continues to use the current algorithm described in Appendix A.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Would it instead make sense to have a "version" of clickonce signing. The default today would be version 1 and this new one would be version 2.

The command-line argument would something like --signing-version 1 and --signing-version 2.

Then we could consider logging a warning when people are using signing version 1 saying that version 2 is better. Eventually we could make version 2 the default, and then users could still specify version 1 as an escape hatch in the future.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@jeffkl, would it be generic (i.e., --signing-version) or ClickOnce-specific (e.g., --clickonce-signing-version)? It seems like it should be the latter to enable signing parts to move independently of one another.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yes it should be specific to clickonce. I originally thought clickonce signing was its own gesture but its not


1. Load the deployment manifest, locate the referenced application manifest, and refuse to continue if it is missing.
1. Stage only the files referenced by the manifests, sign the payloads first, then the application manifest, then the deployment manifest, and finally the bootstrapper.
1. After each signing stage, refresh manifest metadata so hashes, sizes, and entry-point information are consistent with the newly signed bits.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Curious is there are plans to make the whole operation atomic where if any one task fails, none of the files are signed or if signing proceeds until one failure occurs, leaving some files signed and other unsigned.


### Rollout strategy

The new ClickOnce signing algorithm is **opt-in**. Users must pass the `--use-new-clickonce-signing` flag to enable the proposed behavior. Without this flag, Sign CLI continues to use the current algorithm described in Appendix A.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Given that we are still in the pre-release phase for SignCLI, I think breaking changes might be okay until the major release. I am proposing new behavior by default have a different option or an environment variable to fall back to the current behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants