Functions/Remove-ADPrincipalCertificate.ps1

<#
 
.SYNOPSIS
    Remove certificates from an Active Directory principal's userCertificate attribute.
 
.DESCRIPTION
    This function removes certificates from the userCertificate attribute of AD principals (users, computers, service accounts, or other AD objects). By default, self-signed (non-managed) certificates are preserved and only managed certificates are removed. Use the -IncludeSelfSignedCertificate switch to also remove self-signed certificates. This is a destructive operation that permanently deletes certificate data from Active Directory. The script accepts pipeline input from Get-ADUser, Get-ADComputer, Get-ADServiceAccount, or Get-ADObject cmdlets.
 
    WARNING: This operation is irreversible without an Active Directory backup. Ensure you have a current AD backup before running this command.
 
.PARAMETER Identity
    One or more AD principal identities. This can be a Distinguished Name, SamAccountName, ObjectGUID, or SID. This parameter accepts pipeline input and multiple values.
 
.PARAMETER Force
    Bypasses the initial warning prompt. The -Confirm prompt will still be displayed unless -Confirm:$false is also specified.
 
.PARAMETER IncludeSelfSignedCertificate
    When specified, includes self-signed certificates (where Subject equals Issuer) in the removal operation. By default, self-signed certificates are preserved and only managed certificates are removed.
 
.INPUTS
    Microsoft.ActiveDirectory.Management.ADUser, Microsoft.ActiveDirectory.Management.ADComputer, Microsoft.ActiveDirectory.Management.ADServiceAccount, Microsoft.ActiveDirectory.Management.ADObject, or String values.
 
.OUTPUTS
    None.
 
.EXAMPLE
    Remove-ADPrincipalCertificate -Identity 'juser'
 
    Removes managed certificates from user juser after confirmation. Self-signed certificates are preserved.
 
.EXAMPLE
    Get-ADUser juser | Remove-ADPrincipalCertificate
 
    Pipes a user object to remove managed certificates after confirmation. Self-signed certificates are preserved.
 
.EXAMPLE
    Get-ADComputer 'app1' | Remove-ADPrincipalCertificate -WhatIf
 
    Shows what would happen if managed certificates were removed from computer app1 without actually removing them.
 
.EXAMPLE
    Get-ADServiceAccount 'svc_app' | Remove-ADPrincipalCertificate -Confirm:$false -Force
 
    Removes managed certificates from service account svc_app without any prompts (use with caution). Self-signed certificates are preserved.
 
.EXAMPLE
    Get-ADUser -Filter {Department -eq 'Contractors'} | Remove-ADPrincipalCertificate
 
    Removes managed certificates from all users in the Contractors department after confirmation for each.
 
.EXAMPLE
    Remove-ADPrincipalCertificate -Identity 'app1', 'app2' -Force
 
    Removes managed certificates from multiple computers app1 and app2, bypassing the backup warning.
 
.EXAMPLE
    Remove-ADPrincipalCertificate -Identity 'juser' -IncludeSelfSignedCertificate
 
    Removes ALL certificates from user juser, including self-signed certificates.
 
.EXAMPLE
    Get-ADComputer -Filter * | Remove-ADPrincipalCertificate -IncludeSelfSignedCertificate -Force
 
    Removes ALL certificates from all computer accounts, including self-signed certificates.
 
.LINK
    https://github.com/richardhicks/adprincipalcertificate/blob/main/Functions/Remove-ADPrincipalCertificate.ps1
 
.LINK
    https://www.richardhicks.com/
 
.NOTES
    Version: 1.0
    Creation Date: February 7, 2026
    Last Updated: February 7, 2026
    Author: Richard Hicks
    Organization: Richard M. Hicks Consulting, Inc.
    Contact: rich@richardhicks.com
    Website: https://www.richardhicks.com/
 
#>


