Hello again, PowerShell CryptoGuy is back!

I was impressed how many feedback I received on my proof of concept version of Enterprise PKI health verifier: Enterprise PKI (pkiview.msc) PowerShell Edition (PoC). I carefully reviewed each feedback and implemented almost all requests.

What’s new?

New version become more unified with more input and configuration options:

  • The code is moved to a function called Get-EnterprisePKIHealthStatus;
  • The code internally do not call Get-CertificationAuthority command. It is moved to parameters. Now you can pipe CA objects to Get-EnterprisePKIHealthStatus function;
  • Added an ability to pass certificates to the function. This allows to verify the health of Standalone and 3rd party non-Microsoft CAs;
  • Additional CRL validation tests: according to RFC 5280 §5.2.4
  • Expiration threshold switched from absolute values (days/hours) to relative – percentage;
  • Separate expiration thresholds are configurable for: CA certificates, Base CRLs, Delta CRLs and OCSP signing certificates. Default value for all thresholds is set to 80%. With parameters you can override default expiration threshold;
  • Added debug view.

What is not implemented?

I got a request to validate previous non-expired CA keys and certificates. Unfortunately, it is not possible to retrieve CA Exchange certificate for previous CA keys and it is too hard to implement, while workarounds are easier. It is not even scheduled to implement. With new parameter (that accepts end-entity certificate as input) you can manually check previous CA keys/certs.

Parameters

Since the code is more unified, new parameters were added (instead of hardcoded values):

  • CertificateAuthority – specifies the CertificateAuthority object or objects to validate. This object can be retrieved by running a Get-CertificationAuthority command (from PSPKI module);
  • Certificate – Specifies one or more certificates to process. Use this parameter to validate Standalone and/or 3rd party (non-Microsoft) Certification Authorities. This parameter should use end-entity certificates issued by a CA;
  • DownloadTimeout – specifies the URL retrieval timeout in seconds. Default value is 15 seconds;
  • CaCertExpirationThreshold – specifies the CA certificate expiration threshold in percent. This value indicates a point at which CA certificate is about to expire. Default value is 80%.
  • BaseCrlExpirationThreshold – specifies the Base CRL expiration threshold in percent. This value indicates a point at which Base CRL is about to expire. Default value is 80%.
  • DeltaCrlExpirationThreshold – specifies the Delta CRL expiration threshold in percent. This value indicates a point at which Delta CRL is about to expire. Default value is 80%.
  • OcspCertExpirationThreshold – specifies the OCSP signing certificate expiration threshold in percent. This value indicates a point at which OCSP signing certificate is about to expire. Default value is 80%.
  • Debug – provides a verbose debugging information about script flow.

Value range for threshold values is between 1 and 99 (in percent). Help copy added to a file. Here is a sample debug output:

