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) Clear-ComObject $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) Clear-ComObject $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..." $extension = $cert.Extensions["2.5.29.31"] if ($extension) { $asn = New-Object Security.Cryptography.AsnEncodedData (,$extension.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..." $extension = $cert.Extensions["1.3.6.1.5.5.7.1.1"] if ($extension) { $asn = New-Object Security.Cryptography.AsnEncodedData (,$extension.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) $extension = $crl.Extensions["2.5.29.46"] # Freshest CRL if ($extension) { $URLs.FreshestCRL = $extension.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) { $extension = $crl.Extensions["1.3.6.1.4.1.311.21.4"] if (!$extension) {return} $dt = try { (New-Object SysadminsLV.Asn1Parser.Universal.Asn1UtcTime -ArgumentList @(,($extension.RawData))).Value } catch { (New-Object SysadminsLV.Asn1Parser.Universal.Asn1GeneralizedTime -ArgumentList @(,($extension.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 @(,($DeltaCrlIndicator.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) } 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) } # 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 } } # 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[$n + 1] } else { Write-Debug "Failed to download certificate." $urlElement.SetError($s_error -bor [PKI.EnterprisePKI.UrlStatus]::FailedToDownload) $urlElement.ExtendedErrorInfo = $obj } $CAObject.URLs += $urlElement } } # 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; } __verifyOCSP $projectedChain[$n] $urlElement $CAObject.URLs += $urlElement } } # 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 ($chainIndex = 0; $chainIndex -lt $projectedChain.Length; $chainIndex++) { Write-Debug "========================= $($projectedChain[$chainIndex].Certificate.Issuer) =========================" # skip self-signed certificate from checking if (!( Compare-Object -ReferenceObject $projectedChain[$chainIndex].Certificate.SubjectName.RawData ` -DifferenceObject $projectedChain[$chainIndex].Certificate.IssuerName.RawData)) { Write-Debug "Leaf certificate is self-signed, skip validation." break } [void]($projectedChain[$chainIndex].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} $BaseCrlURlPack = __getUrl $projectedChain[$chainIndex].Certificate.RawData $true # process and validate certificate issuer in the AIA extension __processCerts $CAObject $projectedChain # process and validate CDP extensions for ($baseCrlIndex = 0; $baseCrlIndex -lt $BaseCrlURlPack.CDP.Length; $baseCrlIndex++) { $deltas = @() $BaseCrlUrlElement = New-Object PKI.EnterprisePKI.UrlElement -Property @{ Name = "CDP Location #$($baseCrlIndex + 1)"; Url = $BaseCrlURlPack.CDP[$baseCrlIndex]; UrlType = [PKI.EnterprisePKI.UrlType]::Crl; } $BaseCRL = __downloadCrl $BaseCrlUrlElement.Url if ($BaseCRL -is [SysadminsLV.PKI.Cryptography.X509Certificates.X509CRL2]) { $BaseCrlUrlElement.SetObject($BaseCRL) __verifyCDP $BaseCrlUrlElement $projectedChain[$chainIndex + 1] $DeltaCrlUrlPack = __getUrl ($BaseCrlUrlElement.GetObject()).RawData $false # process and validate FreshestCRL extension if exist for ($deltaCrlIndex = 0; $deltaCrlIndex -lt $DeltaCrlUrlPack.FreshestCRL.Length; $deltaCrlIndex++) { # skip duplicate if ($deltas | Where-Object {$_.Url -eq $DeltaCrlUrlPack.FreshestCRL[$deltaCrlIndex]}) { return } $DeltaCrlUrlElement = New-Object PKI.EnterprisePKI.UrlElement -Property @{ Name = "DeltaCRL Location #$($deltaCrlIndex + 1)"; Url = $DeltaCrlUrlPack.FreshestCRL[$deltaCrlIndex]; UrlType = [PKI.EnterprisePKI.UrlType]::Crl; } $DeltaCRL = __downloadCrl $DeltaCrlUrlElement.Url if ($DeltaCRL -is [SysadminsLV.PKI.Cryptography.X509Certificates.X509CRL2]) { $DeltaCrlUrlElement.SetObject($DeltaCRL) __verifyCDP $DeltaCrlUrlElement $projectedChain[$chainIndex + 1] $BaseCRL -DeltaCRL } else { Write-Debug "Failed to download CRL." $DeltaCrlUrlElement.SetError($s_error -bor [PKI.EnterprisePKI.UrlStatus]::FailedToDownload) $DeltaCrlUrlElement.ExtendedErrorInfo = $DeltaCRL } $deltas += $DeltaCrlUrlElement } } else { Write-Debug "Failed to download CRL." $BaseCrlUrlElement.SetError($s_error -bor [PKI.EnterprisePKI.UrlStatus]::FailedToDownload) $BaseCrlUrlElement.ExtendedErrorInfo = $BaseCRL } $CAObject.URLs += $BaseCrlUrlElement $CAObject.URLs += $deltas } # process OCSP links in the AIA extension __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.IsEnterprise) { Write-Debug "$($CA.DisplayName): not supported edition. Current: $($CA.Type)." throw "Only Enterprise CAs are supported by this parameter set." } 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 # MIIvDQYJKoZIhvcNAQcCoIIu/jCCLvoCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAZWJPAxaIyLp6b # N5y7nEt/1xpNWJUXdyWZdtMo3MKB4KCCE5kwggWQMIIDeKADAgECAhAFmxtXno4h # 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 # eE4wggdNMIIFNaADAgECAhAObby6tbZ0sFYtkajicQ8aMA0GCSqGSIb3DQEBDAUA # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwHhcNMjYwMjA2MDAwMDAwWhcNMjkwMjI3MjM1OTU5WjBVMQsw # CQYDVQQGEwJMVjEOMAwGA1UEBwwFUsSrZ2ExGjAYBgNVBAoMEUlLICJTeXNhZG1p # bnMgTFYiMRowGAYDVQQDDBFJSyAiU3lzYWRtaW5zIExWIjCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBAKwGGasrfMfwdc37W1d3/vgvVZ5Wgfmk8Rd4sb3e # f1vZ4D2kA0sLNU1AwbwfLjnnRT0brMbrI2y/MSt10oqOFSnR7nw9AkhW/1PVZS9l # rxRYX/g/RjO+lG5TnxRTsaWUiw7clvhRipxBgWJuxky8MuUD/eTyMLu1rn6He7p/ # cp4kg6nu6ZsaPxsEY9EBTAdXzlyYS/U3NJMk8GjrLdVAlZpoONXnoB9yChIly409 # yh2xWczWIUv9ku2SFrZbB6l3YhY2inDAPKpAKyCd0xeQPKSeRoC+JXAnA1dSSRJ9 # 557LFFBfnR4XFh4E1OHuKRfTk18UjfwQqdF4cdmH5I4rR5Pqhy2shwBP8XrT26em # OhpfUAWVfJWIWcoQPq9JV/3dr8tPZgCW97dZpmu0/q3TlHS/sPASNO279diN2o/f # H0fsNPBzFSe85BCtUk3muyEhVHU3R//Lg1MSFB0BRFeAafhlsm/VvhIQ8tQvb7it # a7UB7gacdB795+XhpY0MLc0fFsrSTENxXeH5N7sIlNa3zao4l3IiNmR6lwvm6IMf # L8aRBPWg6PicLK2sXa10PDQLxee533dHuuvvpaVdpyvcEpaCHlaSKxeFhwur339D # yuaWQ37rospo/pS8oaG05edihaF7O4LRiqeVDx1RL9Yd4uPH02J284seLMKTZ7wa # Tle1AgMBAAGjggIDMIIB/zAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfRO # QjAdBgNVHQ4EFgQU8Tdc7JEZ0WMWKjWL7tfz8hanUtgwPgYDVR0gBDcwNTAzBgZn # gQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BT # MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGt # MIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZN # aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNp # Z25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGE # MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUH # MAKGUGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH # NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAw # DQYJKoZIhvcNAQEMBQADggIBAIRcHe3iWZ4t2G3pttGtw01+okbWPPachOEYxiGL # 2Gp7DBP3Pvfw1qEHnwULtPs0hBG/Rd8+TtKkTpyZO0E2EfIYfe/EVmXjr+6jrOnU # beRiVDMahp7ym37FhrRPzzDEihFmyzz/Ec8YmE4oeEH+0ZTKT+7F7VThVmVGWQ6x # YIGqSL3k6/Af1P1gdK+1MdQtB/E1mg+7aDqiOxCRRsfwXwLjB+LSi0GflBQZeeHf # pvogtHPWM6yMEZVZkY6sxF9jo9ewhVc9DJB86KQSxJ14vkM0OAu7Q83L8N+exia2 # XuC3C+7nuVdHB7+HHu3z/Cz1aftWuHLnVRYrhJZCZ+46YvjRFQxf+GIjqyopBfDh # DX6Oq4GlOSrJPnSnyRgNu4Mr4nOWJ2LuTzFmgJu+GmeHSLH+U2uudNdagjkjRnWQ # TOZ4hdnqG0WlhMazHMf0RGx8s3DRIX9kjttDQ3RbYUJRqMq9KVf5K+CdNYKRyqET # 4tn+ixidYWazVTgsaQi1ZxmdxmS784F5CQxLlg87xWIUlWyIA2fWWnqePxGaLZrB # pDr0KkKT6gF3z2+Bs9q3o1sOAvjP7SvaBLJHuBmC5oeKnbZa+iuVEJFQShEdfl28 # Khk3jA3RU0cOK+CJMup5FXo4mdydO2ItLKZ62vC4f2m3IinXfyh7MfUyZQ0heZMi # Who4MYIayjCCGsYCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl # cnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWdu # aW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAObby6tbZ0sFYtkajicQ8aMA0G # CWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZI # hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC # ARUwLwYJKoZIhvcNAQkEMSIEINUE6McF76fSfUDWL5SyCsG3DgTGRID4ZsthgslX # zJhxMA0GCSqGSIb3DQEBAQUABIICAH45uF62r+U4ea1nZphblXzzLzFc0gm0UCNZ # MoZJ4QxrUydi+ae4cLZD3K2Fd9pmU9djaZIFYAFf4Gs+Pqm3rHiZz43jVFLqBeoX # t12L+shZYapMx0ttf+sLgrasJVdjWY2SYYBJocUj9oTRIbdX4EPSTY+4+8/5sv24 # R2MRf7cE0W2qd8lyotGvn4uFNe1NVmTsTdi0DHDEmsxUUFfFLvysKeM6F4YTVQwJ # MYFhCFTaTZQsMCHrraIdXnNSoLGT1q/ozaS0ZLc2tKbBeBFhrmPneVkxDlvlQrtx # OSgPCTVsKEYsj7US05o5Rp0Xb76mzqHTj6b8M0eOuC/+oX6RpMuUafFqg8KFdln0 # 3+qyAQGWIlKOC65mZzcRFW+nj6/kWQ70dhwT6OgIf9uimPZWM7f/cgFJCPYEfCgg # EFTRP9C6Ht2N09sIm1AQSBtHFyCOmbfPvkrjUkntkX7di4MYoHbSvV5+8vDDw+rr # YJYrDglBnAr16KZ1pQJaGJVAAiFnZmWXQbSXFVe9Go2mUO/cuNDXeefQbgFLAV2T # ZnVimD+O7S6oQYe4Lujbk3ZvKF2OQ/3GWnFpIxluZwb85EVIzMYdwiMDy0DoSy3a # eWmMn089lbRZ6c8yY9cTmYww0pUZUXB/cTDnWQfIIL1E/L9iwqKCKMYf6UHlEShH # T57jI89hoYIXlzCCF5MGCisGAQQBgjcDAwExgheDMIIXfwYJKoZIhvcNAQcCoIIX # cDCCF2wCAQMxDzANBglghkgBZQMEAgIFADCBhwYLKoZIhvcNAQkQAQSgeAR2MHQC # AQEGCWCGSAGG/WwHATBBMA0GCWCGSAFlAwQCAgUABDAhR3lTFnjFKwmPv9hGrDGa # HY1EtK2ighgAcgifCy6GeXegRuhe6OyvQWpHgPoJ/jACEGS+fmzscCNbjKdUZukx # neIYDzIwMjYwNjA4MjExNjA5WqCCEzowggbtMIIE1aADAgECAhAMIENJ+dD3WfuY # LeQIG4h7MA0GCSqGSIb3DQEBDAUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5E # aWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1l # U3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwHhcNMjUwNjA0MDAwMDAw # WhcNMzYwOTAzMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl # cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFNIQTM4NCBSU0E0MDk2IFRpbWVz # dGFtcCBSZXNwb25kZXIgMjAyNSAxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEA2zlS+4t0t+XJDVHY+vNJxpv794sM3O4UQycmKRXmYLs+YRfztyl8QJ7n # /UqxNTKWmjdFDWGv43+a2oiJ41yxOe0sLoFx8F1az2JRTZc7dhAxbne+byd5bf2S # EZlCruGxxWSqbpUY6dAGRCCyBOaiFaoXhkn+L15efcomDSrTnA5Vgd9pvMO+7bM+ # tSW4JzAiIbO2mIPyCEdKYscmPl+YBuenSP7NJw9icL1tWpn61uM6WyUNv4RcyBAz # +NvJbNf5kTM7F46cvBwp0lZYisZR985y5sYj4e4yUBbPBxyrT5aNMZ++5tis8GDm # HCpqyVLQ4eLHwpim5iwR49TREfETtlEFORWTkJ2hOO1zzVAWs6jtdep12VtFZoQO # hIwdUfPHSsAw39xFVevFEFf2u+DVr1sOV7JACY+xcG8hWIeqPGVUwkiyBRUTgA7H # eAxJb0iQl4GDBC6ZBA4wGN/ahMxF4fuJsOs1zwkPBSnXmHkm18HwHgIPKk287dMI # chZyjm7zGcCYZ4bisoUYWL9oTga9JCfFMTc9yl26XDB0zl9rdSwviOmaYSlaRanF # 84oxAYnqgBy6Z89ykPgWnb7SRi31NyP359Whok+36fkyxTPjSrCWvMK7pzbRg8tf # IRlUnxl7G5bIrkPqMbD9zJoB79MHFgLr5ljU7rrcLwy+cEfpzFMCAwEAAaOCAZUw # ggGRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFFWeuednyJEQSbQ2Uo15tyTFPy34 # MB8GA1UdIwQYMBaAFO9vU0rp5AZ8esrikFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIH # gDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYI # KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZR # aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGlt # ZVN0YW1waW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBS # oFCGTmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRp # bWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgG # BmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQEMBQADggIBABt+CySH2Alq # xUHnUWnZJI7rpdAqo0PcikyV48Ltk5QWFgxpHP9WtjR3lskEAOk3TszmuNyMid7V # uxHlQJl4KcdTr5cQ2YLy+l560peBgM7kA4HCJqGqdQdzjXyrlg3YCdfnjs9w/7BO # 8xUmlAaq/D+PTZZO+Mnxa3/IoyYsF+L9gWX4VJxZLljVs5JKmpSonnysMYv7Caqk # QpBDmJWU2F68mLLZXfU0wXbDy9QQTskgcHviyQDeB1l6jl/WwOQiSNTNafYQUR2Z # sJ5rPJu1NPzO1htKwdiUjWenHwq5BRK1BR7+D+TwG97UHX4V0W+JvFZp8z3d3G5s # A7Pt9qO5/6AWZ+0yf8nN58D+HAAShHmny25t6W7qF6VSRZCIpGr8hbAjfbBhO4MY # 8G2U9zwVKp6SljuKknxd2buihO33dioCGsB6trX++xQKf4QlYSggFvD9ZWSG4ysJ # PYOx+hbsBTEONFtr99x6OgJnnyVkDoudIn+gmV+Bq+a2G++BLU5AXOVclExpuoUQ # XUZF5p3sUrd21QjF9Ra0x4RD02gS4XwgzN+tvuY+tjhPICwXmH3ERL+fPIoxZT0X # gwVP+17UqUbi5Zpe4YdadG5WjCTBvtmlM4JVovGYRvyAyfmYJJx0/0T+qK05wRJp # g4q81vOKuCQPaE9H99JCVvfCDBm4KjrEMIIGtDCCBJygAwIBAgIQDcesVwX/IZku # QEMiDDpJhjANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM # RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQD # ExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjUwNTA3MDAwMDAwWhcNMzgw # MTE0MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu # Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJT # QTQwOTYgU0hBMjU2IDIwMjUgQ0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAtHgx0wqYQXK+PEbAHKx126NGaHS0URedTa2NDZS1mZaDLFTtQ2oRjzUX # MmxCqvkbsDpz4aH+qbxeLho8I6jY3xL1IusLopuW2qftJYJaDNs1+JH7Z+QdSKWM # 06qchUP+AbdJgMQB3h2DZ0Mal5kYp77jYMVQXSZH++0trj6Ao+xh/AS7sQRuQL37 # QXbDhAktVJMQbzIBHYJBYgzWIjk8eDrYhXDEpKk7RdoX0M980EpLtlrNyHw0Xm+n # t5pnYJU3Gmq6bNMI1I7Gb5IBZK4ivbVCiZv7PNBYqHEpNVWC2ZQ8BbfnFRQVESYO # szFI2Wv82wnJRfN20VRS3hpLgIR4hjzL0hpoYGk81coWJ+KdPvMvaB0WkE/2qHxJ # 0ucS638ZxqU14lDnki7CcoKCz6eum5A19WZQHkqUJfdkDjHkccpL6uoG8pbF0LJA # QQZxst7VvwDDjAmSFTUms+wV/FbWBqi7fTJnjq3hj0XbQcd8hjj/q8d6ylgxCZSK # i17yVp2NL+cnT6Toy+rN+nM8M7LnLqCrO2JP3oW//1sfuZDKiDEb1AQ8es9Xr/u6 # bDTnYCTKIsDq1BtmXUqEG1NqzJKS4kOmxkYp2WyODi7vQTCBZtVFJfVZ3j7OgWmn # hFr4yUozZtqgPrHRVHhGNKlYzyjlroPxul+bgIspzOwbtmsgY1MCAwEAAaOCAV0w # ggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO9vU0rp5AZ8esrikFb2 # L9RJ7MtOMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB # /wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYI # KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1 # aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RH # NC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIw # CwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQAXzvsWgBz+Bz0RdnEwvb4L # yLU0pn/N0IfFiBowf0/Dm1wGc/Do7oVMY2mhXZXjDNJQa8j00DNqhCT3t+s8G0iP # 5kvN2n7Jd2E4/iEIUBO41P5F448rSYJ59Ib61eoalhnd6ywFLerycvZTAz40y8S4 # F3/a+Z1jEMK/DMm/axFSgoR8n6c3nuZB9BfBwAQYK9FHaoq2e26MHvVY9gCDA/JY # sq7pGdogP8HRtrYfctSLANEBfHU16r3J05qX3kId+ZOczgj5kjatVB+NdADVZKON # /gnZruMvNYY2o1f4MXRJDMdTSlOLh0HCn2cQLwQCqjFbqrXuvTPSegOOzr4EWj7P # tspIHBldNE2K9i697cvaiIo2p61Ed2p8xMJb82Yosn0z4y25xUbI7GIN/TpVfHIq # Q6Ku/qjTY6hc3hsXMrS+U0yy+GWqAXam4ToWd2UQ1KYT70kZjE4YtL8Pbzg0c1ug # MZyZZd/BdHLiRu7hAWE6bTEm4XYRkA6Tl4KSFLFk43esaUeqGkH/wyW4N7Oigizw # JWeukcyIPbAvjSabnf7+Pu0VrFgoiovRDiyx3zEdmcif/sYQsfch28bZeUz2rtY/ # 9TCA6TD8dC3JE3rYkrhLULy7Dc90G6e8BlqmyIjlgp2+VqsS9/wQD7yFylIz0scm # bKvFoW2jNrbM1pD2T7m3XDCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFow # DQYJKoZIhvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0 # IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNl # cnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIz # NTk1OVowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG # A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3Rl # ZCBSb290IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2je # u+RdSjwwIjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bG # l20dq7J58soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBE # EC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/N # rDRAX7F6Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A # 2raRmECQecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8 # IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfB # aYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaa # RBkrfsCUtNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZi # fvaAsPvoZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXe # eqxfjT/JvNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g # /KEexcCPorF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB # /wQFMAMBAf8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQY # MBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEF # BQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBD # BggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0 # QXNzdXJlZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3Js # My5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1Ud # IAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22 # Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih # 9/Jy3iS8UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYD # E3cnRNTnf+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c # 2PR3WlxUjG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88n # q2x2zm8jLfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5 # lDGCA4wwggOIAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0 # LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGlu # ZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMQIQDCBDSfnQ91n7mC3kCBuIezANBglg # hkgBZQMEAgIFAKCB4TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZI # hvcNAQkFMQ8XDTI2MDYwODIxMTYwOVowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU # crz9oBB/STSwBxxhD+bXllAAmHcwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgMvPj # sb2i17JtTx0bjN29j4uEdqF4ntYSzTyqep7/NcIwPwYJKoZIhvcNAQkEMTIEMJd+ # NgDEgYRIe4f1jPxc4CDfeHJBNgulK3LnlgfuwiYA5kJFXxBk06vULLYCyf2+HTAN # BgkqhkiG9w0BAQEFAASCAgCwp1UAAJ40OPGxvij1FmU+2rHNFNz4UsJ+tkM8xzrM # hP1FmhBwur5W39us5lFwqx4wEuFrCCZ/L5tC/zmp3GjpnNUGZsfULqcIEdjwIaGV # lWQyW0JkQ2bf9odVwxhDu3Rvu9CZVi0XrD2NNnARqr8wAckSqid7HAr+80kQKNkk # 8Ur47snxLvhqKxt5XAaARLtvCfHDBgzWY2gI82caxbBGhWZVrrqrkBGo0GeGuGRH # svW6Q8cmQ+uQWm+a3djzQOWfXZs2/A3yY+0kldIupGsDSJApHDkEpqrAE8BVUlvI # SLGWVAaoexKgMkfNhc0bYgBwJfUI2uzhqB/6oQcxTMoOiijav1oHUJhtpqBDYzcu # Z9tZHwQdEj69zJjnAMa1onTokdklo1gAYyH5s1BEdOblwlnFBA9vjKqrzf48s6FJ # saJ2yxUyjX097oOavD9hlSd1JEJkKuDs3fXeSXyGKQxP5JNQT8V+aED9QYkZSBLt # xgjxyaFzKen/ZNZyuismiB8KVWsEVT74MewDv2Vb9VKRIj9qTR8Dch6OzAONrYr3 # dzn8gBzHRahMli7/is+rMENAD+2uuQUxf2r6yAveNkgvM7ZBbkxJA2ILl7PHayqP # 6u/8srOki0pCOI0E4iUYLbPdzGupyEk/iGNKEW0Sot4aMdo5kxnyBQZ9+tkz1rge # iw== # SIG # End signature block |