Function Remove-ADPrincipalCertificate {

    # Prerequisites
    #Requires -Module ActiveDirectory

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([System.Void])]

    Param (

        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0, HelpMessage = 'Specify one or more AD principal identities')]
        [Alias('DistinguishedName', 'SamAccountName', 'ObjectGUID', 'SID', 'Name')]
        [ValidateNotNullOrEmpty()]
        $Identity,

        [Parameter(HelpMessage = 'Bypass the initial backup warning prompt')]
        [switch]$Force,

        [Parameter(HelpMessage = 'Include self-signed certificates in the removal')]
        [switch]$IncludeSelfSignedCertificate

    )

    Begin {

        Write-Verbose 'Begin certificate removal process...'

        # Define required properties once in Begin block for efficiency
        $RequiredProperties = @('userCertificate', 'SamAccountName', 'ObjectClass', 'DistinguishedName')

        # Check if Identity parameter was provided (non-pipeline input)
        # Note: Pipeline input won't be available until Process block
        If (-not $Identity -and -not $MyInvocation.ExpectingInput) {

            Write-Warning 'No input provided. Specify an identity using -Identity or pipe AD objects to this script.'
            $Script:OperationCancelled = $true
            Return

        }

        # Display warning unless -Force is specified
        If (-not $Force -and -not $WhatIfPreference) {

            Write-Warning '*** CRITICAL OPERATION ***'

            If ($IncludeSelfSignedCertificate) {

                Write-Warning 'This operation will permanently remove ALL certificates from the specified AD principal(s).'
                Write-Warning 'This includes self-signed certificates.'

            }

            Else {

                Write-Warning 'This operation will permanently remove managed certificates from the specified AD principal(s).'
                Write-Warning 'Self-signed certificates will be preserved.'

            }

            $Confirmation = Read-Host 'Do you wish to continue? (Yes/No)'

            If ($Confirmation -notmatch '^y(es)?$') {

                Write-Warning 'Operation cancelled by user.'
                $Script:OperationCancelled = $true
                Return

            }

            Write-Verbose 'Confirmation prompt accepted by user. Proceeding with operation...'

        }

        $Script:OperationCancelled = $false

        # Helper function to convert certificate data to a consistent list format
        Function Get-CertificateList {

            Param (
                [Parameter(Mandatory)]
                $CertificateData
            )

            $UserCertType = $CertificateData.GetType()
            Write-Verbose "userCertificate type: $($UserCertType.FullName)"

            If ($UserCertType.FullName -eq 'System.Byte[]') {

                # Single certificate stored as byte array
                Return , @(, $CertificateData)

            }

            ElseIf ($UserCertType.FullName -eq 'System.Byte[][]') {

                # Multiple certificates stored as array of byte arrays
                Return , $CertificateData

            }

            ElseIf ($UserCertType.FullName -like '*ADPropertyValueCollection*') {

                # ADPropertyValueCollection - use .Value property to get the raw data
                $RawValue = $CertificateData.Value

                If ($RawValue -is [byte[]]) {

                    Return , @(, $RawValue)

                }

                ElseIf ($RawValue -is [System.Object[]]) {

                    Return , $RawValue

                }

                Else {

                    # Fallback: convert collection to array
                    Return , @($CertificateData)

                }

            }

            Else {

                # Unknown type - wrap in array
                Write-Verbose "Unknown userCertificate type: $($UserCertType.FullName). Attempting to process..."
                Return , @(, $CertificateData)

            }

        }

        # Helper function to check if AD object has all required properties
        Function Test-HasRequiredProperty {

            Param (
                [Parameter(Mandatory)]
                $AdObject,
                [Parameter(Mandatory)]
                [string[]]$Properties
            )

            ForEach ($Prop in $Properties) {

                If (-not $AdObject.PSObject.Properties.Name.Contains($Prop)) {

                    Return $false

                }

            }

            Return $true

        }

    }

    Process {

        # Exit if operation was cancelled in Begin block
        If ($Script:OperationCancelled) {

            Return

        }

        # Handle both single and array inputs correctly
        $IdsToProcess = If ($Identity -is [array]) { $Identity } Else { , $Identity }

        ForEach ($Id in $IdsToProcess) {

            Try {

                $Principal = $null

                # Check if this is already an AD object with required properties
                If ($Id.PSObject.TypeNames -match 'Microsoft\.ActiveDirectory\.Management\.AD(User|Computer|ServiceAccount|Object)') {

                    # Check if the piped object already has all required properties
                    If ((Test-HasRequiredProperty -AdObject $Id -Properties $RequiredProperties) -and $null -ne $Id.userCertificate) {

                        Write-Verbose "Using piped AD object with existing properties: $($Id.DistinguishedName)"
                        $Principal = $Id

                    }

                    Else {

                        Write-Verbose "Re-querying AD object for certificate data: $($Id.DistinguishedName)"
                        $IdentityValue = $Id.DistinguishedName
                        $ObjectClass = $Id.ObjectClass

                    }

                }

                Else {

                    Write-Verbose "Retrieving AD principal `"$Id`"..."
                    $IdentityValue = $Id
                    $ObjectClass = $null

                }

                # Query AD if we don't already have the complete object
                If (-not $Principal) {

                    Switch ($ObjectClass) {

                        'user' {

                            $Principal = Get-ADUser -Identity $IdentityValue -Properties $RequiredProperties -ErrorAction Stop

                        }

                        'computer' {

                            $Principal = Get-ADComputer -Identity $IdentityValue -Properties $RequiredProperties -ErrorAction Stop

                        }

                        { $_ -in 'msDS-ManagedServiceAccount', 'msDS-GroupManagedServiceAccount' } {

                            $Principal = Get-ADServiceAccount -Identity $IdentityValue -Properties $RequiredProperties -ErrorAction Stop

                        }

                        Default {

                            # Try to find the object using cascading approach
                            Try {

                                $Principal = Get-ADUser -Identity $IdentityValue -Properties $RequiredProperties -ErrorAction Stop
                                Write-Verbose "Found user account `"$IdentityValue`"."

                            }

                            Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {

                                Try {

                                    $Principal = Get-ADComputer -Identity $IdentityValue -Properties $RequiredProperties -ErrorAction Stop
                                    Write-Verbose "Found computer account `"$IdentityValue`"."

                                }

                                Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {

                                    Try {

                                        $Principal = Get-ADServiceAccount -Identity $IdentityValue -Properties $RequiredProperties -ErrorAction Stop
                                        Write-Verbose "Found service account `"$IdentityValue`"."

                                    }

                                    Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {

                                        $Principal = Get-ADObject -Identity $IdentityValue -Properties $RequiredProperties -ErrorAction Stop
                                        Write-Verbose "Found AD object `"$IdentityValue`"."

                                    }

                                }

                            }

                        }

                    }

                }

                # Validate certificate presence
                If (-not $Principal.userCertificate) {

                    Write-Warning "No certificates found for principal `"$($Principal.SamAccountName)`"."
                    Continue

                }

                # Get certificate list using helper function
                $CertificateList = Get-CertificateList -CertificateData $Principal.userCertificate

                $TotalCertificates = $CertificateList.Count
                Write-Verbose "Processing $TotalCertificates certificate(s) for $($Principal.SamAccountName)."

                # Separate non-managed certificates (self-signed) from managed certificates
                # Use Generic List for better performance with large datasets
                $CertificatesToRemove = [System.Collections.Generic.List[byte[]]]::new()
                $SelfSignedCount = 0

                # Use for loop with explicit indexing to prevent byte array unrolling
                For ($i = 0; $i -lt $CertificateList.Count; $i++) {

                    $CertBytes = $CertificateList[$i]
                    $Certificate = $null

                    Try {

                        $Certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertBytes)

                        # Check if certificate is self-signed (Subject equals Issuer)
                        If ($Certificate.Subject -eq $Certificate.Issuer) {

                            # If IncludeSelfSignedCertificate is specified, include self-signed certificates in removal
                            If ($IncludeSelfSignedCertificate) {

                                $CertificatesToRemove.Add($CertBytes)
                                Write-Verbose "Marking self-signed certificate for removal: $($Certificate.Subject) (Thumbprint: $($Certificate.Thumbprint))"

                            }

                            Else {

                                # By default, preserve self-signed certificates
                                $SelfSignedCount++
                                Write-Verbose "Preserving self-signed certificate: $($Certificate.Subject) (Thumbprint: $($Certificate.Thumbprint))"

                            }

                        }

                        Else {

                            $CertificatesToRemove.Add($CertBytes)
                            Write-Verbose "Marking managed certificate for removal: $($Certificate.Subject) (Issuer: $($Certificate.Issuer))"

                        }

                    }

                    Catch {

                        Write-Warning "Unable to parse certificate for '$($Principal.SamAccountName)'. Skipping certificate to be safe: $_"
                        $SelfSignedCount++

                    }

                    Finally {

                        # Dispose of certificate object to prevent memory leaks
                        If ($null -ne $Certificate) {

                            $Certificate.Dispose()

                        }

                    }

                }

                # Check if there are any certificates to remove
                If ($CertificatesToRemove.Count -eq 0) {

                    If (-not $IncludeSelfSignedCertificate) {

                        Write-Warning "No certificates matching the specified criteria were found for principal `"$($Principal.SamAccountName)`"."

                    }

                    Else {

                        Write-Warning "No certificates found for principal `"$($Principal.SamAccountName)`"."

                    }

                    Continue

                }

                $RemoveCount = $CertificatesToRemove.Count
                $PreserveCount = $SelfSignedCount

                Write-Verbose "Certificates to remove: $RemoveCount | Self-signed certificates to preserve: $PreserveCount"

                # Perform the removal with ShouldProcess support
                If (-not $IncludeSelfSignedCertificate -and $PreserveCount -gt 0) {

                    $ConfirmMessage = "Remove $RemoveCount managed certificate(s) from '$($Principal.SamAccountName)' ($($Principal.ObjectClass))? ($PreserveCount self-signed certificate(s) will be preserved)"
                    $WhatIfMessage = "Removing $RemoveCount managed certificate(s) from '$($Principal.SamAccountName)' ($($Principal.ObjectClass)). Preserving $PreserveCount self-signed certificate(s)"

                }

                Else {

                    $ConfirmMessage = "Remove $RemoveCount certificate(s) from '$($Principal.SamAccountName)' ($($Principal.ObjectClass))?"
                    $WhatIfMessage = "Removing $RemoveCount certificate(s) from '$($Principal.SamAccountName)' ($($Principal.ObjectClass))"

                }

                If ($PSCmdlet.ShouldProcess($WhatIfMessage, $ConfirmMessage, 'Remove AD Principal Certificate(s)')) {

                    Try {

                        # Remove certificates in a single AD operation for better performance
                        # Build hashtable with array of certificates to remove
                        $CertsToRemoveArray = $CertificatesToRemove.ToArray()
                        Set-ADObject -Identity $Principal.DistinguishedName -Remove @{ userCertificate = $CertsToRemoveArray } -ErrorAction Stop

                        Write-Verbose "Successfully removed $RemoveCount certificate(s) from '$($Principal.SamAccountName)'."

                        # Output success message
                        If ($PreserveCount -gt 0) {

                            Write-Output "Removed $RemoveCount certificate(s) from '$($Principal.SamAccountName)'. Preserved $PreserveCount self-signed certificate(s)."

                        }

                        Else {

                            Write-Output "Removed $RemoveCount certificate(s) from principal '$($Principal.SamAccountName)'."

                        }

                    }

                    Catch {

                        Write-Error "Failed to remove certificates from '$($Principal.SamAccountName)': $_"

                    }

                }

            }

            Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {

                Write-Warning "AD principal not found: $Id"

            }

            Catch {

                Write-Warning "Error processing principal '$Id': $_"

            }

        }

    }

    End {

        If (-not $Script:OperationCancelled) {

            Write-Verbose 'Certificate removal process completed.'

        }

    }

}

