ClickOnce and Expiring Certificates

The certificate you use to sign your ClickOnce deployment will expire at some point. In the past, creating or purchasing a new certificate and deploying an update to your application always required the customers to uninstall and reinstall the application. This is no longer always the case. This GoldMail summarizes this issue, and provides solutions if it is a problem for you.

This download contains the written article with details and sample code (VS2008, C#). If you are a VB developer and can’t figure out how to translate this to VB, post a query and I’ll see what I can do for you.
ClickOnce_ExpiringCerts.zip

[edit 7/7/2011 Move zip file to Azure blob storage]

Tags:

36 Responses to “ClickOnce and Expiring Certificates”

  1. Deb Says:

    Hi Robin. Do you happen to have a VB version of your “ClickOnce and Expiring Certificates” sample code?

    • robindotnet Says:

      I don’t, but I will write one and post it here w/i the next week.

      • Fergal Reilly Says:

        This could be incredibly handy for me. I’m trying to migrate all my users of one application to another in one easy movement.

        My intention is to use a version of this code to uninstall their current application, and install the new application.

        Question though, you use the IntPtr type in the C# code, how would this translate to VB.Net, since VB doesnt support pointers?

        Thanks
        Fergal

        • robindotnet Says:

          It IS incredibly useful for all kinds of things. I’m going to be posting the code in both C# and VB in the next few days, along with an updated version of the article and a flowchart that includes .NET 4.0. I’m hoping to have at least the code out there by the end of the weekend, and will add a link pointing to the new post from the old one, or add a link to the old one, or something like that.

  2. hardeepbhullar Says:

    Hello Robin,

    I have developed a WPF Browser Application for video chat but i am getting problem of installing certificate on client machine.Please help me how can i install certificate automatically on client’s machine.Right now i have to install certificate manually on each machine.

    Thanks and best regards.
    Hardeep Singh Bhullar
    http://hardeepbhullar.blogspot.com/

    • robindotnet Says:

      Please do post your questions in the MSDN Setup & Deployment and ClickOnce forum. so everyone can benefit. I answer questions there regularly.

      As for installing certificates, there is no way to do this without the user knowing about it. If you could install a certificate silently, then this would completely invalidate the use of certificates, because anyone could make one and install it.

      You can send the cert to the user and have them save it and double-click on it, and it will install it. I think you can also create a script to install it, but it WILL prompt the user for approval. You can’t do this with ClickOnce, but you might be able to do it with an MSI package, and deploy it as a prerequisite to the ClickOnce application. I don’t do S&D, but if you post to the forum, someone else will know if that’s possible.

  3. Fergal Reilly Says:

    I’ve made a couple of modifications to your code. Its a bit of a hash (I’m still getting to grips with C# – I’m a VB developer at the moment), but its functional, which is the important thing.

    Made the following changes:

    1)Extracted the functionality to an external DLL, to that it can be referenced in any application (even VB apps). The deploymentUtils function exposes two methods to the host: UninstallMe(), and InstallNewVersion(string URL). Both should be self-explanatory.

    2) Modified the install code to search for the “Application Install – Security Warning” (our certs arent signed to the level that stops these appearing), and auto-clicks the Install button.

    Thanks again for the help. Theres not a hope of my having developed this on my own.

    Fergal

  4. Rogier Says:

    Hi Robin, this post saved the day for me.

    I have been using ClickOnce for just exactly one year and 2 days :-).

    Thanx!

  5. Omar Delgado Says:

    Robin,

    I’ve been dealing with this problem for a while now. Currently we are developing in vs 2005 and yesterday i had to uninstall an reinstall our application on about 50 computers. I use VB .net so if you have the VB version that would be the best.

    Thanks

  6. Mike Says:

    Robin,

    I think you’ve just saved my life. I’ve had to change the Target CPU from AnyCPU to x86 to get a ClickOnce app to run under Windows 7, and I’ve just discovered that this will break auto-updates.

    I’ve downloaded your example code from MSDN and had a quick look at it, and I have what may be a stupid question. The code which searches for the OK button on the uninstall dialog assumes that the caption of the button is ‘&OK’. What if the user’s locale/culture is not English? If the button caption is something other than ‘OK’, will the auto-click code still find the button? I haven’t yet tried your code – just thought I’d ask first.

    My ClickOnce app is deployed internally to employees in many countries, and I can’t make any assumptions about their regional settings.

    Also, do you have a copy of the modifications that ‘Fergal Reilly’ referred to on 26 Nov 09? I use self-certification and would appreciate seeing the code that auto-clicks the security warning dialog as well as the uninstall dialog.

    Thanks.

    • robindotnet Says:

      Hi,
      That’s a good question. I think you’re going to have to figure out what appears on that button for each culture that you support, in order to click it programmatically. I’ve got some uninstall/reinstall code working for a VSTO app, and it takes just “OK” instead of “&OK”, if you can believe that.

      I don’t have a copy of the changes made by Mr. Reilly. I suppose you could search for that the same way the code searches for the other windows.

      Robin

  7. riken Says:

    Robin,

    I have some queries:

    Our target framework is 2.0 and our users are with mixed OS XP (most of the users),Vista,Win7 etc…

    1) If our certificate is going to expire on say 15th Feb 2011, would there be any problem if we give an update before that with new certificate?
    1.1 what will happen if current certificate is of ABC provider and new certificate is of XYZ provider ?
    1.2 would there be any problem in just renewing or keeping certificate provider same?

    2) As our target framework is 2.0, if we release after certificate expiry, all users have to uninstall and reinstall or is there any work around?

    • robindotnet Says:

      Hi,
      1) I would definitely update it before the old certificate expires if you can. I couldn’t, and I didn’t realize the scope of the problem until after it expired. If you do it beforehand, you can still release an upgrade to uninstall the application programmatically and install a new one with the new certificate.
      1.1 & 1.2. The provider doesn’t matter. When you get a new certificate, even when it’s from the same provider, it will have a different public key token. So it doesn’t matter if it’s the same or different provider.
      2) The only workaround is to programmatically uninstall and install the app. You could extend the certificate as a test certificate, but this is not the best solution, because then the publisher is always ‘unknown’. Here is the final version of this article posted on MSDN. It also has a link to the posted code samples.
      Good luck!

  8. riken Says:

    3) What will happen to current users with our application installed when certificate will expire ? will our application crashed after certificate expiry if we dont give update before expiry?

    • robindotnet Says:

      That is actually explained in the article. Current users should be able to continue to run the application. New users will get “unknown publisher” when they try to install the application (you’ll want to check the raticle on this one). You will not be able to deploy any updates with your expired certificate. And then when you deploy an update with the new certificate, the application will not run and upgrade on all machines. It might work if they have .NET 3.5 SP-1 installed, but you said your application targets .NET 2.0.

      The problem with waiting until your certificate expires is that you have to use RenewCert to create a test certificate with the same public key token that you can use to sign an update. It will show up as “unknown publisher”, and you also don’t want to wait until your certificate expires to create and try out that test certificate in case it doesn’t work.

  9. riken Says:

    Thanks Robin

  10. riken Says:

    Hi robin,

    Our certificate is going to expire on 14th feb, and we planned to give new update with newer certificate.

    In our testing we found that:
    1) App with old certificate gets updated successfully, So we call this as “new certified app”.
    2) But when we try to provide another update on this “new certified app”, it updates properly but while starting the app, it crashes and give below exception:

    Message: The system cannot find the path specified. (Exception from HRESULT: 0x80070003)

    statck trace: at System.Deployment.Internal.Isolation.IActContext.SetApplicationRunningState(UInt32 dwFlags, UInt32 ulState, UInt32& ulDisposition)
    at System.ActivationContext.SetApplicationState(ApplicationState s)
    at System.AppDomain.SetupDomainForApplication(ActivationContext activationContext, String[] activationData)
    at System.AppDomain.SetupApplicationHelper(Evidence providedSecurityInfo, Evidence creatorsSecurityInfo, ApplicationIdentity appIdentity, ActivationContext activationContext, String[] activationData)
    at System.AppDomain.SetDomainManager(Evidence providedSecurityInfo, Evidence creatorsSecurityInfo, IntPtr parentSecurityDescriptor, Boolean publishAppDomain)
    at System.AppDomain.SetDefaultDomainManager(String fullName, String[] manifestPaths, String[] activationData)

    Note: we searched for the problem and found something on “http://support.microsoft.com/kb/911792” but we are sure that it’s not the problem in our case.

    Thanks in advance for your help.

    • robindotnet Says:

      Sorry, I missed the follow-up post. Did you figure your problem out?
      –RobinDotNet

      • riken Says:

        yes, we were able to find the cause of problem.

        I want to point out one thing related to your reply on January 21, 2011 at 12:52 am. In that, in first point you mentioned that before certificate expiry update can be given successfully(provided target framework is 2.0). But we found that any certificate change before/after certificate expiry, users with .net 2.0 will not be able to get app update, in fact those user will not be able to start their app. They have to uninstall and reinstall the app manually.

        • robindotnet Says:

          I think you misunderstood. What I was saying is you can push an upgrade to the current version with the soon-to-be-expired certificate, NOT the new one. The upgrade can programmatically uninstall that version (i.e. itself), and then install a new version of the application from a different URL with the new certificate.

          If you do not do this before the certificate expires, then it is much more difficult — you have to use RenewCert to create an extended, nontrusted certificate to use to allow you to publish a new version with the uninstall/reinstall code in it. Because you can not publish the application with an expired certificate.

  11. Pedro Says:

    Hi Robin,

    i allready used this method a couple of times , and i can say that it really saved my neck.
    Thanks.

    Pedro

  12. Daniel Says:

    Hi Robin,

    Could you clarify the steps for just updating a pre-requisite? This is a bit simpler but I’m a bit confused about what to do.

    i.e. I have updated a custom prerequisite and I would like to force existing users to install the new prerequisite.

    I don’t need to update certificates, and don’t wish to change deployment URLs.

    Do I do the following?
    — Current deployment is 1.1 with the old prerequisite.
    — Release a forced updated to 1.2 with a new prerequisite. (At this point the application will update but the prerequisite will not).
    — In version 1.2, run a forced uninstall/reinstall of 1.2 again, calling setup.exe. This will install the new prerequisite.

    How does that compare to simply packaging up the pre-requisite installer exe and running it once at startup?

    Thanks,
    Daniel.

    • Daniel Says:

      Oh wait I realise that won’t work because the last step will go in a loop forever. I think I’m stuck here.

    • robindotnet Says:

      Hi Daniel,

      1. Publish the application to a new URL with the new prerequisite with a version at least 2 numbers higher than the current one.

      2. Publish an update to the version in production, make it version + 1. This update should uninstall the application and fire off the installation of the new one (process.start) and then exit.

      The user will run his application, get the update (from the old link). The application will uninstall itself, then run setup.exe from the new URL, which will install the new prerequisite if needed, and then install the ClickOnce application.

      The ClickOnce application may not run w/o the prerequisite installed. For example, if you change the prerequisite from .NET 2 to .NET 3.5, the app won’t run w/o .NET 3.5 — it HAS to be installed before the ClickOnce application actually runs.

      Your other choice is to publish a new version to the old URL and send people the link to setup.exe and ask them to run it. In my experience, unless you can go around to each computer and install it, you can’t ensure that people will actually install it. We have thousands of users all over the world, so forcing the uninstall and reinstall was the only way we could upgrade from .NET 2.0 to .NET 3.5. It was seamless, we didn’t get any negative feedback from the customers.

      Robin

      • Daniel Says:

        Thanks Robin,

        That much I understand.

        But then won’t the application will have a new deployment URL from now on? This is something I’m trying to avoid.

        Or do I set up the “version +2” check for updates at the old link again?

        Daniel.

        • robindotnet Says:

          Hi Daniel,
          Yes, it will have a new deployment URL. Other than having your users run setup.exe, there is no way to keep the same URL.
          If you can tell when all of your customers have migrated to the new version, you could wait for that to happen, and then move them back to the original URL.

          Why does it matter if the deployment URL changes?
          Robin

          • Daniel Says:

            Hi Robin,

            Thanks for your reply. The problem with changing the deployment URL is that we have thousands of customers with printed instructions with the old URL, and also with emails saved in their inbox saying “thank you for your purchase, here is the download link”. It’s not a huge problem but we’d prefer if the download link was fairly permanent.

            Also the pre-requisite is subject to be updated every year or so, so this will mean the link will keep changing which might annoy people.

            Otherwise I might just deploy the pre-requisite exe every time, and have a Process.Start routine when necessary. It’s not a large download. It’s no guarantee of course.

            Daniel.

          • robindotnet Says:

            Hi Daniel,

            The URL to your deployment could change if your server name changes, your DNS names change, etc. There are a multitude of reasons this can happen. (It happened to me because we changed CDN companies).

            I recommend you make one change — give a URL to an install page instead of to the deployment itself. You want to have control of that deployment URL, and be able to change it or move the deployment if you need to for any reason. We have an install page that just has a little information about the product and a big fat install button. We host the web page on our website, so we have control over where the button goes when the customer clicks it. You can provide that URL in all of your literature and your e-mails.

            If you can change it once, then you’re set for life going forward, you just have to change that one web page when the application changes.

            In the meantime, if you have people with that URL and they use it to install, and you’ve put the version there that does the uninstall/reinstall, it will install, then uninstall and reinstall the right one. When we put in the uninstall/reinstall code, we put in a dialog box explaining what it was about to do, and why, so the user wasn’t confused.

            I’ll also share that we use a CNAME’d link for our installation, So our installation link may be installgm.goldmail.com, but it points to a specific location and folder where we host our deployment. This way, if we change the server name, we can just move the deployment and change the CNAME information.

            Basically, it’s up to you. You didn’t say what prerequisite you are changing. You’ll have to see if your application will run without the right version of the prerequisite installed. (It won’t if it’s .NET — I already tried that. Haha.) It’s easier to move the deployment, but you have to figure out what works best for you. Good luck.

            Robin

          • Daniel Says:

            Thanks Robin, yes you are right. Deploying from a web page is much better practice than supplying a link to a setup.exe file. I followed your steps in the sample code and all is working now.

            By the way, is the method GetPublicKeyToken “foolproof” in finding the correct application? i.e. Is this token something that is set uniquely and automatically, or is this something that should be set by the developer in Visual Studio?

            For example, on my first attempt at this, the uninstaller mistakenly removed a QA version of the app rather than the production version (I have a setup like this for QA versions – https://robindotnet.wordpress.com/2009/04/22/clickonce-installing-multiple-versions-concurrently/)

          • robindotnet Says:

            >Deploying from a web page: Exactly. (Been there, done that.)

            >GetPublicKeyToken: Good question. IIRC, every application signed with the same certificate will have the same result from GetPublicKeyToken. The final article published on MSDN uses the public key token and the display name from the registry to make sure it has the right application. You shouldn’t have two apps with the same product name AND signed with the same certificate.

      • Daniel Says:

        Thanks again Robin. I hope you don’t mind this long thread.

        I think the code posted might not check a difference in product name as carefully as it could. If I have an app with the product name “MyApp” and another called “MyApp (QA Version)”, both signed with the same cert, then I think the uninstaller can get confused. It might be using a String.Contains() method or something like that to tell the difference. Anyway just mentioning. The concept still remains. (This is such a helpful post, btw).

        • robindotnet Says:

          In the registry, DisplayName is the value you fill in as Product Name in the Options dialog. So if you compare this value programmatically to the Product Name in the app you are running, it will work. It must be an exact match. If you are running multiple versions of the app on the same machine, you should set the Product Names to be different, so the program names on the start menu will be different too.

          Glad the post was helpful!

Leave a comment