PS C:\> get-ca dc2* | Get-EnterprisePKIHealthStatus -Debug
DEBUG: Initializing parameterset: __CA.
==================== contoso-DC2-CA ====================
DEBUG: contoso-DC2-CA: retrieving CA Exchange certificate.
DEBUG: Entering certification path validation routine...
DEBUG: Chain status for certificate 'CN=contoso-DC2-CA-Xchg, DC=contoso, DC=com': True
DEBUG: CA name: contoso-DC2-CA
DEBUG: ========================= CN=contoso-DC2-CA, DC=contoso, DC=com =========================
DEBUG: Getting URLs.
DEBUG: Fetching CRL Distribution Points extension...
DEBUG: Found 1 CDP URLs.
DEBUG: http://www.contoso.com/pki/contoso-DC2-CA(3).crl
DEBUG: Fetching Authority Information Access extension...
DEBUG: Found 1 Certification Authority Issuer URLs.
DEBUG: http://www.contoso.com/pki/dc2ica(3).crt
DEBUG: Found 1 On-line Certificate Status Protocol URLs.
DEBUG: http://dc2.contoso.com/ocsp
DEBUG: Processing Certification Authority Issuer URLs...
DEBUG: Downloading cert URL: http://www.contoso.com/pki/dc2ica(3).crt.
DEBUG: URL error: -2145844844
DEBUG: Failed to download certificate.
DEBUG: Downloading CRL URL: http://www.contoso.com/pki/contoso-DC2-CA(3).crl.
DEBUG: CRL: CN=contoso-DC2-CA, DC=contoso, DC=com
DEBUG: Entering CRL validation routine...
DEBUG: Base CRL start validity : 01/12/2015 15:44:01
DEBUG: Base CRL end validity   : 01/19/2015 17:04:01
DEBUG: Crl number: 5240
DEBUG: Base CRL is about to expire. Elapsed: 88%
DEBUG: Getting URLs.
DEBUG: Fetching 'Freshest CRL extension'
DEBUG: 'Freshest CRL' extension is presented.
DEBUG: Found 1 Freshest CRL URLs.
DEBUG: http://www.contoso.com/pki/contoso-DC2-CA(3)+.crl
DEBUG: Downloading CRL URL: http://www.contoso.com/pki/contoso-DC2-CA(3)+.crl.
DEBUG: CRL: CN=contoso-DC2-CA, DC=contoso, DC=com
DEBUG: Entering CRL validation routine...
DEBUG: Delta CRL start validity : 01/18/2015 15:44:30
DEBUG: Delta CRL end validity   : 01/19/2015 17:04:30
DEBUG: Crl number: 5246
DEBUG: Referenced Base CRL number: 5240
DEBUG: Required minimum Base CRL number: 5240
DEBUG: Processing On-line Certificate Status Protocol URLs...
DEBUG: Entering OCSP validation routine...
DEBUG: URL: http://dc2.contoso.com/ocsp
DEBUG: OCSP server failed: Unauthorized
DEBUG: ========================= CN=Contoso CA, DC=contoso, DC=com =========================
DEBUG: Getting URLs.
DEBUG: Fetching CRL Distribution Points extension...
DEBUG: Found 1 CDP URLs.
DEBUG: http://www.contoso.com/pki/contoso_RCA.crl
DEBUG: Fetching Authority Information Access extension...
DEBUG: Found 1 Certification Authority Issuer URLs.
DEBUG: http://www.contoso.com/pki/Contoso_RCA(1).crt
DEBUG: Found 0 On-line Certificate Status Protocol URLs.
DEBUG: Processing Certification Authority Issuer URLs...
DEBUG: Downloading cert URL: http://www.contoso.com/pki/Contoso_RCA(1).crt.
DEBUG: Certificate: CN=Contoso CA, DC=contoso, DC=com
DEBUG: Entering certificate validation routine.
DEBUG: Leaf certificate: CN=Contoso CA, DC=contoso, DC=com.
DEBUG: Self-signed certificate, issuer is itself.
DEBUG: Certificate start validity : 02/15/2009 16:31:15
DEBUG: Certificate end validity   : 05/19/2029 10:11:24
DEBUG: Subject name binary comparison         : passed
DEBUG: Public key binary comparison           : passed
DEBUG: Public key parameters binary comparison: passed
DEBUG: Certificate passed all validity checks.
DEBUG: Downloading CRL URL: http://www.contoso.com/pki/contoso_RCA.crl.
DEBUG: CRL: CN=Contoso CA, DC=contoso, DC=com
DEBUG: Entering CRL validation routine...
DEBUG: Base CRL start validity : 10/27/2014 01:18:30
DEBUG: Base CRL end validity   : 01/28/2015 01:38:30
DEBUG: Crl number: 390
DEBUG: Base CRL is about to expire. Elapsed: 90%
DEBUG: Getting URLs.
DEBUG: Fetching 'Freshest CRL extension'
DEBUG: No FreshestCRL Urls
DEBUG: Processing On-line Certificate Status Protocol URLs...
DEBUG: ========================= CN=Contoso CA, DC=contoso, DC=com =========================
DEBUG: Leaf certificate is self-signed, skip validation.