# SIG # Begin signature block
# MIIf2wYJKoZIhvcNAQcCoIIfzDCCH8gCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB3h77jHMu+X36M
# m3mreCTF3YSfN8emEyLuxWemiLDERKCCGpkwggNZMIIC36ADAgECAhAPuKdAuRWN
# A1FDvFnZ8EApMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE
# aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMT
# F0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMB4XDTIxMDQyOTAwMDAwMFoXDTM2MDQy
# ODIzNTk1OVowZDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MTwwOgYDVQQDEzNEaWdpQ2VydCBHbG9iYWwgRzMgQ29kZSBTaWduaW5nIEVDQyBT
# SEEzODQgMjAyMSBDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAS7tKwnpUgNolNf
# jy6BPi9TdrgIlKKaqoqLmLWx8PwqFbu5s6UiL/1qwL3iVWhga5c0wWZTcSP8GtXK
# IA8CQKKjSlpGo5FTK5XyA+mrptOHdi/nZJ+eNVH8w2M1eHbk+HejggFXMIIBUzAS
# BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSbX7A2up0GrhknvcCgIsCLizh3
# 7TAfBgNVHSMEGDAWgBSz20ik+aHF2K42QcwRY2liKbxLxjAOBgNVHQ8BAf8EBAMC
# AYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdgYIKwYBBQUHAQEEajBoMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQAYIKwYBBQUHMAKGNGh0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMy5jcnQw
# QgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0R2xvYmFsUm9vdEczLmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEE
# ATAKBggqhkjOPQQDAwNoADBlAjB4vUmVZXEB0EZXaGUOaKncNgjB7v3UjttAZT8N
# /5Ovwq5jhqN+y7SRWnjsBwNnB3wCMQDnnx/xB1usNMY4vLWlUM7m6jh+PnmQ5KRb
# qwIN6Af8VqZait2zULLd8vpmdJ7QFmMwggP+MIIDhKADAgECAhANSjTahpCPwBMs
# vIE3k68kMAoGCCqGSM49BAMDMGQxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp
# Q2VydCwgSW5jLjE8MDoGA1UEAxMzRGlnaUNlcnQgR2xvYmFsIEczIENvZGUgU2ln
# bmluZyBFQ0MgU0hBMzg0IDIwMjEgQ0ExMB4XDTI0MTIwNjAwMDAwMFoXDTI3MTIy
# NDIzNTk1OVowgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw
# FAYDVQQHEw1NaXNzaW9uIFZpZWpvMSQwIgYDVQQKExtSaWNoYXJkIE0uIEhpY2tz
# IENvbnN1bHRpbmcxJDAiBgNVBAMTG1JpY2hhcmQgTS4gSGlja3MgQ29uc3VsdGlu
# ZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFCbtcqpc7vGGM4hVM79U+7f0tKz
# o8BAGMJ/0E7JUwKJfyMJj9jsCNpp61+mBNdTwirEm/K0Vz02vak0Ftcb/3yjggHz
# MIIB7zAfBgNVHSMEGDAWgBSbX7A2up0GrhknvcCgIsCLizh37TAdBgNVHQ4EFgQU
# KIMkVkfISNUyQJ7bwvLm9sCIkxgwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggr
# BgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB/wQE
# AwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBqwYDVR0fBIGjMIGgME6gTKBKhkho
# dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxHM0NvZGVTaWdu
# aW5nRUNDU0hBMzg0MjAyMUNBMS5jcmwwTqBMoEqGSGh0dHA6Ly9jcmw0LmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbEczQ29kZVNpZ25pbmdFQ0NTSEEzODQyMDIx
# Q0ExLmNybDCBjgYIKwYBBQUHAQEEgYEwfzAkBggrBgEFBQcwAYYYaHR0cDovL29j
# c3AuZGlnaWNlcnQuY29tMFcGCCsGAQUFBzAChktodHRwOi8vY2FjZXJ0cy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRHbG9iYWxHM0NvZGVTaWduaW5nRUNDU0hBMzg0MjAy
# MUNBMS5jcnQwCQYDVR0TBAIwADAKBggqhkjOPQQDAwNoADBlAjBMOsBb80qx6E6S
# 2lnnHafuyY2paoDtPjcfddKaB1HKnAy7WLaEVc78xAC84iW3l6ECMQDhOPD5JHtw
# YxEH6DxVDle5pLKfuyQHiY1i0I9PrSn1plPUeZDTnYKmms1P66nBvCkwggWNMIIE
# daADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNV
# BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp
# Y2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAe
# Fw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUw
# EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
# ITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcN
# AQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC
# 4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWl
# fr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1j
# KS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dP
# pzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3
# pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJ
# pMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aa
# dMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXD
# j/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB
# 4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ
# 33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amy
# HeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC
# 0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823I
# DzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYD
# VR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcN
# AQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxpp
# VCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6
# mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPH
# h6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCN
# NWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg6
# 2fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQwgga0MIIEnKADAgECAhANx6xXBf8h
# mS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0z
# ODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcg
# UlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGP
# NRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1I
# pYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5A
# vftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDRe
# b6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBUR
# Jg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/ao
# fEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQ
# skBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJ
# lIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev
# +7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6B
# aaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IB
# XTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQ
# VvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P
# AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC
# hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v
# dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEE
# AjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9
# vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwb
# SI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTL
# xLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD
# 8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVk
# o43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRa
# Ps+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8
# cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRz
# W6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KC
# LPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau
# 1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPS
# xyZsq8WhbaM2tszWkPZPubdcMIIG7TCCBNWgAwIBAgIQCoDvGEuN8QWC0cR2p5V0
# aDANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
# cnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1w
# aW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0ExMB4XDTI1MDYwNDAwMDAwMFoXDTM2
# MDkwMzIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMTswOQYDVQQDEzJEaWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBUaW1lc3RhbXAg
# UmVzcG9uZGVyIDIwMjUgMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# ANBGrC0Sxp7Q6q5gVrMrV7pvUf+GcAoB38o3zBlCMGMyqJnfFNZx+wvA69HFTBdw
# bHwBSOeLpvPnZ8ZN+vo8dE2/pPvOx/Vj8TchTySA2R4QKpVD7dvNZh6wW2R6kSu9
# RJt/4QhguSssp3qome7MrxVyfQO9sMx6ZAWjFDYOzDi8SOhPUWlLnh00Cll8pjrU
# cCV3K3E0zz09ldQ//nBZZREr4h/GI6Dxb2UoyrN0ijtUDVHRXdmncOOMA3CoB/iU
# SROUINDT98oksouTMYFOnHoRh6+86Ltc5zjPKHW5KqCvpSduSwhwUmotuQhcg9tw
# 2YD3w6ySSSu+3qU8DD+nigNJFmt6LAHvH3KSuNLoZLc1Hf2JNMVL4Q1OpbybpMe4
# 6YceNA0LfNsnqcnpJeItK/DhKbPxTTuGoX7wJNdoRORVbPR1VVnDuSeHVZlc4seA
# O+6d2sC26/PQPdP51ho1zBp+xUIZkpSFA8vWdoUoHLWnqWU3dCCyFG1roSrgHjSH
# lq8xymLnjCbSLZ49kPmk8iyyizNDIXj//cOgrY7rlRyTlaCCfw7aSUROwnu7zER6
# EaJ+AliL7ojTdS5PWPsWeupWs7NpChUk555K096V1hE0yZIXe+giAwW00aHzrDch
# Ic2bQhpp0IoKRR7YufAkprxMiXAJQ1XCmnCfgPf8+3mnAgMBAAGjggGVMIIBkTAM
# BgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTkO/zyMe39/dfzkXFjGVBDz2GM6DAfBgNV
# HSMEGDAWgBTvb1NK6eQGfHrK4pBW9i/USezLTjAOBgNVHQ8BAf8EBAMCB4AwFgYD
# VR0lAQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGFMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXQYIKwYBBQUHMAKGUWh0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFt
# cGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBWMFSgUqBQhk5o
# dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3Rh
# bXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkwFzAIBgZngQwB
# BAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQBlKq3xHCcEua5gQezR
# CESeY0ByIfjk9iJP2zWLpQq1b4URGnwWBdEZD9gBq9fNaNmFj6Eh8/YmRDfxT7C0
# k8FUFqNh+tshgb4O6Lgjg8K8elC4+oWCqnU/ML9lFfim8/9yJmZSe2F8AQ/UdKFO
# tj7YMTmqPO9mzskgiC3QYIUP2S3HQvHG1FDu+WUqW4daIqToXFE/JQ/EABgfZXLW
# U0ziTN6R3ygQBHMUBaB5bdrPbF6MRYs03h4obEMnxYOX8VBRKe1uNnzQVTeLni2n
# HkX/QqvXnNb+YkDFkxUGtMTaiLR9wjxUxu2hECZpqyU1d0IbX6Wq8/gVutDojBIF
# eRlqAcuEVT0cKsb+zJNEsuEB7O7/cuvTQasnM9AWcIQfVjnzrvwiCZ85EE8LUkqR
# hoS3Y50OHgaY7T/lwd6UArb+BOVAkg2oOvol/DJgddJ35XTxfUlQ+8Hggt8l2Yv7
# roancJIFcbojBcxlRcGG0LIhp6GvReQGgMgYxQbV1S3CrWqZzBt1R9xJgKf47Cdx
# VRd/ndUlQ05oxYy2zRWVFjF7mcr4C34Mj3ocCVccAvlKV9jEnstrniLvUxxVZE/r
# ptb7IRE2lskKPIJgbaP5t2nGj/ULLi49xTcBZU8atufk+EMF/cWuiC7POGT75qaL
# 6vdCvHlshtjdNXOCIUjsarfNZzGCBJgwggSUAgEBMHgwZDELMAkGA1UEBhMCVVMx
# FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTwwOgYDVQQDEzNEaWdpQ2VydCBHbG9i
# YWwgRzMgQ29kZSBTaWduaW5nIEVDQyBTSEEzODQgMjAyMSBDQTECEA1KNNqGkI/A
# Eyy8gTeTryQwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgk54zV9m58M+ArqiKl3CMTaB1
# NaqgdAApqgDtDYioc+cwCwYHKoZIzj0CAQUABEgwRgIhANjyHtN+SHL0KnemjrFR
# mD77mfhGVRUVeiQ+n0Y+LR2BAiEAjIZdADMpCSbDrGnXb9HX4mNg/RTLjENFtmHs
# Gi1l1PChggMmMIIDIgYJKoZIhvcNAQkGMYIDEzCCAw8CAQEwfTBpMQswCQYDVQQG
# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0
# IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex
# AhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcNAQkD
# MQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjYwMjA3MjEzNTUwWjAvBgkq
# hkiG9w0BCQQxIgQgpLXfUOUpz/CWmyQq8Tsr+p1NkWseDx6pXkavvUGxDSowDQYJ
# KoZIhvcNAQEBBQAEggIAGFb61p6z5gSSzBwEPL8EBs15n72/DO+i6L9SWW1g/GEG
# zetVAvLfqjVlJFUeA9QRbPsqRiFu+Gcjt7zNHR3RIMx+2wrXZr1kYwNw/wmt46qn
# 6JgyM79/XUgyjbPAiZu3a8lCPPEVLVHBxMacE44EObw05sqiUc/C3GO80fxCfAOm
# evhQhn+eTmgvaHjgSG31DqT1On8fZzByvMLxOmHf7QIsK+a5Ytz+4d5WjBGgtwBJ
# swfXdGpcmwCiqHdLQ4tFruqDcSmas7gUkKBwpgSdcFpZDh0v+oslzcW6P/4NseeT
# k+TwTEruCTIpjcJ1y+Vk25YnRyBrKtopjWvfCRtJl7xEDe3Cv4PX6hM6zStHLWr9
# bC2v0aTZEcJYeY/UHcZE6KRgWLXcaj13zAQl6VuVEeLO0gTMO8NaBQNo6jclIhIg
# X+kOxPp579pUp6dCTdporfNOJbtMPiTu8GdsrE2LYi1ssarTqNdDJ2oGntl5xFmm
# uwrYqYfDXo1Eq5cuTbzxyMDXGJV4we/qW3c/7mwhUB6/vUHXKh/wgaNVB4GpRMDr
# mH/hJZFonjdoCrApS0chOWrHUjrI02rc2PxEupkMoJxzB2i2Xrlp4SnuKPqfRWzR
# dDk0dDqNJ3n6frYChr8b4VUtURpWNPepPur4Z4Em48CAw02OW6cui5f6g0RNWtA=
# SIG # End signature block