Forum Discussion
The PowerShell script that's worked for 2 years to find a signing certificate, stopped working
- Jul 08, 2022
I also meant to add: is there some reason Set-AuthenticodeSignature won't work for you?
The only gap I see between it and signtool.exe is the ability to control the timestamping algorithm, but I wouldn't have thought this would have mattered.
It would be a little easier/more readable to use Set-AuthenticodeSignature but if you need that finer-grain control from signtool.exe then that's fair enough.
Cheers,
Lain
Hey, Rod.
Good diagnostic thinking - that's a great skill to have whether it's PowerShell or something else you're nutting through.
Having four certificates is bothersome since they can be returned in any order. I'd probably suggest tossing a "Sort-Object -Property NotAfter -Desc" in there before the "Select -First 1" to ensure you're at least always grabbing the most recently-obtained certificate.
But setting that aside, the things I'd suggest checking the returned certificate that:
- It hasn't expired (NotAfter date);
- The CRL is reachable;
- The Active Directory policy endpoint is reachable (if relevant).
Cheers,
Lain
- Rod FalangaJul 01, 2022Brass ContributorThank you, Lain, for the kind complements.
I'm just a better than average beginner PowerShell user, so I don't know how to use NotAfter. For example, do I do something like NotAfter 7/1/2022? Or does NotAfter -Desc sort a description field in descending order? And if so, is that description field always the expiration date?
And what does CRL mean?
I've no idea at all if Active Directory policy endpoint is involved, and certainly no idea if it is reachable, even if it is involved. But since it worked as recently as last week, I presume it either isn't involved or if involved it was reachable.- LainRobertsonJul 02, 2022Silver Contributor
Hey, Rod. Here's some short-form responses.
Sort-Object
Expanding on my "Sort-Object" comment, this is what I meant:
Going from this original line:
$cert = ls cert:\ -Recurse -CodeSigningCert | ? {$_.Verify()} | Select -First 1
To this:
$cert = ls cert:\ -Recurse -CodeSigningCert | Sort-Object -Property NotAfter -Desc | ? {$_.Verify()} | Select -First 1
This ensures that the command returns the certificate with the furtherest-away expiration date, and not just any randomly-ordered matching certificate.
CRL distribution point
An example of a CRL distribution point is shown below. If your TFS host can't reach the CRL location (multiple CRL locations can be listed, meaning you'd want to check them all) then the call within your command line to the .Verify() method will fail, which is why I listed this as an option.
If the CRL begins with "http" then you can simply try accessing that location (including the file name) from a browser. If it begins with ldap: then perhaps for now, just skip this test (you'd need to leverage something like certutil.exe to run this test) since there's a good chance this won't be the issue anyway.
Example of plugging a http-based CRL into the browser bar to test existence:
Active Directory policy endpoint
If you don't see a "Certificate Template Information" line as shown below in the Details tab, then just ignore this part as it's most likely you're not using an Active Directory policy anyway.
If you do see such a line, then you will have a policy and therefore an endpoint but as with the "ldap" statement above, I'd just assume this is working for now as it's not as easy to test as a "http"-based CRL.
Summary of my initial thoughts
My thoughts at this early stage are:
- I doubt the script is actually the issue;
- I feel like it's either selecting an expired certificate (i.e. current date > NotAfter value on the selected certificate); or
- All of the CRLs from the chosen certificate are unreachable.
While I don't think the script will be at fault, it does bother me that it's scoped to search both the local machine store (which is what I'd expect to see) but also the current user store (which I didn't expect to see.) That doesn't sit well with me since if the script selected a certificate from within the user store, I'd expect that TFS could not read it - at least not from where it sits in the user store.
While that bothers me, it's also not a reason for the call to .Verify() to fail, which your testing shows is happening (i.e. ".Verify()" will be returning $false.)
Purely as a testing exercise, you can run the following. If all the results back as "False" then that serves as confirmation about what you've hypothesized regarding the "null" return value:
cert:\ -Recurse -CodeSigningCert | ForEach-Object {$_.Verify()}
Naturally, there are other options but these are my initial guesses, as per my first response.
Make sure you take a peek at Event Viewer for clues, too.
Cheers,
Lain
Edited: Corrected multiple typos.
- Rod FalangaJul 05, 2022Brass Contributor
LainRobertson I'm at my work laptop so I can try what you suggested. I took this line:
ls cert:\ -Recurse -CodeSigningCert | Sort-Object -Property NotAfter -Descending | Select -First 1
Then changed it, per your suggestion, to this:
ls cert:\ -Recurse -CodeSigningCert | Sort-Object -Property NotAfter -Descending | ? {$_.Verify()} | Select -First 1
The first line does return 1 of the records from the Certificate store. The second line doesn't return anything from the Certificate store. And I know there's a valid certificate in the certificate store that won't expire until June of 2023.