Renewing Self-Signed “Temporary” Certificates

If you’re like me—writing internal apps for a small company—you create your own self-signed certificates when publishing a ClickOnce application. Visual Studio will create these certificates for you automatically on the first publish, but they’ll only be good for a couple of years. That’s when you find yourself under pressure, updating a malfunctioning application, and suddenly you’re getting an error when trying to push out your fixes.

A great tool to renew your certificates is RenewCert, written by Cliff Stanford. Using this tool, you can quickly extend the expiration date of your signing certificate to 5 years from the current date. Much thanks are due to Cliff for figuring this all out! This stuff isn’t really documented anywhere.

But I found a few limitations:

  1. I couldn’t run it in a setting without VC++ installed. The author acknowledges this issue, but with walking the dependencies I couldn’t manage to find them all. I had to run this on a remote computer so it was a deal-breaker.
  2. I couldn’t get the thing to compile on my system. I’ve rooted around C++ apps before and I could probably figure it out, but I just didn’t want to.
  3. Because I couldn’t compile I couldn’t extend the expiration date beyond 5 years, like 25 years for example.
  4. I wanted to use it as part of a tool to move a ClickOnce app from one location to another. I needed a way to determine if a certificate was out of date by running a command line tool and checking the [generic]errorlevel[/generic] output.

So I set about rewriting the app in C#. The hardest part was tracking down all the P/Invoke declarations. The [generic]CertCreateSelfSignCertificate[/generic] API was particularly finicky. The rewrite works the same way as the original with a hard-coded year, but you can always hard-code your own or add support for an additional argument.

New features include:

  • If you don’t supply a CN, it will look up the original and use it rather than a placeholder.
  • You can use a “/e” argument to see if the given certificate has expired.
  • All cleanups are wrapped in a [csharp]try..finally[/csharp] so you’re less likely to destabilize the system (which I did a couple of times and had to restart).

Here’s a complete example:

[shell linenumbers=”false”]
set certFile=C:\My Project\MyCert.pfx
renewcert “%certFile%” /e
if %errorlevel% equ 0 echo Certificate is not expired. & goto SkipCertRenew
echo Certificate is out of date and must be renewed!
renewcert “%certFile%” “%certFile%”
if %errorlevel% neq 0 echo Error %errorlevel% renewing certificate.
:SkipCertRenew
pause
[/shell]

Like the original author, I got it working and stopped, so it’s not the prettiest thing in the world—mea culpa. But this should be easier to extend to suit your needs.

Download: RenewCert.zip (for VS 2010)

5 thoughts on “Renewing Self-Signed “Temporary” Certificates

  1. Thank for this it has got me out of trouble a couple of times, when Microsoft took project deployment out of VS I thought the replacement ClickOnce was a great idea and it revolutionised the way I gave updates to my clients until applications were more than a year old

  2. Well, I tried this by compiling (also in debug) in VS 2015 and on Windows 7 machine and it threw AccessViolation exception always at the CertNameToStr call (line 261 in main). I guess I’ll just

  3. I gave it a try in VS2017 on Win10 64bit and also got an AccessViolation at the CertNameToStr call. Looks like a bug but the code is certainly “finickity” !

  4. The fix is to change the signature of CertNameToStr in Crypt.cs from

    [DllImport(“crypt32.dll”, CharSet = CharSet.Auto)]
    public static extern int CertNameToStr(X509Encoding dwCertEncodingType, ref CRYPT_DATA_BLOB pName, CertNameType dwStrType, ref string psz, int csz);

    to

    [DllImport("crypt32.dll", CharSet = CharSet.Auto)]
    public static extern int CertNameToStr(X509Encoding dwCertEncodingType, ref CRYPT_DATA_BLOB pName, CertNameType dwStrType, string psz, int csz);

    then just change the method call line in Program.cs from

    if ((d = Crypt.CertNameToStr(Crypt.X509Encoding.ASN_Encodings, ref certNameBlob, Crypt.CertNameType.CERT_X500_NAME_STR, ref buffer, 1024 * sizeof(char))) != 0)

    To

    if ((d = Crypt.CertNameToStr(Crypt.X509Encoding.ASN_Encodings, ref certNameBlob, Crypt.CertNameType.CERT_X500_NAME_STR, buffer, 1024 * sizeof(char))) != 0)

    ie the output buffer string is passed by value not reference.

    After that it worked fine for me.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.