Functions/Get-ADPrincipalCertificate.ps1

<#
 
.SYNOPSIS
    Display certificate details for Active Directory principals.
 
.DESCRIPTION
    This function retrieves and displays detailed certificate information for AD principals (users, computers, service accounts, or other AD objects) that have certificates attached. The script accepts pipeline input from Get-ADUser, Get-ADComputer, Get-ADServiceAccount, or Get-ADObject cmdlets.
 
.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 OutFile
    When specified, exports the certificate(s) to file(s). Files are named using the AccountName with a .crt extension. Multiple certificates are numbered sequentially (e.g., AccountName_001.crt, AccountName_002.crt).
 
.PARAMETER OutCsv
    Specifies the path to a CSV file where certificate information will be exported. The CSV file will contain all certificate details for the queried principals.
 
.PARAMETER GenerateReport
    Specifies the path to an HTML file where a formatted certificate report will be generated. The report includes a styled table with all certificate details and summary statistics.
 
.PARAMETER ExcludeSelfSignedCertificate
    When specified, excludes self-signed certificates (where Subject equals Issuer) from the output.
 
.INPUTS
    Microsoft.ActiveDirectory.Management.ADUser, Microsoft.ActiveDirectory.Management.ADComputer, Microsoft.ActiveDirectory.Management.ADServiceAccount, Microsoft.ActiveDirectory.Management.ADObject, or String values.
 
.OUTPUTS
    Custom objects containing certificate details including AccountName, AccountType, Subject, Issuer, Thumbprint, Issued, Expires, EKU (Enhanced Key Usage/Application Policies), SerialNumber, KeyType, KeyLength, and SignatureAlgorithm.
 
.EXAMPLE
    Get-ADPrincipalCertificate -Identity 'juser'
 
    Displays certificate details for user juser.
 
.EXAMPLE
    Get-ADUser juser | Get-ADPrincipalCertificate
 
    Pipes a user object to display certificate details.
 
.EXAMPLE
    Get-ADComputer 'app1' | Get-ADPrincipalCertificate
 
    Pipes a computer object to display certificate details.
 
.EXAMPLE
    Get-ADServiceAccount 'svc_app' | Get-ADPrincipalCertificate
 
    Pipes a service account object to display certificate details.
 
.EXAMPLE
    Get-ADUser -Filter {Department -eq 'Human Resources'} | Get-ADPrincipalCertificate
 
    Displays certificate details for all users in the Human Resources department.
 
.EXAMPLE
    Get-ADServiceAccount -Filter * | Get-ADPrincipalCertificate
 
    Displays certificate details for all service accounts that have certificates.
 
.EXAMPLE
    Get-ADPrincipalCertificate -Identity 'juser' -OutFile
 
    Displays certificate details for user juser and exports the certificate(s) to file(s).
 
.EXAMPLE
    Get-ADUser juser | Get-ADPrincipalCertificate -OutFile
 
    Pipes a user object, displays certificate details, and exports to juser.crt (or juser_001.crt, juser_002.crt for multiple certificates).
 
.EXAMPLE
    Get-ADPrincipalCertificate -Identity 'juser' -OutCsv 'C:\Temp\certificates.csv'
 
    Displays certificate details for user juser and exports the information to a CSV file.
 
.EXAMPLE
    Get-ADUser -Filter {Department -eq 'Human Resources'} | Get-ADPrincipalCertificate -OutCsv 'C:\Temp\hr_certificates.csv'
 
    Exports certificate details for all users in the Human Resources department to a CSV file.
 
.EXAMPLE
    Get-ADPrincipalCertificate -Identity 'juser' -GenerateReport 'C:\Temp\certificates.html'
 
    Displays certificate details for user juser and generates an HTML report.
 
.EXAMPLE
    Get-ADUser -Filter * | Get-ADPrincipalCertificate -GenerateReport 'C:\Temp\all_certificates.html'
 
    Generates an HTML report containing certificate details for all users in Active Directory.
 
.EXAMPLE
    Get-ADPrincipalCertificate -Identity 'app1', 'app2'
 
    Displays certificate details for multiple computers app1 and app2.
 
.EXAMPLE
    Get-Content -Path .\users.txt | Get-ADPrincipalCertificate
 
    Reads principal identities from a text file (one per line) and displays certificate details for each.
 
.EXAMPLE
    Get-ADPrincipalCertificate -Identity 'juser' -ExcludeSelfSignedCertificate
 
    Displays certificate details for user juser, excluding any self-signed certificates.
 
