Functions/Remove-ExpiredCertificate.ps1
|
<#
.SYNOPSIS Delete expired certificates from the CA server database. .PARAMETER State This parameter defines what type of certificate record to delete - Denied, Failed, Issued, or Revoked. Multiple values may be specified. Use the value 'All' to process all record types. .PARAMETER Template The Object Identifier (OID) of a specific certificate template to delete database records for. Use Get-ADCertificateTemplate to retrieve the OID of a published certificate template. When multiple states are specified, the template filter applies to every state processed. .PARAMETER Date Database records older than this date will be deleted. Accepts any date format valid for the current culture. The date must be no later than today. The default value is today. .PARAMETER Delete Use this switch to delete records from the CA database. If this switch is not present, the command runs in view only mode and records that would be deleted are written to a log file for review. No records are deleted. Deleting records requires the Manage CA permission on the certification authority. Deleting records is a high impact operation and confirmation is required by default. Use -Confirm:$false to suppress the confirmation prompt. .PARAMETER LogFilePath Specifies the location to store CA maintenance log files. The default location is C:\Users\<username>\AppData\Local\Temp\. .PARAMETER Csv Use this switch to create an additional log file in CSV format alongside the standard text log file. The CSV file is written to the same location and shares the same name as the text log file with a .csv extension. Creating the CSV file requires a second CA database query, which may increase processing time on CA servers with large databases. .PARAMETER CompactDatabase Use this switch to compact the CA database after performing maintenance (recommended). Compacting the database stops and restarts the Certificate Services service and requires an elevated PowerShell session. This switch is only valid when used with the -Delete parameter and is ignored in view only mode because no records are deleted. .EXAMPLE Remove-ExpiredCertificate -State Denied Records all expired Denied certificates to a log file for review. Does not delete any records. .EXAMPLE Remove-ExpiredCertificate -State Failed -Delete Deletes all expired Failed certificates. Confirmation is required before records are deleted. Use -Confirm:$false to suppress the confirmation prompt. .EXAMPLE Remove-ExpiredCertificate -State Issued, Denied Records all expired Issued and Denied certificates to log files for review. Does not delete any records. .EXAMPLE Remove-ExpiredCertificate -State All -Delete Deletes all expired Denied, Failed, Issued, and Revoked certificates. Confirmation is required before records are deleted. Use -Confirm:$false to suppress the confirmation prompt. .EXAMPLE Remove-ExpiredCertificate -State Issued -Csv Records all expired Issued certificates to a log file for review and creates an additional log file in CSV format. Does not delete any records. .EXAMPLE Remove-ExpiredCertificate -State Issued -Template '1.3.6.1.4.1.311.21.8.8823763.7881424.11597667.39223303.50834909.808.1387547.7582140' Records all expired Issued certificates based on the specified certificate template OID to a log file for review. Does not delete any records. .EXAMPLE Remove-ExpiredCertificate -State Revoked -Date 12/31/2022 -Delete -Confirm:$false -CompactDatabase Deletes all expired Revoked certificates prior to December 31, 2022 without prompting for confirmation and compacts the CA database. .EXAMPLE Remove-ExpiredCertificate -State Issued -Delete -WhatIf Displays the records that would be deleted without making any changes to the CA database. .DESCRIPTION Use this command to remove expired certificates from a CA server, and optionally compact the CA database after performing maintenance. Deleting records requires the Manage CA permission on the certification authority. Compacting the database requires an elevated PowerShell session. This function parses certutil.exe output and requires an English operating system display language. This function depends on the private ADCSTools module functions Test-IsElevated and Test-CaPermission and is not intended to run standalone outside the module. .INPUTS None. This function does not accept pipeline input. .OUTPUTS PSCustomObject. A summary object for each state processed containing the state, the number of matching records, the number of records deleted, the number of records that could not be processed, the log file path, and the CSV log file path when the -Csv parameter is used. .LINK https://github.com/richardhicks/adcstools/blob/main/Functions/Remove-ExpiredCertificate.ps1 .LINK https://vanbrenk.blogspot.com/2020/12/how-to-cleanup-expired-certificates.html .LINK https://www.richardhicks.com/ .NOTES Version: 3.0 Creation Date: January 18, 2020 Last Updated: July 26, 2026 Special Note: This script adapted from original published guidance by Andre Gibel Original Author: Andre Gibel Original Script: https://vanbrenk.blogspot.com/2020/12/how-to-cleanup-expired-certificates.html Author: Richard Hicks Organization: Richard M. Hicks Consulting, Inc. Contact: rich@richardhicks.com Website: https://www.richardhicks.com/ #> Function Remove-ExpiredCertificate { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([PSCustomObject])] Param ( [Parameter(Mandatory)] [ValidateSet('All', 'Denied', 'Failed', 'Issued', 'Revoked')] [String[]]$State, [ValidatePattern('^\d+(\.\d+)+$')] [String]$Template, [ValidateScript({ If ($_ -gt (Get-Date)) { Throw 'The date specified is in the future. Specify a date no later than today.' } $True })] [DateTime]$Date = (Get-Date), [Switch]$Delete, [String]$LogFilePath = $env:temp, [Switch]$Csv, [Alias('Compress', 'CompressDatabase')] [Switch]$CompactDatabase ) # Verify the Certificate Services service is present and running $Service = Get-Service -Name CertSvc -ErrorAction SilentlyContinue If (-Not $Service) { Throw 'Certificate Services does not appear to be installed on this server.' } If ($Service.Status -ne 'Running') { Throw 'The Certificate Services service is not running. Start the service and try again.' } # Deleting CA database records requires the Manage CA permission on the certification authority If ($Delete -and -not (Test-CaPermission -Role ManageCa)) { Throw 'The current user does not hold the Manage CA permission on this certification authority. Records cannot be deleted.' } # Database compaction serves no purpose in view only mode because no records are deleted If ($CompactDatabase -and -not $Delete) { Write-Warning 'The -CompactDatabase parameter has no effect in view only mode and will be ignored. Use the -Delete parameter to delete records and compact the CA database.' $CompactDatabase = $False } # Database compaction requires an elevated session to stop the Certificate Services service and access the database files If ($CompactDatabase -and -not (Test-IsElevated)) { Throw 'The -CompactDatabase parameter requires an elevated PowerShell session. Run PowerShell as an administrator and try again.' } # Expand 'All' to every supported state, otherwise remove duplicate values while preserving the order specified $SupportedStates = 'Denied', 'Failed', 'Issued', 'Revoked' If ($State -Contains 'All') { $StatesToProcess = $SupportedStates } Else { # Normalize each value to its canonical capitalization so output, warnings, and log file paths are consistent regardless of the casing specified $StatesToProcess = @($State | ForEach-Object { $CurrentValue = $_; $SupportedStates | Where-Object { $_ -eq $CurrentValue } } | Select-Object -Unique) } # Format the date for certutil.exe using the current culture so the restriction is interpreted correctly on the local system $RestrictDate = $Date.ToString('d', [System.Globalization.CultureInfo]::CurrentCulture) # All log files created during this run share the same timestamp $TimeStamp = Get-Date -Format 'yyyyMMdd-HHmmss' $OutColumns = 'Request.RequestID,Request.RequesterName,Request.SubmittedWhen,NotBefore,NotAfter,Request.Disposition,CertificateTemplate,SerialNumber,CertificateHash' If (-Not $Delete) { Write-Warning "'$($MyInvocation.MyCommand.Name)' is in view only mode. Use the -Delete parameter to delete CA database entries." } ForEach ($CurrentState in $StatesToProcess) { Switch ($CurrentState) { 'Issued' { $Disposition = '20' $DateFilterField = 'NotAfter' } 'Revoked' { $Disposition = '21' $DateFilterField = 'NotAfter' } 'Failed' { $Disposition = '30' $DateFilterField = 'Request.SubmittedWhen' } 'Denied' { $Disposition = '31' $DateFilterField = 'Request.SubmittedWhen' } } Write-Verbose "`$State = $CurrentState" Write-Verbose "`$Date = $RestrictDate" Write-Verbose "`$Disposition = $Disposition" # Create the log file folder if it doesn't already exist. -WhatIf is explicitly overridden because creating the log folder is required to record the matching entries during a -WhatIf pass $StateLogFolder = Join-Path -Path $LogFilePath -ChildPath $CurrentState If (-Not (Test-Path -LiteralPath $StateLogFolder)) { New-Item -Path $StateLogFolder -ItemType Directory -Force -WhatIf:$False | Out-Null } If ($Delete) { $CertLogFilePath = Join-Path -Path $StateLogFolder -ChildPath "RequestID-$CurrentState-Deleted-$TimeStamp.txt" } Else { $CertLogFilePath = Join-Path -Path $StateLogFolder -ChildPath "RequestID-$CurrentState-ViewOnly-$TimeStamp.txt" } Write-Verbose "Log file path is $CertLogFilePath." If ($PSBoundParameters['Template']) { # Select certificates matching a specific template $Restrict = "Certificate Template=$Template,Disposition=$Disposition,$DateFilterField<=$RestrictDate" } Else { # Select certificates matching any template $Restrict = "Disposition=$Disposition,$DateFilterField<=$RestrictDate" } Write-Verbose "Query: certutil.exe -view -restrict '$Restrict' -Out '$OutColumns'" & certutil.exe -view -restrict $Restrict -Out $OutColumns | Out-File -FilePath $CertLogFilePath -WhatIf:$False If ($LASTEXITCODE -ne 0) { Write-Warning "The certutil.exe database query for the $CurrentState state failed with exit code $LASTEXITCODE. Verify this server is a certification authority and the query restriction is valid. Skipping this state." Continue } # certutil.exe writes certificate hash values with a space between each byte. Remove the spaces so thumbprints can be copied directly from the log file $LogContent = Get-Content -LiteralPath $CertLogFilePath | ForEach-Object { If ($_ -Match '^(\s*Certificate Hash:\s*")([^"]*)(".*)$') { $Matches[1] + ($Matches[2] -Replace '\s', '') + $Matches[3] } Else { $_ } } Set-Content -LiteralPath $CertLogFilePath -Value $LogContent -WhatIf:$False If ($Csv) { # Create an additional log file in CSV format for review and reporting. certutil.exe cannot emit both output formats in a single pass, so a second database query is required $CsvLogFilePath = [System.IO.Path]::ChangeExtension($CertLogFilePath, 'csv') Write-Verbose "CSV log file path is $CsvLogFilePath." Write-Verbose "Query: certutil.exe -view -restrict '$Restrict' -Out '$OutColumns' csv" & certutil.exe -view -restrict $Restrict -Out $OutColumns csv | Out-File -FilePath $CsvLogFilePath -WhatIf:$False If ($LASTEXITCODE -ne 0) { Write-Warning "The certutil.exe CSV database query failed with exit code $LASTEXITCODE. The CSV log file may be missing or incomplete." } Else { # Remove spaces from certificate hash values in the CSV log file $CsvRows = @(Import-Csv -LiteralPath $CsvLogFilePath) If ($CsvRows.Count -gt 0) { ForEach ($CsvRow in $CsvRows) { $CsvRow.'Certificate Hash' = $CsvRow.'Certificate Hash' -Replace '\s' } $CsvRows | Export-Csv -LiteralPath $CsvLogFilePath -NoTypeInformation -WhatIf:$False } } } Write-Verbose 'Processing log file...' $MatchingRequestIDCollection = @(Select-String -Path $CertLogFilePath -SimpleMatch 'Request ID:') $EntryDeletedCount = 0 $EntryFailedCount = 0 If ($MatchingRequestIDCollection.Count -eq 0) { Write-Warning "No $CurrentState entries to delete from the CA database." } Else { Write-Verbose "Number of matching entries in the CA database: $($MatchingRequestIDCollection.Count)." ForEach ($Entry in $MatchingRequestIDCollection) { # Extract the hexadecimal request ID from "Request ID: 0xb (11)" => "0xb" $ReqIDHex = $Entry.Line -Replace '(\s*Request\sID\:\s)(0x[0-9a-f]+)(.*)', '$2' Try { $IDDec = [System.Convert]::ToInt64($ReqIDHex, 16) } Catch { Write-Warning "Unable to parse the request ID from log entry '$($Entry.Line.Trim())'." $EntryFailedCount++ Continue } If ($Delete -and $PSCmdlet.ShouldProcess("Request ID $IDDec", 'Delete CA database record')) { Write-Verbose "Executing command: certutil.exe -deleterow $ReqIDHex (Request ID $IDDec)." & certutil.exe -deleterow $ReqIDHex | Out-Null If ($LASTEXITCODE -eq 0) { $EntryDeletedCount++ } Else { Write-Warning "Failed to delete Request ID $IDDec (certutil.exe exit code $LASTEXITCODE)." $EntryFailedCount++ } } } If ($Delete) { Write-Verbose "Number of deleted records: $EntryDeletedCount." } } # Output summary for this state [PSCustomObject]@{ State = $CurrentState EntriesMatched = $MatchingRequestIDCollection.Count EntriesDeleted = $EntryDeletedCount EntriesFailed = $EntryFailedCount LogFile = $CertLogFilePath CsvFile = If ($Csv) { $CsvLogFilePath } Else { $Null } } } If ($CompactDatabase) { # Identify CA database location Write-Verbose 'Identifying the Certificate Services database location...' Try { $Config = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration' -ErrorAction Stop $DbFolder = $Config.DBDirectory $DbName = $Config.Active $DbPath = Join-Path -Path $DbFolder -ChildPath "$DbName.edb" } Catch { Write-Warning "Failed to identify the Certificate Services database location. $_" Return } Write-Verbose "The Certificate Services database location is `"$DbPath`"." If (-Not (Test-Path -LiteralPath $DbPath)) { Write-Warning "The Certificate Services database file `"$DbPath`" was not found. Database compaction will not be performed." Return } # Database compaction requires free space at least equal to the database size on the database volume for the temporary defragmented copy Try { $DbSize = (Get-Item -LiteralPath $DbPath -ErrorAction Stop).Length $DbVolume = [System.IO.Path]::GetPathRoot($DbFolder) $Drive = New-Object -TypeName System.IO.DriveInfo -ArgumentList $DbVolume If ($Drive.AvailableFreeSpace -lt $DbSize) { Write-Warning "Insufficient free space on volume $DbVolume to compact the database. Required: $([Math]::Round($DbSize / 1GB, 2)) GB. Available: $([Math]::Round($Drive.AvailableFreeSpace / 1GB, 2)) GB. Database compaction will not be performed." Return } } Catch { Write-Warning "Unable to verify free disk space on the database volume. $_" } If ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Stop the Certificate Services service and compact the CA database')) { # Stop certificate services service Write-Verbose 'Stopping the Certificate Services service...' Try { Stop-Service -Name CertSvc -ErrorAction Stop } Catch { Write-Warning "Failed to stop the Certificate Services service. $_" Return } Try { # Perform integrity check on the certificate services database. The check runs from the log file folder so the integrity check log file is created in a known location $IntegrityLogFile = Join-Path -Path $LogFilePath -ChildPath "$DbName.INTEG.RAW" Write-Verbose 'Performing integrity check on the Certificate Services database...' Push-Location -LiteralPath $LogFilePath Try { & esentutl.exe /g $DbPath | Out-Host } Finally { Pop-Location } If ($LASTEXITCODE -ne 0) { Write-Warning "Database integrity check failed with exit code $LASTEXITCODE. Database compaction will not be performed." Write-Warning "Review the integrity check log file located at '$IntegrityLogFile' for more information." Return } Write-Verbose 'Database integrity check passed.' Write-Verbose "Removing integrity check log file located at '$IntegrityLogFile'..." Remove-Item -LiteralPath $IntegrityLogFile -ErrorAction SilentlyContinue # Compact certificate services database. Compaction runs from the database folder so the temporary defragmented database is created on the database volume Write-Verbose 'Compacting the Certificate Services database...' Push-Location -LiteralPath $DbFolder Try { & esentutl.exe /d $DbPath | Out-Host } Finally { Pop-Location } If ($LASTEXITCODE -ne 0) { Write-Warning "Database compaction failed with exit code $LASTEXITCODE." } } Finally { # Always restart the certificate services service, even if the integrity check or compaction failed Write-Verbose 'Starting the Certificate Services service...' Try { Start-Service -Name CertSvc -ErrorAction Stop } Catch { Write-Warning "Failed to start the Certificate Services service. $_" } } } } } # SIG # Begin signature block # MIIk7AYJKoZIhvcNAQcCoIIk3TCCJNkCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAeg9CpzO33ybiU # N6ivtuaOOrOMb4B/3ljqlWymCVgRjaCCH6YwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggW0MIIDnKADAgECAhAOxitIKuZQm69NGxw+uiH/MA0GCSqG # SIb3DQEBDAUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j # LjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNB # NDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjYwNTE2MDAwMDAwWhcNMjcwODE3MjM1 # OTU5WjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNV # BAcTDU1pc3Npb24gVmllam8xJDAiBgNVBAoTG1JpY2hhcmQgTS4gSGlja3MgQ29u # c3VsdGluZzEkMCIGA1UEAxMbUmljaGFyZCBNLiBIaWNrcyBDb25zdWx0aW5nMFkw # EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOooTPiege6mCA4AriPO+Xh3mymiiZ+3k # kn31uJifB2ojzzfY7VkAVKhgj+rcVBnofnj2b8OhvAJ4YaQ2Iwuc6aOCAgMwggH/ # MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBQJvGhl # Ahwi6UKROatrFKBmPLmd5TA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUF # BwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeA # MBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6 # Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5n # UlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEz # ODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0 # dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2Vy # dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0 # MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkqhkiG9w0BAQwFAAOC # AgEAbaKnnRcJAMHjuWSc2PG/QhJ0jj4hQVwJIbddYDJNxPmD0cxuuorSiR9gX2nl # ajqNI9N7Kl+FB3oheRTGh/wp4JgZMpCq0qS0zGJ/N6Js+HmVtbkFaPyYxJMXbIWq # p9zKkoXtSXkpR6nGZnzYkn3EBcRlu4R6hIJHzM/C2PUztH/Hd4fGIryyD69iHvKx # zotYdlHHY6+X1ACaQnuCz3TLxs3/CDKhPUXesKcISnXHmm4uCwyVdtGyl7wPuZVk # +rfCIOeWn+XG5J7L8xwhXCPSJ5fKJ5m8/H5cICLR0I7hI4SUiybE1nG5CZ1hKhbW # abSfNer1dHH/vSYi80YGXCej/88vZeCGQ9/rrjugsg0yN7WCPqNKjEMTYGWkrt37 # lp4cJqULS+alUbL6x1HBdoBStDE2CFmPivL7cCCtnudqCA6b3XB416/FlRo8t4Lw # Dc2ty+RDKirWM84Zj3ANTVs5fi43rxClBQwngGdqi5TjriKHGTkEKYRIFTViy6Ie # JDIboOkCFJU5vM7Curvh4rQnw+aM4CyjwnDwnzwcKQVZC3Iy1T4h/FvmpSgu5ouM # wjdzaR3cSh4OPDRrfBl1YIOoZEOHcshCaHDC46t8+UyAf70BMlrB7Nj84ORTuKTi # IlU062VzGeREc1KHJqp/S3/NtArpVUVQEgibRxQ99KJCOV8wggawMIIEmKADAgEC # AhAIrUCyYNKcTJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0Mjkw # MDAwMDBaFw0zNjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5E # aWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2Rl # IFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYw # n6SOaNhc9es0JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43i # CH00fUyAVxJrQ5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1 # hz1RGeiQIXhFLqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd # 6BgTZcV/sk+FLEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObar # YBLj6Na59zHh3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18eb # MlrC/2pgVItJwZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYo # X7BzzosmJQayg9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDz # d5Ea/ttQokbIYViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8S # kXbev1jLchApQfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZ # YIpkVMHMIRroOBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxW # EQIDAQABo4IBWTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg # 67Y7+F8Rhvv+YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4c # D08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUF # BwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEG # CCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRU # cnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5k # aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTAT # MAcGBWeBDAEDMAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6P # vDqZ01bgAhql+Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V # 1T9J9Ce7FoFFUP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+ # 3NiAGhEZGM1hmYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcn # P/2Q0XaG3RywYFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgU # kpn13c5UbdldAhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6Q # B7BDf5WIIIJw8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3 # kuZOX956rEnPLqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKL # QcBIhEuWTatEQOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47v # tevLt/B3E+bnKD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0 # qFEgu60bhQjiWQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0 # YW6/aOImYIbqyK+p/pQd52MbOoZWeE4wgga0MIIEnKADAgECAhANx6xXBf8hmS5A # QyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE # aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMT # GERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0zODAx # MTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j # LjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNB # NDA5NiBTSEEyNTYgMjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK # AoICAQC0eDHTCphBcr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGPNRcy # bEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1IpYzT # qpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5AvftB # dsOECS1UkxBvMgEdgkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3 # mmdglTcaarps0wjUjsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBURJg6z # MUjZa/zbCclF83bRVFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS # 5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQskBB # BnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqL # XvJWnY0v5ydPpOjL6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7ps # NOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6BaaeE # WvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCC # AVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQVvYv # 1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/ # BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggr # BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVo # dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0 # LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v # RGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjAL # BglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9vgvI # tTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwbSI/m # S83afsl3YTj+IQhQE7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgX # f9r5nWMQwr8Myb9rEVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD8liy # rukZ2iA/wdG2th9y1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVko43+ # Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRaPs+2 # ykgcGV00TYr2Lr3ty9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8cipD # oq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRzW6Ax # nJll38F0cuJG7uEBYTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAl # Z66RzIg9sC+NJpud/v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1 # MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZs # q8WhbaM2tszWkPZPubdcMIIG7TCCBNWgAwIBAgIQCoDvGEuN8QWC0cR2p5V0aDAN # BgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs # IEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5n # IFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0ExMB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkw # MzIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu # MTswOQYDVQQDEzJEaWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVz # cG9uZGVyIDIwMjUgMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANBG # rC0Sxp7Q6q5gVrMrV7pvUf+GcAoB38o3zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwB # SOeLpvPnZ8ZN+vo8dE2/pPvOx/Vj8TchTySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/ # 4QhguSssp3qome7MrxVyfQO9sMx6ZAWjFDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3 # K3E0zz09ldQ//nBZZREr4h/GI6Dxb2UoyrN0ijtUDVHRXdmncOOMA3CoB/iUSROU # INDT98oksouTMYFOnHoRh6+86Ltc5zjPKHW5KqCvpSduSwhwUmotuQhcg9tw2YD3 # w6ySSSu+3qU8DD+nigNJFmt6LAHvH3KSuNLoZLc1Hf2JNMVL4Q1OpbybpMe46Yce # NA0LfNsnqcnpJeItK/DhKbPxTTuGoX7wJNdoRORVbPR1VVnDuSeHVZlc4seAO+6d # 2sC26/PQPdP51ho1zBp+xUIZkpSFA8vWdoUoHLWnqWU3dCCyFG1roSrgHjSHlq8x # ymLnjCbSLZ49kPmk8iyyizNDIXj//cOgrY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+ # AliL7ojTdS5PWPsWeupWs7NpChUk555K096V1hE0yZIXe+giAwW00aHzrDchIc2b # Qhpp0IoKRR7YufAkprxMiXAJQ1XCmnCfgPf8+3mnAgMBAAGjggGVMIIBkTAMBgNV # HRMBAf8EAjAAMB0GA1UdDgQWBBTkO/zyMe39/dfzkXFjGVBDz2GM6DAfBgNVHSME # GDAWgBTvb1NK6eQGfHrK4pBW9i/USezLTjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0l # AQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGFMCQGCCsGAQUFBzAB # hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9j # YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGlu # Z1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBWMFSgUqBQhk5odHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBp # bmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIw # CwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQBlKq3xHCcEua5gQezRCESe # Y0ByIfjk9iJP2zWLpQq1b4URGnwWBdEZD9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FU # FqNh+tshgb4O6Lgjg8K8elC4+oWCqnU/ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7Y # MTmqPO9mzskgiC3QYIUP2S3HQvHG1FDu+WUqW4daIqToXFE/JQ/EABgfZXLWU0zi # TN6R3ygQBHMUBaB5bdrPbF6MRYs03h4obEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/ # QqvXnNb+YkDFkxUGtMTaiLR9wjxUxu2hECZpqyU1d0IbX6Wq8/gVutDojBIFeRlq # AcuEVT0cKsb+zJNEsuEB7O7/cuvTQasnM9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3 # Y50OHgaY7T/lwd6UArb+BOVAkg2oOvol/DJgddJ35XTxfUlQ+8Hggt8l2Yv7roan # cJIFcbojBcxlRcGG0LIhp6GvReQGgMgYxQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ # ndUlQ05oxYy2zRWVFjF7mcr4C34Mj3ocCVccAvlKV9jEnstrniLvUxxVZE/rptb7 # IRE2lskKPIJgbaP5t2nGj/ULLi49xTcBZU8atufk+EMF/cWuiC7POGT75qaL6vdC # vHlshtjdNXOCIUjsarfNZzGCBJwwggSYAgEBMH0waTELMAkGA1UEBhMCVVMxFzAV # BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVk # IEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQDsYrSCrm # UJuvTRscProh/zANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKAC # gAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsx # DjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCCS8LftlKZRzJYVeBRv3dm9 # tFNnTSybbrmZA+naay/JfTALBgcqhkjOPQIBBQAERzBFAiEA7yaiW40QfmxuymAi # BJH9BRrlcdUXLnPB3DQ2+KXoab4CIFnDF4E9DLpWRBfJuuKvYLXSdFGwnvsoLJmR # z2w6SxtXoYIDJjCCAyIGCSqGSIb3DQEJBjGCAxMwggMPAgEBMH0waTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENB # MQIQCoDvGEuN8QWC0cR2p5V0aDANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJ # AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI2MDcyNzA0MDI1NVowLwYJ # KoZIhvcNAQkEMSIEIFHaVeWedWgwZrHX9NF4T8UpLwl3WxSBX1KLO3TpikkZMA0G # CSqGSIb3DQEBAQUABIICAJxVH90qVdM2eumo0w6DCZ7juRZxFt/weULUj3J8NWw2 # oog5hQPvOsPFEcBaeYbAeFcYdo6b6gArKLm3aCaMMYLrlmkifz/Xgykcf5VCMpPl # lEch6MRI4IPWwdzR7RHHkxSzvnHluH4MHOV90Yfswvdf92vhKPmqFr+Y5khkW2Er # ml9i45AZL0hzzbFvAOYRXxp12JAmCEiVzntH/sINJLZVPhjxoIoRwjj4ZoqYafmo # Pp8HqEA8lurHUmvuzpcJBmR0jO/L4q5vbGlQ2YUWGAyzrkAdcKgotlwwy1Ge6B+4 # GSgb+EC6QTi+fjmaHKQTGfq+hG7vZqeJIxA8TTunysTmEqhAg9HoRAlmtLxkAE7k # uMyHMWd5ZV7eQ+YJmx8/BsLVf7k5G1H2Vgxw6PPH7V37zeJhjTIhAupot0KZQJNS # 6iZUk08MvvUfYh10z85TFhN57IqH1omBVoR3/5g8TzXsYCd7Du5Q/w3wJwKRr+tM # BMNcNCu8/O4dkBs0oDNlMAabZHIIcC2W2m5QE80mxIdOM5GElP+C6MMLO59ZCPVg # cgHtFZGLvWxu9CmqpQtmKnO9Pz+edIyquCPRxcZKCNDA89UJQnA5MxUU7kg6+RMN # fYU99d8oMa/YH3PWgC3vKKDQUdwR3rBb5auvjMTbSeQ58Om6QUra8lXijQSxUU0i # SIG # End signature block |