Server/Get-EnterprisePKIHealthStatus.ps1
function Get-EnterprisePKIHealthStatus { <# .ExternalHelp PSPKI.Help.xml #> [OutputType('PKI.EnterprisePKI.X509HealthPath')] [CmdletBinding(DefaultParameterSetName = '__CA')] param( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = '__CA' )] [Alias('CA')] [PKI.CertificateServices.CertificateAuthority[]]$CertificateAuthority, [Parameter(Mandatory = $true, ParameterSetName = '__EndCerts')] [Security.Cryptography.X509Certificates.X509Certificate2[]]$Certificate, # configuration [int]$DownloadTimeout = 15, [ValidateRange(1,99)] [int]$CaCertExpirationThreshold = 80, [ValidateRange(1,99)] [int]$BaseCrlExpirationThreshold = 80, [ValidateRange(1,99)] [int]$DeltaCrlExpirationThreshold = 80, [ValidateRange(1,99)] [int]$OcspCertExpirationThreshold = 80 ) begin { #region native function declarations $cryptnetsignature = @" [DllImport("cryptnet.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool CryptRetrieveObjectByUrl( //[MarshalAs(UnmanagedType.LPStr)] string pszUrl, //[MarshalAs(UnmanagedType.LPStr)] int pszObjectOid, int dwRetrievalFlags, int dwTimeout, ref IntPtr ppvObject, IntPtr hAsyncRetrieve, IntPtr pCredentials, IntPtr pvVerify, IntPtr pAuxInfo ); "@ Add-Type -MemberDefinition $cryptnetsignature -Namespace "PKI.EnterprisePKI" -Name Cryptnet $crypt32signature = @" [DllImport("Crypt32.dll", SetLastError = true)] public static extern Boolean CertFreeCertificateContext( [In] IntPtr pCertContext ); [DllImport("Crypt32.dll", SetLastError = true)] public static extern Boolean CertFreeCRLContext( [In] IntPtr pCrlContext ); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CRL_CONTEXT { public int dwCertEncodingType; public IntPtr pbCrlEncoded; public int cbCrlEncoded; public IntPtr pCrlInfo; public IntPtr hCertStore; } "@ Add-Type -MemberDefinition $crypt32signature -Namespace "PKI.EnterprisePKI" -Name Crypt32 Add-Type @" using System; using System.Linq; using System.Security.Cryptography.X509Certificates; namespace PKI.EnterprisePKI { public enum ChildStatus { Ok = 0x0, Warning = 0x100, Error = 0x8000, } // 0-49 -- common // 50-99 -- certs // 100-149 -- crls // 150-199 -- ocsp public enum UrlStatus { // common Ok = 0, // CRT/CRL/OCSP FailedToDownload = 10, NotYetValid = 11, Expired = 12, Expiring = 13, InvalidSignature = 14, NetworkRetrievalError = 15, // certs only Revoked = 50, InvalidCert = 51, // CRLs only. ScheduleExpired means that there is a "Next CRL Publish" // extension and current time is ahead of "Next CRL Publish value" InvalidIssuer = 100, ScheduleExpired = 101, InvalidBase = 102, InvalidCrlType = 103, NonCriticalDeltaIndicator = 104, StaleDelta = 105, // ocsp only MalformedRequest = 151, InternalError = 152, TryLater = 153, SignatureRequired = 155, Unauthorized = 156, ResponseInvalidData = 160, InvalidSignerCert = 161, // CAs only Offline, } public enum UrlType { Certificate, Crl, Ocsp } public class UrlElement { ushort error; Object hiddenObject; public String Name { get; set; } public UrlStatus Status { get { return (UrlStatus)(error & 0xff); } } public String ExtendedErrorInfo { get; set; } public Uri Url { get; set; } public DateTime? ExpirationDate { get; set; } public UrlType UrlType { get; set; } public Object GetObject() { return hiddenObject; } public void SetObject(Object obj) { hiddenObject = obj; } public void SetError(ushort statusCode) { error = statusCode; } public ushort GetError() { return error; } public override String ToString() { return Name + ": " + Url + ", expire: " + ExpirationDate + ", Status: " + Status; } } public class CAObject { bool isOffline; public String Name { get; set; } // can be 'Ok', 'Warning', or 'Error' public ChildStatus Status { get { if (isOffline) { return ChildStatus.Error; } if (URLs == null) { return ChainStatus == X509ChainStatusFlags.NoError ? ChildStatus.Ok : ChildStatus.Error; } ChildStatus retValue = ChildStatus.Ok; foreach (var url in URLs) { if ((url.GetError() & 0xFF00) > (int)retValue) { retValue = (ChildStatus)(url.GetError() & 0xFF00); } } return retValue; } } public X509ChainStatusFlags ChainStatus { get; set; } public String ExtendedErrorInfo { get; set; } public UrlElement[] URLs { get; set; } public void Offline() { isOffline = true; } } public class X509HealthPath { public String Name { get; set; } public ChildStatus Status { get { if (Childs == null || Childs.Length == 0) { return ChildStatus.Ok; } return Childs.Any(child => child.Status == ChildStatus.Error) ? ChildStatus.Error : (Childs.Any(child => child.Status == ChildStatus.Warning) ? ChildStatus.Warning : ChildStatus.Ok); } } public CAObject[] Childs { get; set; } } } "@ #endregion #region Error severity $s_ok = 0x0 $s_warning = 0x100 $s_error = 0x8000 #endregion #region script internal config if ($PSBoundParameters.Verbose) {$VerbosePreference = "continue"} if ($PSBoundParameters.Debug) {$DebugPreference = "continue"} $timeout = $DownloadTimeout * 1000 #endregion #region helper functions # returns [X509ChainElement[]] $chainRoots = @() function __getChain([Security.Cryptography.X509Certificates.X509Certificate2]$cert) { Write-Verbose "Entering certificate chaining engine." $chain = New-Object Security.Cryptography.X509Certificates.X509Chain $chain.ChainPolicy.RevocationMode = [Security.Cryptography.X509Certificates.X509RevocationMode]::NoCheck $status = $chain.Build($cert) Write-Debug "Chain status for certificate '$($cert.Subject)': $status" if ($chainRoots -notcontains $chain.ChainElements[0].Certificate.Thumbprint) { $chainRoots += $chain.ChainElements[0].Certificate.Thumbprint } $retValue = New-Object Security.Cryptography.X509Certificates.X509ChainElement[] -ArgumentList $chain.ChainElements.Count $chain.ChainElements.CopyTo($retValue,0) $chain.Reset() $retValue } # returns [X509Certificate2] or [String] that contains error message function __downloadCert($url) { Write-Debug "Downloading cert URL: $url." $ppvObject = [IntPtr]::Zero if ([PKI.EnterprisePKI.Cryptnet]::CryptRetrieveObjectByUrl($url,1,4,$timeout,[ref]$ppvObject, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero) ) { $cert = New-Object Security.Cryptography.X509Certificates.X509Certificate2 $ppvObject Write-Debug "Certificate: $($cert.Subject)" $cert [void][PKI.EnterprisePKI.Crypt32]::CertFreeCertificateContext($ppvObject) } else { $hresult = [Runtime.InteropServices.Marshal]::GetLastWin32Error() Write-Debug "URL error: $hresult" $CertRequest = New-Object -ComObject CertificateAuthority.Request $CertRequest.GetErrorMessageText($hresult,0) [SysadminsLV.PKI.Utils.CryptographyUtils]::ReleaseCom($CertRequest) } } # returns [X509CRL2] or [String] that contains error message function __downloadCrl($url) { Write-Debug "Downloading CRL URL: $url." $ppvObject = [IntPtr]::Zero if ([PKI.EnterprisePKI.Cryptnet]::CryptRetrieveObjectByUrl($url,2,4,$timeout,[ref]$ppvObject, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero) ) { $crlContext = [Runtime.InteropServices.Marshal]::PtrToStructure($ppvObject,[Type][PKI.EnterprisePKI.Crypt32+CRL_CONTEXT]) $rawData = New-Object byte[] -ArgumentList $crlContext.cbCrlEncoded [Runtime.InteropServices.Marshal]::Copy($crlContext.pbCrlEncoded,$rawData,0,$rawData.Length) $crl = New-Object SysadminsLV.PKI.Cryptography.X509Certificates.X509CRL2 (,$rawData) Write-Debug "CRL: $($crl.Issuer)" $crl [void][PKI.EnterprisePKI.Crypt32]::CertFreeCRLContext($ppvObject) } else { $hresult = [Runtime.InteropServices.Marshal]::GetLastWin32Error() Write-Debug "URL error: $hresult" $CertRequest = New-Object -ComObject CertificateAuthority.Request $CertRequest.GetErrorMessageText($hresult,0) [SysadminsLV.PKI.Utils.CryptographyUtils]::ReleaseCom($CertRequest) } } # returns PSObject -- UrlPack function __getUrl ([Byte[]]$rawData, [bool]$isCert) { Write-Verbose "Getting URLs." Write-Debug "Getting URLs." $URLs = New-Object psobject -Property @{ CDP = $null; AIA = $null; OCSP = $null; FreshestCRL = $null; } $ofs = "`n" if ($isCert) { $cert = New-Object Security.Cryptography.X509Certificates.X509Certificate2 @(,$rawData) # CRL Distribution Points Write-Debug "Fetching 'CRL Distribution Points' extension..." $e = $cert.Extensions["2.5.29.31"] if ($e) { $asn = New-Object Security.Cryptography.AsnEncodedData (,$e.RawData) $cdp = New-Object SysadminsLV.PKI.Cryptography.X509Certificates.X509CRLDistributionPointsExtension $asn, $false $URLs.CDP = $cdp.GetURLs() Write-Debug "Found $(($URLs.CDP).Length) CDP URLs." if ($URLs.CDP) {$URLs.CDP | ForEach-Object {Write-Debug "$_"}} } else { Write-Debug "Missing 'CRL Distribution Points' extension." } # Authority Information Access Write-Debug "Fetching 'Authority Information Access' extension..." $e = $cert.Extensions["1.3.6.1.5.5.7.1.1"] if ($e) { $asn = New-Object Security.Cryptography.AsnEncodedData (,$e.RawData) $aia = New-Object SysadminsLV.PKI.Cryptography.X509Certificates.X509AuthorityInformationAccessExtension $asn, $false $URLs.AIA = $aia.CertificationAuthorityIssuer Write-Debug "Found $(($URLs.AIA).Length) Certification Authority Issuer URLs." if ($URLs.AIA) {$URLs.AIA | ForEach-Object {Write-Debug $_}} $URLs.OCSP = $aia.OnlineCertificateStatusProtocol Write-Debug "Found $(($URLs.OCSP).Length) On-line Certificate Status Protocol URLs." if ($URLs.OCSP) {$URLs.OCSP | ForEach-Object {Write-Debug $_}} } else { Write-Debug "Missing 'Authority Information Access' extension." } $URLs } else { Write-Debug "Fetching 'Freshest CRL' extension..." $crl = New-Object SysadminsLV.PKI.Cryptography.X509Certificates.X509CRL2 @(,$rawData) $e = $crl.Extensions["2.5.29.46"] # Freshest CRL if ($e) { $URLs.FreshestCRL = $e.GetURLs() Write-Debug "Found $(($URLs.FreshestCRL).Length) Freshest CRL URLs." if ($URLs.FreshestCRL) {$URLs.FreshestCRL | ForEach-Object {Write-Debug $_}} } else { Write-Debug "Missing 'Freshest CRL' extension." } $URLs } } # returns UrlElement function __verifyAIA { param ( [PKI.EnterprisePKI.UrlElement]$urlElement, [Security.Cryptography.X509Certificates.X509ChainElement]$CAcert ) Write-Verbose "Entering certificate validation routine." Write-Debug "Entering certificate validation routine." $cert = $urlElement.GetObject() Write-Debug "Leaf certificate: $($cert.Subject)." $parent = if ($cert.Subject -eq $cert.Issuer) { Write-Debug "Self-signed certificate, issuer is itself." $cert } else { Write-Debug "Issuer candidate: $($CAcert.Certificate.Subject)." $CAcert.Certificate } Write-Debug "Certificate start validity : $($cert.NotBefore)" Write-Debug "Certificate end validity : $($cert.NotAfter)" $urlElement.ExpirationDate = $cert.NotAfter $subjComp = Compare-Object $cert.SubjectName.RawData $parent.SubjectName.RawData $pubKeyComp = Compare-Object $cert.PublicKey.EncodedKeyValue.RawData $parent.PublicKey.EncodedKeyValue.RawData $pubKeyParamComp = Compare-Object $cert.PublicKey.EncodedParameters.RawData $parent.PublicKey.EncodedParameters.RawData Write-Debug "Subject name binary comparison : $(if ($subjComp) {'failed'} else {'passed'})" Write-Debug "Public key binary comparison : $(if ($pubKeyComp) {'failed'} else {'passed'})" Write-Debug "Public key parameters binary comparison: $(if ($pubKeyParamComp) {'failed'} else {'passed'})" $fullTime = ($cert.NotAfter - $cert.NotBefore).TotalSeconds $elapsed = ((Get-Date) - $cert.NotBefore).TotalSeconds $errorCode = if ($subjComp -or $pubKeyComp -or $pubKeyParamComp) { $s_error -bor [PKI.EnterprisePKI.UrlStatus]::InvalidCert } elseif ($cert.NotBefore -gt (Get-Date)) { Write-Debug "Certificate is not yet valid." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::NotYetValid } elseif ($cert.NotAfter -lt (Get-Date)) { Write-Debug "Certificate is expired." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::Expired } elseif ($CaCertExpirationThreshold -lt $elapsed / $fullTime * 100) { Write-Debug "Certificate is about to expire. Elapsed $([int]($elapsed / $fullTime * 100))%" $s_warning -bor [PKI.EnterprisePKI.UrlStatus]::Expiring } else { Write-Debug "Certificate passed all validity checks." $s_ok -bor [PKI.EnterprisePKI.UrlStatus]::Ok } $urlElement.SetError($errorCode) $urlElement } # returns DateTime or Null (for CRL v1) function __getCrlNextPublish($crl) { $e = $crl.Extensions["1.3.6.1.4.1.311.21.4"] if (!$e) {return} $dt = try { (New-Object SysadminsLV.Asn1Parser.Universal.Asn1UtcTime -ArgumentList @(,($e.RawData))).Value } catch { (New-Object SysadminsLV.Asn1Parser.Universal.Asn1GeneralizedTime -ArgumentList @(,($e.RawData))).Value } } # returns UrlElement. $cert -- issuer candidate/X509ChainElement. function __verifyCDP { param( [PKI.EnterprisePKI.UrlElement]$urlElement, [Security.Cryptography.X509Certificates.X509ChainElement]$cert, [SysadminsLV.PKI.Cryptography.X509Certificates.X509CRL2]$BaseCRL, [switch]$DeltaCRL ) Write-Verbose "Entering CRL validation routine..." Write-Debug "Entering CRL validation routine..." $crl = $urlElement.GetObject() Write-Debug "$($crl.Type) start validity : $($crl.ThisUpdate)" Write-Debug "$($crl.Type) end validity : $($crl.NextUpdate)" $urlElement.ExpirationDate = $crl.NextUpdate [Numerics.BigInteger]$dcrlNumber = $crl.GetCRLNumber() Write-Debug "CRL number: $dcrlNumber" if ($DeltaCRL) { [Numerics.BigInteger]$bcrlNumber = $BaseCRL.GetCRLNumber() Write-Debug "Referenced Base CRL number: $bcrlNumber" $DeltaCrlIndicator = $crl.Extensions["2.5.29.27"] if ($DeltaCrlIndicator -ne $null) { [Numerics.BigInteger]$indicator = (New-Object SysadminsLV.Asn1Parser.Universal.Asn1Integer -ArgumentList @(,($e.RawData))).Value Write-Debug "Required minimum Base CRL number: $indicator" [bool]$indicatorIsCritical = $DeltaCrlIndicator.Critical } else { Write-Debug "Missing 'Delta CRL Indicator' CRL extension." } } $errorCode = if ($DeltaCRL -and ($crl.Type -ne "DeltaCrl")) { Write-Debug "Invalid CRL type. Expected Delta CRL, but received Base CRL." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::InvalidCrlType } elseif (!$DeltaCRL -and ($crl.Type -ne "BaseCrl")) { Write-Debug "Invalid CRL type. Expected Base CRL, but received Delta CRL." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::InvalidCrlType } elseif (!$crl.VerifySignature($cert.Certificate, $true)) { Write-Debug "CRL signature check failed." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::InvalidIssuer } elseif ($crl.ThisUpdate -gt [datetime]::Now) { Write-Debug "CRL is not yet valid." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::NotYetValid } elseif ($crl.NextUpdate -lt [datetime]::Now) { Write-Debug "CRL is expired." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::Expired } elseif ($DeltaCRL -and !$indicatorIsCritical) { Write-Debug "'Delta CRL Indicator' is not critical." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::NonCriticalDeltaIndicator } elseif ($DeltaCRL -and ($bcrlNumber -lt $indicator)) { Write-Debug "Base CRL number has lower version than version required by 'Delta CRL Indicator' extension." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::InvalidBase } elseif ($DeltaCRL -and ($dcrlNumber -lt $bcrlNumber)) { Write-Debug "Delta CRL is outdated. A new version of Base CRL is available that overlaps current Delta CRL." $s_warning -bor [PKI.EnterprisePKI.UrlStatus]::StaleDelta } else { $dt = __getCrlNextPublish $crl if ($dt) { if ((Get-Date) -gt $dt) { Write-Debug "Scheduled CRL publish expired." $urlElement.SetError($s_warning -bor [PKI.EnterprisePKI.UrlStatus]::ScheduleExpired) } $urlElement return } $fullTime = ($crl.NextUpdate - $crl.ThisUpdate).TotalSeconds $elapsed = ((Get-Date) - $crl.ThisUpdate).TotalSeconds if ($DeltaCRL) { if ($DeltaCrlExpirationThreshold -lt $elapsed / $fullTime * 100) { Write-Debug "$($crl.Type) is about to expire. Elapsed: $([int]($elapsed / $fullTime * 100))%" $s_warning -bor [PKI.EnterprisePKI.UrlStatus]::Expiring } else { $s_ok -bor [PKI.EnterprisePKI.UrlStatus]::Ok } } else { if ($BaseCrlExpirationThreshold -lt $elapsed / $fullTime * 100) { Write-Debug "$($crl.Type) is about to expire. Elapsed: $([int]($elapsed / $fullTime * 100))%" $s_warning -bor [PKI.EnterprisePKI.UrlStatus]::Expiring } else { $s_ok -bor [PKI.EnterprisePKI.UrlStatus]::Ok } } } $urlElement.SetError($errorCode) $urlElement } # returns UrlElement function __verifyOCSP { param( [Security.Cryptography.X509Certificates.X509ChainElement]$cert, [PKI.EnterprisePKI.UrlElement]$urlElement ) Write-Verbose "Entering OCSP validation routine..." Write-Debug "Entering OCSP validation routine..." Write-Debug "URL: $($urlElement.Url.AbsoluteUri)" $req = New-Object SysadminsLV.PKI.OcspClient.OCSPRequest $cert.Certificate $req.URL = $urlElement.Url try { $resp = $req.SendRequest() $urlElement.SetObject($resp) $errorCode = if ($resp.ResponseStatus -ne [SysadminsLV.PKI.OcspClient.OCSPResponseStatus]::Successful) { Write-Debug "OCSP server failed: $($resp.ResponseStatus)" $s_error -bor (150 + $resp.ResponseStatus) } elseif (!$resp.SignatureIsValid) { Write-Debug "OCSP response signature validation failed." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::InvalidSignature } elseif ([int]$resp.ResponseErrorInformation) { Write-Debug "Response contains invalid data: $($resp.ResponseErrorInformation)" $s_error -bor [PKI.EnterprisePKI.UrlStatus]::ResponseInvalidData } elseif (!$resp.SignerCertificateIsValid) { Write-Debug "Signer certificate has one or more issues." $s_error -bor [PKI.EnterprisePKI.UrlStatus]::InvalidSignerCert } else { $totalValidity = ($resp.SignerCertificates[0].NotAfter - $resp.SignerCertificates[0].NotBefore).TotalSeconds $elapsed = ((Get-Date) - $resp.SignerCertificates[0].NotBefore).TotalSeconds if ($OcspCertExpirationThreshold -le $elapsed / $totalValidity * 100) { Write-Debug "OCSP signing certificate is about to expire. Elapsed: $($elapsed / $totalValidity * 100)%" $s_warning -bor [PKI.EnterprisePKI.UrlStatus]::Expiring } else { Write-Debug "OCSP response passed all checks." $urlElement.ExpirationDate = $resp.Responses[0].NextUpdate Write-Debug "OCSP response expires: $($urlElement.ExpirationDate)" $s_ok -bor [PKI.EnterprisePKI.UrlStatus]::Ok } } $urlElement.SetError($errorCode) } catch { $urlElement.SetError($s_error -bor [PKI.EnterprisePKI.UrlStatus]::NetworkRetrievalError) $urlElement.ExtendedErrorInfo = $_.Error.Exception.Message } $urlElement } # returns CAObject function __processCerts ($CAObject, $projectedChain) { Write-Verbose "Processing Certification Authority Issuer URLs..." Write-Debug "Processing Certification Authority Issuer URLs..." for ($n = 0; $n -lt $urlPack.AIA.Length; $n++) { $urlElement = New-Object PKI.EnterprisePKI.UrlElement -Property @{ Name = "AIA Location #$($n + 1)"; Url = $urlPack.AIA[$n]; UrlType = [PKI.EnterprisePKI.UrlType]::Certificate; } $obj = __downloadCert $urlElement.Url if ($obj -is [Security.Cryptography.X509Certificates.X509Certificate2]) { $urlElement.SetObject($obj) $urlElement = __verifyAIA $urlElement $projectedChain[$i + 1] } else { Write-Debug "Failed to download certificate." $urlElement.SetError($s_error -bor [PKI.EnterprisePKI.UrlStatus]::FailedToDownload) $urlElement.ExtendedErrorInfo = $obj } $CAObject.URLs += $urlElement } $CAObject } # returns CAObject function __processOcsp ($CAObject, $projectedChain) { Write-Verbose "Processing On-line Certificate Status Protocol URLs..." Write-Debug "Processing On-line Certificate Status Protocol URLs..." for ($n = 0; $n -lt $urlPack.OCSP.Length; $n++) { $urlElement = New-Object PKI.EnterprisePKI.UrlElement -Property @{ Name = "OCSP Location #$($n + 1)"; Url = $urlPack.OCSP[$n]; UrlType = [PKI.EnterprisePKI.UrlType]::Ocsp; } $urlElement = __verifyOCSP $projectedChain[$i] $urlElement $CAObject.URLs += $urlElement } $CAObject } # returns X509HealthPath function __validateSinglePath { param( [Security.Cryptography.X509Certificates.X509Certificate2]$cert, # this parameter is not used [int]$keyIndex = -1 ) Write-Verbose "Entering certification path validation routine..." Write-Debug "Entering certification path validation routine..." if ([IntPtr]::Zero.Equals($cert.Handle)) { throw New-Object SysadminsLV.PKI.Exceptions.UninitializedObjectException "The certificate is not initialized." return } $projectedChain = __getChain $cert [void]($cert.Issuer -match "CN=([^,]+)") Write-Debug "CA name: $($matches[1])" $out = if ($keyIndex -lt 0) { New-Object PKI.EnterprisePKI.X509HealthPath -Property @{Name = $matches[1]} } else { New-Object PKI.EnterprisePKI.X509HealthPath -Property @{Name = "$($matches[1]) ($keyIndex)"} } for ($i = 0; $i -lt $projectedChain.Length; $i++) { Write-Debug "========================= $($projectedChain[$i].Certificate.Issuer) =========================" # skip self-signed certificate from checking if (!( Compare-Object -ReferenceObject $projectedChain[$i].Certificate.SubjectName.RawData ` -DifferenceObject $projectedChain[$i].Certificate.IssuerName.RawData)) { Write-Debug "Leaf certificate is self-signed, skip validation." break } [void]($projectedChain[$i].Certificate.Issuer -match "CN=([^,]+)") $CAObject = if ($keyIndex -lt 0) { New-Object PKI.EnterprisePKI.CAObject -Property @{Name = $matches[1]} } else { New-Object PKI.EnterprisePKI.CAObject -Property @{Name = "$($matches[1]) ($keyIndex)"} } $projectedChain | ForEach-Object {[int]$CAObject.ChainStatus += [int]$_.Status} $urlpack = __getUrl $projectedChain[$i].Certificate.RawData $true # process and validate certificate issuer in the AIA extension $CAObject = __processCerts $CAObject $projectedChain # process and validate CDP extensions for ($n = 0; $n -lt $urlPack.CDP.Length; $n++) { $deltas = @() $urlElement = New-Object PKI.EnterprisePKI.UrlElement -Property @{ Name = "CDP Location #$($n + 1)"; Url = $urlPack.CDP[$n]; UrlType = [PKI.EnterprisePKI.UrlType]::Crl; } $obj = __downloadCrl $urlElement.Url if ($obj -is [SysadminsLV.PKI.Cryptography.X509Certificates.X509CRL2]) { $urlElement.SetObject($obj) $urlElement = __verifyCDP $urlElement $projectedChain[$i + 1] $urlPack2 = __getUrl ($urlElement.GetObject()).RawData $false # process and validate FreshestCRL extension if exist for ($m = 0; $m -lt $urlPack2.FreshestCRL.Length; $m++) { # skip duplicate if ($deltas | Where-Object {$_.Url -eq $urlPack2.FreshestCRL[$m]}) { return } $urlElement2 = New-Object PKI.EnterprisePKI.UrlElement -Property @{ Name = "DeltaCRL Location #$($m + 1)"; Url = $urlPack2.FreshestCRL[$m]; UrlType = [PKI.EnterprisePKI.UrlType]::Crl; } $obj2 = __downloadCrl $urlElement2.Url if ($obj2 -is [SysadminsLV.PKI.Cryptography.X509Certificates.X509CRL2]) { $urlElement2.SetObject($obj2) $urlElement2 = __verifyCDP $urlElement2 $projectedChain[$i + 1] $obj -DeltaCRL } else { Write-Debug "Failed to download CRL." $urlElement2.SetError($s_error -bor [PKI.EnterprisePKI.UrlStatus]::FailedToDownload) $urlElement2.ExtendedErrorInfo = $obj2 } $deltas += $urlElement2 } } else { Write-Debug "Failed to download CRL." $urlElement.SetError($s_error -bor [PKI.EnterprisePKI.UrlStatus]::FailedToDownload) $urlElement.ExtendedErrorInfo = $obj } $CAObject.URLs += $urlElement $CAObject.URLs += $deltas } # process OCSP links in the AIA extension $CAObject = __processOcsp $CAObject $projectedChain $out.Childs += $CAObject } $out } #endregion Write-Debug "Initializing parameterset: $($PsCmdlet.ParameterSetName)." } process { switch ($PsCmdlet.ParameterSetName) { '__CA' { foreach ($CA in $CertificateAuthority) { if (!$CA.Ping()) { Write-Debug "$($CA.DisplayName): ICertAdmin is down." $retValue = New-Object PKI.EnterprisePKI.CAObject -Property @{Name = $CA.DisplayName} $retValue.Offline() $retValue return } if (!$CA.Type.StartsWith("Enterprise")) { Write-Debug "$($CA.DisplayName): not supported edition. Current: $($CA.Type)." throw "Only Enterprise CAs are supported by this parameterset." } Write-Verbose ("{0} {1} {0}" -f ('=' * 20), $CA.DisplayName) Write-Debug ("{0} {1} {0}" -f ('=' * 20), $CA.DisplayName) Write-Debug "$($CA.DisplayName): retrieving CA Exchange certificate." $xchg = $CA.GetCAExchangeCertificate() __validateSinglePath $xchg } } '__EndCerts' { $Certificate | ForEach-Object {__validateSinglePath $_} } } } } # SIG # Begin signature block # MIIvJQYJKoZIhvcNAQcCoIIvFjCCLxICAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDY2SzAXitD++WH # fWaGcvymRI6nXYOJUKEpAj6NLrE8B6CCFA4wggWQMIIDeKADAgECAhAFmxtXno4h # MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV # BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z # ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB # AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z # G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ # anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s # Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL # 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb # BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3 # JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c # AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx # YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0 # viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL # T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud # EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf # Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk # aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS # PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK # 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB # cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp # 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg # dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri # RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7 # 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5 # nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3 # i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H # EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G # CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C # 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce # 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da # E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T # SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA # FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh # D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM # 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z # 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05 # huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY # mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP # /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T # AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD # VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG # A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV # HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU # cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN # BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry # sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL # IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf # Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh # OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh # dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV # 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j # wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH # Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC # XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l # /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW # eE4wggfCMIIFqqADAgECAhAGA4KnHGNGeWNwwL64+8bqMA0GCSqGSIb3DQEBCwUA # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwHhcNMjQwMjE2MDAwMDAwWhcNMjcwMjE3MjM1OTU5WjCByjET # MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhEZWxhd2FyZTEd # MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzcxODYwNjgx # CzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZPcmVnb24xETAPBgNVBAcTCFBvcnRsYW5k # MRowGAYDVQQKExFQS0kgU29sdXRpb25zIExMQzEaMBgGA1UEAxMRUEtJIFNvbHV0 # aW9ucyBMTEMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDB3dy7mO4+ # NTSZ/VJqwaAzkrjTkgetLDeXsxLmPzz1dRSJJjMe82xTXkMz8ogSJYDzstAKD5Wi # HgRs+OSK2nwtHHV+pG32kEWvZtxQrUA/e0VILxyfA3UrSV/O3NZ1icR5kaKkuB2o # EG+Fi6Rmk6a7a4uIx667wbxFkKJ88DXYlL9l5gw38ELmQSHx5qZO4RypZx15jhJi # gLkVEYso8jvZpSPZkoKpCWBsmXvA3x9B0azWkpZznspdpWZr38ZyPc6asBXi7woZ # /yVA9TpxD3hPe7a0P6fNGCyErwOcXlaEgxofwHWvPfwMQlYDdR1JlvhjZ1nlOqM7 # t7mBhFdka9NCDHX1P1ZxpQZuzlZDfsWbVTjNSm0Ru+ZzCTVTMu8LQoxmHgmX6SgU # Axa6k9tXecCLBUj5BeqATwN3uW/MPSLzMlLXTFDcGibaqQUSsxQrsD9XYM6fkC4n # sWi1aKF8A8X2zFNOxkiMBJ9vcahYC1ipkobRLkPKXa25nthXdPijlf/62O7EGMPX # X38BSp1+XFp6xh8yru2BtBRmwqGYESUKO3hac6G3qFOQcxAigh+n2Gs4MbF+Y8p6 # 2V6yISNtiZzRLUDGkEU7FyEPFj1bfq6W141g0atqakH70Qvgtqv3JHFJI4wQXmj4 # at5bIz70emJTA9LUsl4VpXihgdhJqY8jnwIDAQABo4ICAjCCAf4wHwYDVR0jBBgw # FoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFC1EF5EaZJGlny3kLKBz # rF05hbECMD0GA1UdIAQ2MDQwMgYFZ4EMAQMwKTAnBggrBgEFBQcCARYbaHR0cDov # L3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK # BggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEz # ODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j # cmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k # aWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIw # MjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggIBAF/iLNlb6YAZ # mTBeY2r6vGsSzw5GmYYVyZVNx9JIPAL0c4maTStNWOmz0qbcA418mu1CVLiCxVlu # IjSbu14ido3SBLRMukQKu08wJGyVn0sKk/A+TG8pmxKRdhFgia3UyIdVU7+a32uw # wB03/rOlqj2EsM8CQKes3i/h1hB7AkaPXonIrHYXadTyEbr6T7INib9zwaA4DxDa # d12vxXfmhQBofc84ZlulZOI3A10VRuQ9IMvg1HtkEnZKPpkaNQ8RHJJ2uuXw0uI5 # BTRRCAbT1ffLSUABG/Oo8Vcbehq4+HPPQnOIo5ApAAoz9d2lMyHwmTLxLKK4Jz9A # rI9ObJrbcG28Iemurjw/NykxolHrVJQkZRIushQnjTn2fvcD8a5kv5gHNK7S8Uw8 # zN4boJCjXYqskZ2Vbwu+AV6BJZhDGfo++H+OBxmo3U4+dPpFfN98ScXTjHKBDKy8 # LBRDCOgx0n5Nqy5c0jatqY/ew/irNkNNgNLiFGufjdz8KQeiMTM/gIF0pDv+wAiN # ZsxYQPRhWczn+zD7SPpKF4zkG9xfhOLFgyxeYmiyiyG64nbSCScX/mN9cjmM/xw1 # r54L5Q8GBCdBffSliqiVyyLuZPXUQ9wfpDgDDpaUoiKaGyCUon63vRyCut5JwfB/ # UmoQVy/g6B40cYLXZp4oBIVdeE2rtVuUMYIabTCCGmkCAQEwfTBpMQswCQYDVQQG # EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0 # IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0Ex # AhAGA4KnHGNGeWNwwL64+8bqMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcC # AQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYB # BAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIODuBxn87TG2 # 5pf9VgGyFybzFDD2Hadeohhux7zagllIMA0GCSqGSIb3DQEBAQUABIICALvyP41+ # Elx9tvkqIQYFHAHZlURSc0dJAbvWuEjsRyiZaGXremXScVfejy67vq5l6WPFLQvL # oJ/7AkIAaNDfyZwKQYvhPXpwi1eoaPwP+kGN0/gDYayU9yJlrHc/2YBzlnhqE8O/ # I6bFS+ly+lC5BRd3H+bFLO3DT3yRSoTBFgd1v1zdlfphpWVcvHcODwzdqqd+mXoD # kqGQFfVrjMY2LO/1np7Np+DQvjmrwQWAt9hUNMr0k3V2dPACOny5fY5TwkJKlqJK # 2vV/hgHZv4nQutHAI+DmUybAitwXGAug0r73Umut5JgRgozV+n0m2NcvkaolPrH5 # wU3p88/8B33Ta0toWFSGSb+rMsK1Zm871E2hpp3qKXy8lXPiUUAJ3YMcQczt0Om7 # YKlcfUo3oPts4c3S72Ws6SktyoeL6P9zm4JgAlfiNxFK0Gkd5pYPn53Cv/pP8S6O # TFgmu8KWvfDSbPDAfFou/Ql1tZvPCKiV3i+6p48+W3RTufByZE1KQhTEbc4HQzb3 # tEJLzXdpBnXd+CBljfvKOwF2Yw3gS6V0pzVTVOU3PbCiuxYRTLYiGqGMWW5BWGMW # eDxhWM5Yni69HVWs4gFfsOrNI1yz0qHlmWrKvvUTUx4OuWl9d0OzY8FWvowtWbKb # H8oglEauG8hkK9NhdZKaC95KOk6FwjKFMuGyoYIXOjCCFzYGCisGAQQBgjcDAwEx # ghcmMIIXIgYJKoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4 # BgsqhkiG9w0BCRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIB # BQAEIOsUXzW9/8OQqp35RHwl6WLojtvIk3ChTtb9AX3uDShvAhEApcroeYQyg2dU # JDu1xx2/dhgPMjAyNTA0MDIwNzQyMjBaoIITAzCCBrwwggSkoAMCAQICEAuuZrxa # un+Vh8b56QTjMwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNV # BAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0 # IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBa # Fw0zNTExMjUyMzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2Vy # dDEgMB4GA1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3 # DQEBAQUAA4ICDwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnj # pcH9CjAgQxK+CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghC # aft0djvKKO+hDu6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+ # Un1h4eN6zh926SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ4 # 9+wxGE1/UXjWfISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/fe # un0eV+pIF496OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEW # dX4rA/FE1Q0rqViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6V # TZ3rOHNeiYnY+V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF # 5IDbPgjvwmnAalNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8Aa # iCaiJnfdzUcb3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBK # AOlb2u29Vwgfta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOG # hCjl+QIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAw # FgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJ # YIZIAYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1Ud # DgQWBBSfVywDdw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hB # MjU2VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEF # BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRw # Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2 # U0hBMjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4f # dplb4ziEEkfZQ5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz # 7wZmXa5VgW9B76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQe # h83pXnWwwsxc1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6 # u4I/VBQC9VK7iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvm # gF8VlJt1qQcl7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k # 5QrN9FQBhLLISZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPl # MwerwJZP/Gtbu3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WL # FQOhtloDRWGoCwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX # 7TaRQuR+0BGOzISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/k # z1YCsdhIBHXqBzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccH # HXKibMs/yXHhDXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ck # VHzYR6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNV # BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8G # A1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoX # DTM3MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0 # LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hB # MjU2IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJf # pIjzaPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r # 2j3EF3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tE # QYncfGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkS # Z+8OpWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCw # MROpVymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vs # gd4iFNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZz # QmiftkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJ # UlD0UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6z # j9NeS3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ1 # 4mCjWAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIB # WTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGog # j57IbzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8E # BAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsG # AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0 # dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQu # Y3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsG # CWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC # 4bAYLhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQg # JTQxZ822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequF # zUNf7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhU # ifDVinF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g # 55j7+6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7 # HQhJD5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLX # JmvkOHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvY # fvJGnXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXA # OimGsJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQ # I38AC+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrn # Ew4d2zc4GqEr9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0G # CSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0 # IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5 # NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV # BAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQg # Um9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvk # XUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdt # HauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu # 34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0 # QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2 # kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM # 1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmI # dph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZ # K37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72 # gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqs # X40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyh # HsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8E # BTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAW # gBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUH # AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI # KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMu # ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAE # CjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX # 979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offy # ct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3 # J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0 # d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6ts # ds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQx # ggN2MIIDcgIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1 # NiBUaW1lU3RhbXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIB # BQCggdEwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEP # Fw0yNTA0MDIwNzQyMjBaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+ # e+T2cUhQhyTVhltFMC8GCSqGSIb3DQEJBDEiBCBjwzFIz3L20WhwtBHShQpFwgji # WhcoZtyrTIG6IyRC+DA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0ML # OiMwrtZWdf7Xc9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgBgoaLWf01W # gaqvuhxT0l7xGXs7n9jgJ5QO1kJs25q3vE17uvX4Q8hFTANKTp0ZKtyj2uqljfcI # ZfGUWVHMjY5LNzLD/Lc5VJzcVqWoYibpQBU62G9mbUf2lQK/5iKMXyWflYymtOLm # rnOse8UqN/zpe4mmzsJaQlidxDtbrUu2HQhTJfKhZPdFUC0lwx5M7CQDOsaFbXvX # aMuB8pH9cAiEQbPI81jKr2DzxhMi30maa9+9E7Ed/1KcoYJ2H9FInacnwx75P4+Q # d5mxui1d78x/kPAVF8ML8en3Tg51pitMdPpRl1kSiCLlOi25xVSp5i9tX0UzcLWG # /fgUfzQgyUZyHzRSwESkTfpiVpBl/vrWEAVYhLuU0bzx/cZOLqYofGIrfipJMSZ/ # Up1gyV9b2bjzpHw+MHlkiqMWIKqcJwag7JaxgrFp5Fe+Z7j6IguAJNfjKhwWQhM6 # Hk0SIjYTQI7Woj6gLzlI8nz1YRGLlE5vITyxMx8+PnvJHml2mtobQ3b5XDh4rlVp # qxSh5F019nEIt+KXc2qRsisxQetVBUHQRLzhFESvDADoTWE5yuTP4w7bwc9zeh8J # bo+/3SbOMP+INyyT/YuPCz2b1GZ1ylKeM01QQp2Lf674DHOfEFRdwxLNj+0Ah6gM # 4YbaI+w8qD3iFcpPqPic798F1JXUdJrEGA== # SIG # End signature block |