Name                                                                     Status Childs
----                                                                     ------ ------
contoso-DC2-CA                                                            Error {contoso-DC2-CA, Contoso CA}


PS C:\> (get-ca dc2* | Get-EnterprisePKIHealthStatus).childs


Name              : contoso-DC2-CA
Status            : Error
ChainStatus       : NoError
ExtendedErrorInfo :
URLs              : {AIA Location #1: http://www.contoso.com/pki/dc2ica(3).crt, expire: , Status: FailedToDownload, CDP
                     Location #1: http://www.contoso.com/pki/contoso-DC2-CA(3).crl, expire: 2015.01.19. 17:04:01, Statu
                    s: Expiring, DeltaCRL Location #1: http://www.contoso.com/pki/contoso-DC2-CA(3)+.crl, expire: 2015.
                    01.19. 17:04:30, Status: Ok, OCSP Location #1: http://dc2.contoso.com/ocsp, expire: , Status: Unaut
                    horized}

Name              : Contoso CA
Status            : Warning
ChainStatus       : NoError
ExtendedErrorInfo :
URLs              : {AIA Location #1: http://www.contoso.com/pki/Contoso_RCA(1).crt, expire: 2029.05.19. 10:11:24, Stat
                    us: Ok, CDP Location #1: http://www.contoso.com/pki/contoso_RCA.crl, expire: 2015.01.28. 1:38:30, S
                    tatus: Expiring}



PS C:\>

CA objects are the same. In order to make things more consistent, I decided to wrap CA object collection to a single chain object. For each chain a separate object is used. This object has “Status” property which is automatically calculated based on CA object (in Childs property) status. CA object status is now automatically calculated based on URL object status (in URLs property). Therefore, when automating CA health validation, you need only to check Status property on chain object.

Feedback

As always, feedback is accepted and appreciated. How good the tool will be – it depends on you :)

Download

 

this script requires installed PowerShell PKI module!


Share this article:

Comments:

Jordan ALLIOT
Jordan ALLIOT 29.01.2015 19:10 (GMT+2) Enterprise PKI health verifier, PowerShell Edition v1.5

Hello, Great new version! And indeed, passing a certificate directly is enough for checking previous CA keys/certificates. I have found some bugs though when passing some certificates directly. First, try to pass the "VeriSign Class 3 Secure Server CA - G3" CA certificate (installed by default in the CA store on Windows), and you'll have the following error: Exception calling "DecodeInteger" with "1" argument(s): "Value cannot be null." At C:\Users\ng177f6\Documents\WindowsPowerShell\Get-EnterprisePKIHealthStatus.ps1:490 char:4 + [Int64]$dcrlNumber = [PKI.ASN.ASN1]::DecodeInteger(($crl.Extensions | ? {$_.O ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : ArgumentNullException StackTrace: at System.Management.Automation.ExceptionHandlingOps.ConvertToMethodInvocationException(Exception exception, Type ty peToThrow, String methodName, Int32 numArgs, MemberInfo memberInfo) at CallSite.Target(Closure , CallSite , RuntimeType , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1) at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) I also have a "Proxy Authentication Required" when sending the OCSP request. It might be good to allow passing a proxy object to the command since many functions will need to set it (I don't know if it is not automatically set for cert/CRL download but it's needed at least for OCSP requests which don't use the CryptoAPI functions). Finally, there seems to be a bug in the constructor of the X509CRLDistributionPointExtension (maybe linked with the PSPKI module itself then...). I can't reproduce it with the default installed CAs so you'll have to test it on this certificate in order to reproduce: http://publication.certificateservices.eads.com/CAcerts/EADSecoSHA2CA1.cer (this is not a Microsoft CA). Here is the error: New-Object : Exception calling ".ctor" with "2" argument(s): "The data is invalid" At C:\Users\ng177f6\Documents\WindowsPowerShell\Get-EnterprisePKIHealthStatus.ps1:363 char:13 + $cdp = New-Object Security.Cryptography.X509Certificates.X509CRLDistributio ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand StackTrace: at System.Management.Automation.DotNetAdapter.AuxiliaryConstructorInvoke(MethodInformation methodInformation, Object [] arguments, Object[] originalArguments) at System.Management.Automation.DotNetAdapter.ConstructorInvokeDotNet(Type type, ConstructorInfo[] constructors, Obj ect[] arguments) at Microsoft.PowerShell.Commands.NewObjectCommand.CallConstructor(Type type, ConstructorInfo[] constructors, Object[ ] args) If you need any more details, don't hesitate to ask. Jordan

