ClickOnce and Expiring Code Signing Certificates

There’s a really nasty bug in .NET 2.0’s ClickOnce deployment technology. If you deploy a smart client as availaible “off line” (launchable from the start menu) and you sign your manifest with a certificate from an official certificate authority (such as Thawte or VeriSign), you cannot renew your certificate and deploy to the same location! If you do, the next time someone starts your client it will barf up an error and won’t start. Examining the log will reveal the problem in the somewhat cryptic message “The deployment identity does not match the subscription”.

The problem is that the certificate authorities issue “renewed” certificates with a different private key. This makes up part of the ClickOnce app’s “identity” and ClickOnce validates this when starting an app to prevent tampering.

The only workaround is to uninstall and reinstall the app via the add/remove programs control panel applet. This really puts a kink in the whole “click once” thing, doesn’t it? I ran into this issue recently at work, as our code signing certificate was set to expire. Since I’m experienced enough to never trust Microsoft to do the right thing, I tested the “renewed” certificate in a test environment. Several Google searches later, I see that I’m not alone in discovering this problem.

To avoid having over 200 users fart around in the “add/remove programs” control panel applet, I came up with a kludge to have the client application uninstall itself and launch the ClickOnce installer, signed with the new certificate, from a new location. So after deploying the client signed with the new certificate, I deploy an update with the old certificate that contains the “reinstall” logic. Part of this trickery involves obtaining the “Public Key Token” for your app (I believe this comes from the certificate used to sign the ClickOnce manifest). The following snippet illustrates how to determine the public key token programmatically:

/// <summary>
/// Gets the public key token for the current ClickOnce app.
/// </summary>
private static string GetPublicKeyToken()
    ApplicationSecurityInfo asi =
        new ApplicationSecurityInfo(
    byte[] pk = asi.ApplicationId.PublicKeyToken;
    StringBuilder pkt = new StringBuilder();
    for (int i = 0; i < pk.GetLength(0); i++)
        pkt.Append(String.Format("{0:x}", pk[i]));
    return pkt.ToString();

Next, we need to find the uninstall string in the registry, based on the PublicKeyToken:

/// <summary>
/// Gets the uninstall string for the current ClickOnce app from the
/// Windows Registry.
/// </summary>
/// <param name="PublicKeyToken">The public key token of the app.
/// </param>
/// <returns>The command line to execute that will uninstall the app.
/// </returns>
private static string GetUninstallString(string PublicKeyToken, 
  out string DisplayName)
    string uninstallString = null;
    string searchString = "PublicKeyToken=" + PublicKeyToken;
    RegistryKey uninstallKey = Registry.CurrentUser.OpenSubKey(
    string[] appKeyNames = uninstallKey.GetSubKeyNames();
    DisplayName = null;
    foreach(string appKeyName in appKeyNames)
        RegistryKey appKey = uninstallKey.OpenSubKey(appKeyName);
        uninstallString = (string)appKey.GetValue("UninstallString");
        DisplayName = (string)appKey.GetValue("DisplayName");
    return uninstallString;

I then launch the uninstaller, using the Process class, and use some Interop calls to the Win32 API to find the uninstaller window and automatically “push” the “OK” button (would have been nice if the was a /silent switch so I wouldn’t have to do this). Finally, I launch ClickOnce for the new version of the client, signed with the new certificate, to update the user’s workstation. A zip file with this source code can be found here: Using these utility classes, I just insert the following code into the client’s startup routine to make it reinstall itself:

// Self-uninstall

13 thoughts on “ClickOnce and Expiring Code Signing Certificates”

  1. Hey I just wanted to compliment you on your blog. We have a similar situation with our click once application and while researching how to easily uninstall the thing for 50+ users I came across this and it was very helpful. Kudos to you!

  2. Hi,

    I was originally going to use your suggestion above but luckily MIcrosoft came up with a ‘workaround’…

    The solution above is only great if you can get to all the computers with the clickonce application installed. Ours is installed in cars with a very unstable GPRS connection…

    Just so you know…

    The C++ program on the page above works well although it shouldn’t have been a problem in the first place 🙂


  3. Johnny,

    I did see the MS article while researching this. Unfortunately, I’m using a certificate from Thawte, so the C++ program won’t work in my case. That only works if you “self sign”. So, I opted for “method 1” 🙂


  4. Great piece of work and documentation. We have 140 locations with an average of 3 workstations per location. I’ve been using VS 2005 since beta 1. The Click-Once technology is terrific but, if anything goes wrong, you’re hosed. I have had the self-signing fail (you don’t know until it is deployed and tested) because I added components to the app that it didn’t consider the “same” as before. Lately, I have had to publish twice (bumping the ver each time) to get the clients to install.
    Anyway, thanks for your info here. I will keep this link somewhere safe.

  5. Hey Jim, i just wanted to thank you a lot for your blog. it helps a lot. i have a question though, i incorporated your self-uninstall in my clickonce application. i noticed that it does not remove the application in the C:\Documents and Settings\user name\Local Settings\Apps\2.0 folder. unlike if you uninstall manually the application in the add/remove programs it clears the said folder. is it ok? thank you very much again. have a nice day! =)

  6. I read your comment above: “I deploy an update with the old certificate that contains the GÇ£reinstallGÇ¥ logic”. I can’t understand how you are able to publish an update if the original certificate has expired. Did I misunderstand something? I am not able to update my app after the certificate has expired.

  7. Hi Jim, Thanks for the blog, its been very helpful. Although the new sign tool technique is probably the way to go I’m still interested in your uninstall technique. However the link to appears to be dead. Do you plan to make it available again? If so I’d like to give it a try.

    PS Sorry for trying to register, I was confused!

  8. Eric – try again, it should work now. I recently changed hosting companies and lost that file in the shuffle!

  9. Nice solution. One comment though: the format string for getting the PublicKeyToken has a bug if there’s a 0 in the token. It will drop the 0 if it’s the first part of the byte. This code will fix it:

    String.Format(“{0:x2}”, pk[i]);

    Thanks Jim!


Comments are closed.