.EXAMPLE
    Get-ADComputer -Filter * | Get-ADPrincipalCertificate -ExcludeSelfSignedCertificate
 
    Displays certificate details for all computer accounts, excluding self-signed certificates.
 
.LINK
    https://github.com/richardhicks/adprincipalcertificate/blob/main/Functions/Get-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 Get-ADPrincipalCertificate {

    # Prerequisites
    #Requires -Module ActiveDirectory

    [CmdletBinding()]

    Param (

        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0, HelpMessage = 'Specify one or more AD principal identities')]
        [Alias('DistinguishedName', 'SamAccountName', 'ObjectGUID', 'SID', 'Name')]
        [ValidateNotNullOrEmpty()]
        $Identity,
        [switch]$OutFile,
        [Parameter(HelpMessage = 'Specify a path to export certificate information to a CSV file')]
        [ValidateNotNullOrEmpty()]
        [string]$OutCsv,
        [Parameter(HelpMessage = 'Specify a path to generate an HTML certificate report')]
        [ValidateNotNullOrEmpty()]
        [string]$GenerateReport,
        [Parameter(HelpMessage = 'Exclude self-signed certificates from the output')]
        [switch]$ExcludeSelfSignedCertificate

    )

    Begin {

        Write-Verbose 'Begin certificate detail retrieval...'

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

        # Initialize collection to store results for CSV export or HTML report
        If ($OutCsv -or $GenerateReport) {

            $Script:CollectedResults = [System.Collections.Generic.List[PSCustomObject]]::new()

        }

    }

    Process {

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

        # Notify user if no identity was provided
        If ($null -eq $Identity) {

            Write-Warning 'No input provided. Specify an identity using -Identity or pipe AD objects to this script.'
            Return

        }

        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)') {

                    # If piped object already has userCertificate with data, use it directly
                    If ($Id.userCertificate -and $Id.userCertificate.Count -gt 0) {

                        Write-Verbose "Using piped AD object: $($Id.SamAccountName)"
                        $Principal = $Id

                    }

                    Else {

                        # Re-query to get userCertificate property
                        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 $Script:RequiredProperties -ErrorAction Stop

                        }

                        'Computer' {

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

                        }

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

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

                        }

                        Default {

                            # Try to find the object using cascading approach
                            Try {

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

                            }

                            Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {

                                Try {

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

                                }

                                Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {

                                    Try {

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

                                    }

                                    Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {

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

                                    }

                                }

                            }

                        }

                    }

                }

                # Validate certificate presence
                If (-not $Principal.userCertificate -or $Principal.userCertificate.Count -eq 0) {

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

                }

                Write-Verbose "Found $($Principal.userCertificate.Count) certificate(s) for $($Principal.SamAccountName)"

                # Determine account name for file naming
                $BaseFileName = If ([string]::IsNullOrWhiteSpace($Principal.SamAccountName)) {

                    If ($Principal.DistinguishedName -match 'CN=([^,]+)') {

                        $Matches[1].TrimEnd('$')

                    }

                    Else {

                        Write-Warning "Unable to determine account name for principal: $($Principal.DistinguishedName)"
                        Continue

                    }

                }

                Else {

                    $Principal.SamAccountName.TrimEnd('$')

                }

                # Process each certificate
                $CertificateCount = $Principal.userCertificate.Count
                $CertificateNumber = 0
                $NonSelfSignedCertFound = $False

                ForEach ($CertBytes in $Principal.userCertificate) {

                    $CertificateNumber++

                    Try {

                        # Create X509Certificate2 object from binary data
                        $Certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertBytes)

                        # Skip self-signed certificates if ExcludeSelfSignedCertificate is specified
                        If ($ExcludeSelfSignedCertificate -and $Certificate.Subject -eq $Certificate.Issuer) {

                            Write-Verbose "Skipping self-signed certificate for $($Principal.SamAccountName) (Thumbprint: $($Certificate.Thumbprint))"
                            Continue

                        }

                        # Track that we found at least one non-self-signed certificate
                        $NonSelfSignedCertFound = $True

                        # Export certificate if requested
                        If ($OutFile) {

                            $FileName = If ($CertificateCount -eq 1) {

                                "$BaseFileName.crt"

                            }

                            Else {

                                '{0}_{1:D3}.crt' -f $BaseFileName, $CertificateNumber

                            }

                            Try {

                                # Export in PEM format
                                $PemCert = @(

                                    '-----BEGIN CERTIFICATE-----'
                                    [Convert]::ToBase64String($CertBytes, [System.Base64FormattingOptions]::InsertLineBreaks)
                                    '-----END CERTIFICATE-----'

                                )

                                -join "`r`n"

                                $PemCert | Out-File -FilePath $FileName -Encoding ASCII -Force
                                Write-Verbose "Certificate exported to `"$FileName`"."

                            }

                            Catch {

                                Write-Warning "Error exporting certificate to file '$FileName': $_"

                            }

                        }

                        # Extract Enhanced Key Usage (EKU) / Application Policies
                        $EkuExtension = $Certificate.Extensions | Where-Object { $_.Oid.Value -eq '2.5.29.37' }
                        $EkuList = If ($EkuExtension) {

                            $EkuExtension.EnhancedKeyUsages | ForEach-Object {

                                If ($_.FriendlyName) { $_.FriendlyName } Else { $_.Value }

                            }

                        }

                        Else {

                            $null

                        }

                        # Build certificate details object
                        $CertDetails = [PSCustomObject]@{

                            AccountName        = $Principal.SamAccountName
                            AccountType        = $Principal.ObjectClass
                            CertificateNumber  = $CertificateNumber
                            Subject            = $Certificate.Subject
                            Issuer             = $Certificate.Issuer
                            SerialNumber       = $Certificate.SerialNumber
                            Thumbprint         = $Certificate.Thumbprint
                            Issued             = $Certificate.NotBefore
                            Expires            = $Certificate.NotAfter
                            Policies           = If ($EkuList) { $EkuList -join '; ' } Else { $null }
                            KeyType            = $Certificate.PublicKey.Oid.FriendlyName
                            KeyLength          = $Certificate.PublicKey.Key.KeySize
                            SignatureAlgorithm = $Certificate.SignatureAlgorithm.FriendlyName

                        }

                        # Add to collection if exporting to CSV or generating report
                        If ($OutCsv -or $GenerateReport) {

                            $Script:CollectedResults.Add($CertDetails)

                        }

                        # Output certificate details
                        $CertDetails

                    }

                    Catch {

                        Write-Warning "Error processing certificate $CertificateNumber for '$($Principal.SamAccountName)': $_"

                    }

                }

                # Warn if ExcludeSelfSignedCertificate was specified and all certificates were self-signed
                If ($ExcludeSelfSignedCertificate -and -not $NonSelfSignedCertFound) {

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

                }

            }

            Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {

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

            }

            Catch {

                Write-Warning "Error retrieving certificate details for '$Id': $_"

            }

        }

    }

    End {

        # Export to CSV if requested
        If ($OutCsv -and $Script:CollectedResults.Count -gt 0) {

            Try {

                $Script:CollectedResults | Export-Csv -Path $OutCsv -NoTypeInformation -Force
                Write-Verbose "Certificate information exported to `"$OutCsv`"."

            }

            Catch {

                Write-Warning "Error exporting to CSV file '$OutCsv': $_"

            }

        }
        ElseIf ($OutCsv -and $Script:CollectedResults.Count -eq 0) {

            Write-Warning "No certificate data to export to CSV file."

        }

        # Generate HTML report if requested
        If ($GenerateReport -and $Script:CollectedResults.Count -gt 0) {

            Try {

                # Calculate summary statistics
                $TotalCertificates = $Script:CollectedResults.Count
                $UniqueAccounts = ($Script:CollectedResults | Select-Object -Property AccountName -Unique).Count
                $ExpiredCertificates = ($Script:CollectedResults | Where-Object { $_.Expires -lt (Get-Date) }).Count
                $ExpiringIn30Days = ($Script:CollectedResults | Where-Object { $_.Expires -ge (Get-Date) -and $_.Expires -lt (Get-Date).AddDays(30) }).Count
                $ExpiringIn90Days = ($Script:CollectedResults | Where-Object { $_.Expires -ge (Get-Date) -and $_.Expires -lt (Get-Date).AddDays(90) }).Count
                $SelfSignedCertificates = ($Script:CollectedResults | Where-Object { $_.Subject -eq $_.Issuer }).Count

                # Build HTML content
                $HtmlHead = @"
<!DOCTYPE html>
<html>
<head>
    <title>AD Principal Certificate Report</title>
    <style>
        body {
            font-family: Segoe UI, Arial, sans-serif;
            margin: 20px;
            background-color: #f5f5f5;
        }
        h1 {
            color: #2c3e50;
            border-bottom: 3px solid #3498db;
            padding-bottom: 10px;
        }
        h2 {
            color: #34495e;
            margin-top: 30px;
        }
        .summary {
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        .summary-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
        }
        .summary-item {
            background-color: #3498db;
            color: white;
            padding: 15px;
            border-radius: 5px;
            text-align: center;
        }
        .summary-item.warning {
            background-color: #f39c12;
        }
        .summary-item.danger {
            background-color: #e74c3c;
        }
        .summary-item.info {
            background-color: #95a5a6;
        }
        .summary-item .number {
            font-size: 2em;
            font-weight: bold;
        }
        .summary-item .label {
            font-size: 0.9em;
            opacity: 0.9;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            background-color: #fff;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            border-radius: 8px;
            overflow: hidden;
        }
        th {
            background-color: #2c3e50;
            color: white;
            padding: 12px 8px;
            text-align: left;
            font-weight: 600;
        }
        td {
            padding: 10px 8px;
            border-bottom: 1px solid #ecf0f1;
            font-size: 0.9em;
        }
        tr:hover {
            background-color: #f8f9fa;
        }
        tr.expired {
            background-color: #fadbd8;
        }
        tr.expiring-soon {
            background-color: #fdebd0;
        }
        tr.self-signed {
            background-color: #d5d8dc;
        }
        .timestamp {
            color: #7f8c8d;
            font-size: 0.85em;
            margin-top: 20px;
        }
        .thumbprint {
            font-family: Consolas, monospace;
            font-size: 0.85em;
        }
    </style>
</head>
<body>
    <h1>AD Principal Certificate Report</h1>
"@


                $HtmlSummary = @"
    <div class="summary">
        <h2>Summary</h2>
        <div class="summary-grid">
            <div class="summary-item">
                <div class="number">$TotalCertificates</div>
                <div class="label">Total Certificates</div>
            </div>
            <div class="summary-item">
                <div class="number">$UniqueAccounts</div>
                <div class="label">Unique Accounts</div>
            </div>
            <div class="summary-item info">
                <div class="number">$SelfSignedCertificates</div>
                <div class="label">Self-Signed</div>
            </div>
            <div class="summary-item danger">
                <div class="number">$ExpiredCertificates</div>
                <div class="label">Expired</div>
            </div>
            <div class="summary-item warning">
                <div class="number">$ExpiringIn30Days</div>
                <div class="label">Expiring in 30 Days</div>
            </div>
            <div class="summary-item warning">
                <div class="number">$ExpiringIn90Days</div>
                <div class="label">Expiring in 90 Days</div>
            </div>
        </div>
    </div>
"@


                # Build table rows
                $TableRows = ForEach ($Cert in $Script:CollectedResults) {

                    $RowClass = ''
                    If ($Cert.Expires -lt (Get-Date)) {

                        $RowClass = ' class="expired"'

                    }
                    ElseIf ($Cert.Expires -lt (Get-Date).AddDays(30)) {

                        $RowClass = ' class="expiring-soon"'

                    }
                    ElseIf ($Cert.Subject -eq $Cert.Issuer) {

                        $RowClass = ' class="self-signed"'

                    }

                    $ExpiresFormatted = $Cert.Expires.ToString('yyyy-MM-dd HH:mm')
                    $IssuedFormatted = $Cert.Issued.ToString('yyyy-MM-dd HH:mm')

                    @"
        <tr$RowClass>
            <td>$([System.Web.HttpUtility]::HtmlEncode($Cert.AccountName))</td>
            <td>$([System.Web.HttpUtility]::HtmlEncode($Cert.AccountType))</td>
            <td>$([System.Web.HttpUtility]::HtmlEncode($Cert.Subject))</td>
            <td>$([System.Web.HttpUtility]::HtmlEncode($Cert.Issuer))</td>
            <td class="thumbprint">$([System.Web.HttpUtility]::HtmlEncode($Cert.Thumbprint))</td>
            <td>$IssuedFormatted</td>
            <td>$ExpiresFormatted</td>
            <td>$([System.Web.HttpUtility]::HtmlEncode($Cert.KeyType))</td>
            <td>$($Cert.KeyLength)</td>
        </tr>
"@


                }

                $HtmlTable = @"
    <h2>Certificate Details</h2>
    <table>
        <thead>
            <tr>
                <th>Account Name</th>
                <th>Account Type</th>
                <th>Subject</th>
                <th>Issuer</th>
                <th>Thumbprint</th>
                <th>Issued</th>
                <th>Expires</th>
                <th>Key Type</th>
                <th>Key Length</th>
            </tr>
        </thead>
        <tbody>
$($TableRows -join "`n")
        </tbody>
    </table>
"@


                $HtmlFooter = @"
    <p class="timestamp">Report generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')</p>
</body>
</html>
"@


                # Combine and write HTML
                $HtmlContent = $HtmlHead + $HtmlSummary + $HtmlTable + $HtmlFooter
                $HtmlContent | Out-File -FilePath $GenerateReport -Encoding UTF8 -Force
                Write-Verbose "HTML report generated: `"$GenerateReport`"."

                # Open the report in the default browser
                Invoke-Item -Path $GenerateReport

            }

            Catch {

                Write-Warning "Error generating HTML report '$GenerateReport': $_"

            }

        }
        ElseIf ($GenerateReport -and $Script:CollectedResults.Count -eq 0) {

            Write-Warning "No certificate data to generate HTML report."

        }

        Write-Verbose 'Certificate detail retrieval completed.'

    }

}

# SIG # Begin signature block
# MIIf2QYJKoZIhvcNAQcCoIIfyjCCH8YCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBOAHQE0MqXdzSE
# 31pIT0mBtUqCrmMKTOLQoVgWj3rGNqCCGpkwggNZMIIC36ADAgECAhAPuKdAuRWN
# 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
# 6vdCvHlshtjdNXOCIUjsarfNZzGCBJYwggSSAgEBMHgwZDELMAkGA1UEBhMCVVMx
# FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTwwOgYDVQQDEzNEaWdpQ2VydCBHbG9i
# YWwgRzMgQ29kZSBTaWduaW5nIEVDQyBTSEEzODQgMjAyMSBDQTECEA1KNNqGkI/A
# Eyy8gTeTryQwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgtwTGWgh2X29xurLZgx6SSiNQ
# ziEUBjsqMdCjFB3ecRIwCwYHKoZIzj0CAQUABEYwRAIgdLnmNlkqVZeoOehmoLZn
# DzIJLkW5Em4LsrqDL3LW8XcCICiMbqjxKz9VHOJ0h/vcq8xTKt6x+ucxL6lnECQZ
# 2ceaoYIDJjCCAyIGCSqGSIb3DQEJBjGCAxMwggMPAgEBMH0waTELMAkGA1UEBhMC
# VVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBU
# cnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMQIQ
# CoDvGEuN8QWC0cR2p5V0aDANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI2MDIwNzIxMzU1MFowLwYJKoZI
# hvcNAQkEMSIEICYWU1OcdMFXzFx/Li1s4Mo3pef1Y55Bv4peaQh/qqsEMA0GCSqG
# SIb3DQEBAQUABIICALLCSfhdTBfgfJj5mMCxqLMdZXZwXENP+K5/ytSug4SlRXmc
# hJv21IA2djcpzV/GQB+7CcYWMPxRQAL3e3GNA8Sw/VzBcfu3OdDjFutYG+imQOER
# Alb3o2iZNFHr9+KVFF5rI/+61N3BGpF87JdLvO3ETDXiUVfrTtn8B4kqsNTooejB
# isd29pFsKnYvi6pqoc6vHZ6X8BqXSPZZIsruf8T3rR+IPgAe8CK2ewelx0kfsswu
# uw2XSrgdBfIUtM1DHk0sXdZ4h3DN7z1C0yj/6lMXpGaoIt3Lkk2fUgCsKBlgmDK4
# rcN868uu29sAY8Ffm500JjJlXrE0GaKBxeJXZcswZi1WU307GXBgyeWGoC037T7a
# c3qRYovFw212gdIa+tjO0jrO4kpdTNYrBbAYN+FKdUZ3yl0d2BnAVLMQHpYgSlV4
# RTNF+OBsBpF5J1RGB+hrqdfoRssIcnj2iD7ujXyGd+UZqClKngY9s3K/5QyHiw7m
# gfn0ahcPkD3VKsJgAoVt4GKnVuISy5AF3+tjQSeoGxrB3Dd8oDTOUdEWEO/FwZYw
# yEY8uBac/xC98y27c2IqerdSGyn+yMA8V69nM3LfPdnsrgQb9v9mMJ4FO0JleeLm
# PO2V5Nn3BMXvfsbSa1F/eoeiRXH245LM7kaS+UpfOlXvLpbmGC5FlqK2hk+L
# SIG # End signature block