Publish obfuscated and signed assemblies with ClickOnce

Akos Nagy
Feb 19, 2017

Let me start off by saying: ClickOnce is not awesome. Or at least I think so. Unfortunately, when it comes to publishing thick-clients (yes, I still have to do that on occassion, like we used to in the 2000s), there is no good alternative. Everything else I know about has a lot of dependencies and takes a lot of time to configure (I know that you can use Squirrel, maybe I should go with that?). And the ClickOnce auto-update feature is very handy for customers. So for know, let's assume I'm stuck with ClickOnce.

Publishing with obfuscation

Now when it comes to ClickOnce publishing, it really is just a click once, and you're done. If you want to sign the assemblies with a code-signing certificate, you can select that in Visual Studio as well (I'm not gonna mention the fact that this feature has never worked for me in Visual Studio — I've always done this from the command line).
But what if you want to obfuscate your assemblies before publishing? Why? Why not :) I know obfuscation is not a sure fire way to secure your code, but it's better than nothing.
Obviously, obfuscation must occur before signing. When you sign the code, a hash is created from the assembly — if you change the assembly, the hash changes and the original signature becomes invalid. And obfuscation changes your code - that's the point. So first, you have to obfuscate. And then, you can create the ClickOnce package.It is important that the ClickOnce package has to be the last step. When you create a ClickOnce package, then a signature is created for the package as well — if you change a file after packaging, the package becomes invalid. And just for the fun of it, you can even sign the ClickOnce package itself.

So you have to do the following:

  1. Build your assembly.
  2. Obfuscate the assembly.
  3. Sign the assembly.
  4. Create ClickOnce package.
  5. Sign the ClickOnce package.

I tried going this way, but it turned out to be too complex for me. A little bit more digging revealed that you can also update an existing ClickOnce package (technically, the manifest that belongs to and describes the package), if a file changes after the creation of the package. So I decided to go another way:

  1. Create the ClickOnce package.
  2. Obfuscate the assembly.
  3. Sign the assembly.
  4. Update the ClickOnce package.

Creating a ClickOnce package is easy. You use Visual Studio to set up everything on the UI, and then simply call msbuild with the /p switch. This will create a ClickOnce package.
Obfuscating the assembly is a custom step, I'm not gonna go into details. You can use your favorite obfuscator (hopefully it can be invoked from the command line or has a silent mode or something).
Signing the assembly is not that difficult either. You can use the signtool (sn.exe) with some arguments.
And finally updating the package with the changes requires some magic. No worries — if you need magic, call the mage. Mage.exe (Manifest Generation and Edit Tool) is a little tool that can generate and edit manifests and it can update your ClickOnce manifests. You just have to run that and you're done.

I have uploaded a heavily redacted, but hopefully usable version of my deploy script to Github. It has also some other nice little other features:

Please note that this is not a full-fledged CI script :) I basically hacked this together to make my life easier, but it might (and it probably does) contain some errors here and there (and I might have made some mistakes while redacting the script so I can make it public). Also, when it comes to PowerShell, I usually have no idea what I'm doing. So use this at your own risk :) and of course fixes or suggestions are welcome.

Akos Nagy
Posted in .NET