Vadims Podans
Vadims Podans 29.01.2015 21:39 (GMT+2) Enterprise PKI health verifier, PowerShell Edition v1.5

Thanks for report. First issue is caused due to a missing extension existence check (CRL is extensionless v1 CRL). I will add this check. Second issue in the X509CRLDistributionPointExtension -- it is a non-implemented code branch. I did not support partitioned CRLs, because never had them to verify. It is strange that you use non-partitioned CRL in a partitioned manner (internal encoding). I'll write a missing code branch to fully support CDP extension. BTW, it would be easier if you contact me via email and I will send you fixed files.

teja
teja 08.02.2016 15:10 (GMT+2) Enterprise PKI health verifier, PowerShell Edition v1.5

HI vadmins,

I've tried to use the get-enterprisepkihealthstatus with a certificate, created a new-object of type x509certificate2 and assigned a certificate to it. ran the cmdlet only to get an error saying this.

PS C:\Users\administrator.JLR> Get-EnterprisePKIHealthStatus -Certificate $cer -debug
DEBUG: Initializing parameterset: __EndCerts.
DEBUG: Entering certification path validation routine...
DEBUG: Chain status for certificate 'CN=sam.jlr.ktsecureqa.co.uk, DC=jlr, DC=ktsecureqa, DC=co, DC=uk': True
DEBUG: CA name: JLR eToken Issuing CA
DEBUG: ========================= CN=JLR eToken Issuing CA, O=JaguarLandRover, C=GB =========================
DEBUG: Getting URLs.
DEBUG: Fetching CRL Distribution Points extension...
DEBUG: Found 1 CDP URLs.
DEBUG: string[] GetURLs()
DEBUG: Fetching Authority Information Access extension...
DEBUG: Found 1 Certification Authority Issuer URLs.
DEBUG: http://pki.jlr.ktsecureqa.co.uk/JLR%20eToken%20Issuing%20CA.crt
DEBUG: Found 0 On-line Certificate Status Protocol URLs.
DEBUG: Processing Certification Authority Issuer URLs...
DEBUG: Downloading cert URL: http://pki.jlr.ktsecureqa.co.uk/JLR eToken Issuing CA.crt.
DEBUG: Certificate: CN=JLR eToken Issuing CA, O=JaguarLandRover, C=GB
DEBUG: Entering certificate validation routine.
DEBUG: Leaf certificate: CN=JLR eToken Issuing CA, O=JaguarLandRover, C=GB.
DEBUG: Issuer candidate: CN=JLR eToken Issuing CA, O=JaguarLandRover, C=GB.
DEBUG: Certificate start validity : 07/30/2015 13:57:29
DEBUG: Certificate end validity   : 07/30/2016 14:07:29
DEBUG: Subject name binary comparison         : passed
DEBUG: Public key binary comparison           : passed
DEBUG: Public key parameters binary comparison: passed
DEBUG: Certificate passed all validity checks.
New-Object : The value supplied is not valid, or the property is read-only. Change the value, and then try again.
At C:\Program Files\Sysadmins LV\PowerShell PKI Module\pspki\Server\Get-EnterprisePKIHealthStatus.ps1:605 char:20
+                     $urlElement = New-Object PKI.EnterprisePKI.UrlElement -Property @{
+                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [New-Object], Exception
    + FullyQualifiedErrorId : SetValueException,Microsoft.PowerShell.Commands.NewObjectCommand

DEBUG: Downloading CRL URL: .
DEBUG: URL error: 50
DEBUG: Failed to download CRL.
You cannot call a method on a null-valued expression.
At C:\Program Files\Sysadmins LV\PowerShell PKI Module\pspki\Server\Get-EnterprisePKIHealthStatus.ps1:638 char:7
+                         $urlElement.SetError($s_error -bor [PKI.EnterprisePKI.UrlStatus]::FailedTo ...
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

The property 'ExtendedErrorInfo' cannot be found on this object. Verify that the property exists and can be set.
At C:\Program Files\Sysadmins LV\PowerShell PKI Module\pspki\Server\Get-EnterprisePKIHealthStatus.ps1:639 char:7
+                         $urlElement.ExtendedErrorInfo = $obj
+                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

DEBUG: Processing On-line Certificate Status Protocol URLs...
DEBUG: ========================= CN=JaguarLandRover Root CA =========================
DEBUG: Getting URLs.
DEBUG: Fetching CRL Distribution Points extension...
DEBUG: Found 1 CDP URLs.
DEBUG: string[] GetURLs()
DEBUG: Fetching Authority Information Access extension...
DEBUG: Found 1 Certification Authority Issuer URLs.
DEBUG: http://pki.jaguarlandrover.com/JaguarLandRoverRootCertificate.crt
DEBUG: Found 0 On-line Certificate Status Protocol URLs.
DEBUG: Processing Certification Authority Issuer URLs...
DEBUG: Downloading cert URL: http://pki.jaguarlandrover.com/JaguarLandRoverRootCertificate.crt.
DEBUG: Certificate: CN=JaguarLandRover Root CA
DEBUG: Entering certificate validation routine.
DEBUG: Leaf certificate: CN=JaguarLandRover Root CA.
DEBUG: Self-signed certificate, issuer is itself.
DEBUG: Certificate start validity : 07/30/2015 11:27:56
DEBUG: Certificate end validity   : 07/30/2035 11:37:55
DEBUG: Subject name binary comparison         : passed
DEBUG: Public key binary comparison           : passed
DEBUG: Public key parameters binary comparison: passed
DEBUG: Certificate passed all validity checks.
New-Object : The value supplied is not valid, or the property is read-only. Change the value, and then try again.
At C:\Program Files\Sysadmins LV\PowerShell PKI Module\pspki\Server\Get-EnterprisePKIHealthStatus.ps1:605 char:20
+                     $urlElement = New-Object PKI.EnterprisePKI.UrlElement -Property @{
+                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [New-Object], Exception
    + FullyQualifiedErrorId : SetValueException,Microsoft.PowerShell.Commands.NewObjectCommand

 

the above is debug for the cmdlet, looks like its failing to fetch the cdp url and hence creating an empty object, thus giving out error.

 

Vadims Podāns
Vadims Podāns 08.02.2016 15:26 (GMT+2) Enterprise PKI health verifier, PowerShell Edition v1.5

It is a known issue: https://pspki.codeplex.com/workitem/86. You can redownload PSPKI.zip and updated PKI.Core.dll. This should your issue.

teja
teja 08.02.2016 18:16 (GMT+2) Enterprise PKI health verifier, PowerShell Edition v1.5

thanks a ton mate, worked like a charm. Now i want to insert, ouputs and exit codes within the script and pipe the status messages to nagios. 

Guess i need to resign the script with a certificate or change my execution policy for the time being. thank you vadmis.


Post your comment:

Please, solve this little equation and enter result below. Captcha