Get-UEFICertificate.ps1

<#PSScriptInfo
 
.VERSION 1.0
 
.GUID 7c06efd4-2530-487d-b92c-d5874d0b53b3
 
.AUTHOR Richard Hicks
 
.COMPANYNAME Richard M. Hicks Consulting, Inc.
 
.COPYRIGHT Copyright (C) 2025 Richard M. Hicks Consulting, Inc. All Rights Reserved.
 
.LICENSE Licensed under the MIT License. See LICENSE file in the project root for full license information.
 
.LICENSEURI https://github.com/richardhicks/uefi/blob/main/LICENSE
 
.PROJECTURI https://github.com/richardhicks/uefi/
 
.TAGS UEFI, SecureBoot, Certificates, PK, KEK
 
#>


<#
 
.SYNOPSIS
    Reads Platform Key (PK) and Key Exchange Key (KEK) certificates from UEFI.
 
.DESCRIPTION
    This script retrieves and displays Secure Boot certificates (PK and KEK) from UEFI firmware and optionally saves them to files.
 
.PARAMETER OutFile
    Switch to enable saving certificates to files in base64 format. Files are named pkcert.cer and kekcert.cer (with numeric suffixes if multiple certificates exist).
 
.PARAMETER OutPath
    Optional path to a folder where certificates will be saved. If not specified when using -OutFile, files are saved to the script's directory.
 
.EXAMPLE
    .\Get-UEFICertificate.ps1
 
    Returns certificate objects without saving to files.
 
.EXAMPLE
    .\Get-UEFICertificate.ps1 -OutFile
 
    Returns certificate objects and saves them as base64-encoded .cer files in the script's directory.
 
.EXAMPLE
    .\Get-UEFICertificate.ps1 -OutFile -OutPath 'C:\Temp\UEFICertificates'
 
    Returns certificate objects and saves them as base64-encoded .cer files in the specified folder.
 
.INPUTS
    None.
 
.OUTPUTS
    PSCustomObject representing each certificate with properties such as Type, Subject, Issuer, Thumbprint, Issued, Expires, SerialNumber, and methods to save the certificate to a file.
 
.LINK
    https://github.com/richardhicks/uefi/Get-UEFICertificate.ps1
 
.NOTES
    Version: 1.0
    Creation Date: November 13, 2025
    Last Updated: November 13, 2025
    Author: Richard Hicks
    Organization: Richard M. Hicks Consulting, Inc.
    Contact: rich@richardhicks.com
    Website: https://www.richardhicks.com/
 
#>


[CmdletBinding()]

Param(

    [Parameter()]
    [Switch]$OutFile,
    [String]$OutPath

)

# Script requires administrative privileges
#Requires -RunAsAdministrator

# Parse ESL (EFI Signature List) format
Function ConvertFrom-SignatureList {

    Param(

        [Byte[]]$Data

    )

    $Certificates = @()
    $Offset = 0

    While ($Offset -lt $Data.Length) {

        # ESL Header is 28 bytes
        If (($Data.Length - $Offset) -lt 28) {

            Break

        }

        # Read Signature Type GUID (16 bytes)
        $GuidBytes = $Data[$Offset..($Offset + 15)]
        $SignatureType = [Guid]::new([Byte[]]$GuidBytes)
        $Offset += 16

        # Read List Size (4 bytes)
        $ListSize = [BitConverter]::ToUInt32($Data, $Offset)
        $Offset += 4

        # Read Header Size (4 bytes)
        $HeaderSize = [BitConverter]::ToUInt32($Data, $Offset)
        $Offset += 4

        # Read Signature Size (4 bytes)
        $SignatureSize = [BitConverter]::ToUInt32($Data, $Offset)
        $Offset += 4

        If ($ListSize -eq 0 -or $SignatureSize -eq 0) {

            Break

        }

        # Skip signature list header
        If ($HeaderSize -gt 0) {

            $Offset += $HeaderSize

        }

        # Calculate number of signatures
        $DataSize = $ListSize - 28 - $HeaderSize
        $SigCount = [Math]::Floor($DataSize / $SignatureSize)

        # Extract each signature
        For ($I = 0; $I -lt $SigCount; $I++) {

            # Signature Header is 16 bytes (Owner GUID)
            If (($Offset + 16) -gt $Data.Length) {

                Break

            }

            $OwnerGuidBytes = $Data[$Offset..($Offset + 15)]
            $OwnerGuid = [Guid]::new([Byte[]]$OwnerGuidBytes)
            $Offset += 16

            # Certificate data
            $CertSize = $SignatureSize - 16
            If (($Offset + $CertSize) -gt $Data.Length) {

                Break

            }

            $CertData = $Data[$Offset..($Offset + $CertSize - 1)]
            $Offset += $CertSize

            $Certificates += [PSCustomObject]@{

                SignatureType   = $SignatureType
                OwnerGuid       = $OwnerGuid
                CertificateData = $CertData
                CertificateSize = $CertSize

            }

        }

    }

    Return $Certificates

}

# Convert certificate data to PEM format
Function ConvertTo-PemFormat {

    Param(

        [Byte[]]$CertificateData

    )

    $Base64 = [Convert]::ToBase64String($CertificateData)
    $Pem = "-----BEGIN CERTIFICATE-----`n"

    For ($I = 0; $I -lt $Base64.Length; $I += 64) {

        $Length = [Math]::Min(64, $Base64.Length - $I)
        $Pem += $Base64.Substring($I, $Length) + "`n"

    }

    $Pem += "-----END CERTIFICATE-----`n"

    Return $Pem

}

# Main script
Try {

    # Check if Secure Boot is available
    Try {

        $SecureBootStatus = Confirm-SecureBootUEFI

    }

    Catch {

        Write-Warning 'Unable to access UEFI Secure Boot information. This system may not support UEFI or Secure Boot.'
        Exit 1

    }

    Write-Verbose "Secure Boot Status: $SecureBootStatus."

    # Determine output directory if -OutFile is used
    $OutputDirectory = $Null
    If ($OutFile) {

        # If -OutPath is provided, use it; otherwise use script directory
        If ([String]::IsNullOrWhiteSpace($OutPath)) {

            $OutputDirectory = If ($PSScriptRoot) {

                $PSScriptRoot

            }

            Else {

                (Get-Location).Path

            }

            Write-Verbose "Using script directory for output: $OutputDirectory"

        }

        Else {

            $OutputDirectory = $OutPath

        }

        # Check if path looks like a file (has an extension)
        If ([System.IO.Path]::HasExtension($OutputDirectory)) {

            Write-Warning 'OutPath must be a folder path, not a file path. Please provide a folder path without a filename.'
            Exit 1

        }

        # Create directory if it doesn't exist
        If (-not (Test-Path $OutputDirectory)) {

            Write-Verbose "Output directory does not exist. Creating: $OutputDirectory"

            Try {

                New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
                Write-Verbose "Successfully created directory: $OutputDirectory"

            }

            Catch {

                Write-Warning "Failed to create directory '$OutputDirectory': $($_.Exception.Message)"
                Exit 1

            }

        }

        Else {

            # Verify it's a directory, not a file
            If (-not (Test-Path $OutputDirectory -PathType Container)) {

                Write-Warning "The path '$OutputDirectory' exists but is not a folder. Please provide a valid folder path."
                Exit 1

            }

        }

    }

    # Initialize results array, certificate counters, and saved files list
    $Results = @()
    $PkCount = 0
    $KekCount = 0
    $SavedFiles = @()

    # Define certificates to retrieve
    $CertTypes = @(

        @{ Name = 'PK'; Description = 'Platform Key'; VariableName = 'pk' }
        @{ Name = 'KEK'; Description = 'Key Exchange Key'; VariableName = 'kek' }

    )

    ForEach ($CertType in $CertTypes) {

        Write-Verbose "Reading $($CertType.Description) ($($CertType.Name))..."

        Try {

            # Get UEFI variable
            $UefiVar = Get-SecureBootUEFI -Name $CertType.VariableName

            If ($Null -eq $UefiVar -or $Null -eq $UefiVar.Bytes -or $UefiVar.Bytes.Length -eq 0) {

                Write-Warning "No $($CertType.Name) certificate found or empty data."
                Continue

            }

            # Parse the signature list
            $Signatures = ConvertFrom-SignatureList -Data $UefiVar.Bytes

            If ($Signatures.Count -eq 0) {

                Write-Warning "Could not parse certificates from $($CertType.Name) data."
                Continue

            }

            Write-Verbose "Found $($Signatures.Count) signature(s)."

            # Create result object for each signature
            $SigIndex = 0
            ForEach ($Sig in $Signatures) {

                $SigIndex++

                # Try to parse as X.509 certificate
                Try {

                    # Try different certificate parsing approaches
                    $Cert = $Null
                    $ParseError = $Null

                    # Method 1: Direct parsing
                    Try {

                        $Cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($Sig.CertificateData)

                    }

                    Catch {

                        $ParseError = $_.Exception.Message

                    }

                    # Method 2: Try parsing with explicit type
                    If ($Null -eq $Cert) {

                        Try {

                            $Cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New([Byte[]]$Sig.CertificateData)

                        }

                        Catch {

                            $ParseError = $_.Exception.Message

                        }

                    }

                    If ($Null -ne $Cert) {

                        $CertInfo = @{

                            Subject      = $Cert.Subject
                            Issuer       = $Cert.Issuer
                            Thumbprint   = $Cert.Thumbprint
                            Issued       = $Cert.NotBefore
                            Expires      = $Cert.NotAfter
                            SerialNumber = $Cert.SerialNumber
                            ParseError   = $Null

                        }

                    }

                    Else {

                        $CertInfo = @{

                            Subject      = 'Unable to parse certificate'
                            Issuer       = 'Unable to parse certificate'
                            Thumbprint   = 'N/A'
                            Issued       = $Null
                            Expires      = $Null
                            SerialNumber = 'N/A'
                            ParseError   = $ParseError

                        }

                    }

                }

                Catch {

                    $CertInfo = @{

                        Subject      = 'Error parsing certificate'
                        Issuer       = 'Error parsing certificate'
                        Thumbprint   = 'N/A'
                        Issued       = $Null
                        Expires      = $Null
                        SerialNumber = 'N/A'
                        ParseError   = $_.Exception.Message

                    }

                }

                $ResultObj = [PSCustomObject]@{

                    Type            = $CertType.Name
                    Description     = $CertType.Description
                    Index           = $SigIndex
                    SignatureType   = $Sig.SignatureType
                    OwnerGuid       = $Sig.OwnerGuid
                    CertificateSize = $Sig.CertificateSize
                    Subject         = $CertInfo.Subject
                    Issuer          = $CertInfo.Issuer
                    Thumbprint      = $CertInfo.Thumbprint
                    Issued          = $CertInfo.Issued
                    Expires         = $CertInfo.Expires
                    SerialNumber    = $CertInfo.SerialNumber
                    ParseError      = $CertInfo.ParseError
                    RawData         = $Sig.CertificateData

                }

                # Add Save method
                $ResultObj | Add-Member -MemberType ScriptMethod -Name 'SaveToFile' -Value {

                    Param(

                        [String]$Path

                    )

                    If ([String]::IsNullOrWhiteSpace($Path)) {

                        Throw 'Path parameter is required'

                    }

                    $Dir = Split-Path -Path $Path -Parent
                    If ($Dir -and -not (Test-Path $Dir)) {

                        New-Item -ItemType Directory -Path $Dir -Force | Out-Null

                    }

                    # Convert to PEM format using helper function
                    $Pem = ConvertTo-PemFormat -CertificateData $This.RawData
                    [System.IO.File]::WriteAllText($Path, $Pem)

                    Write-Output "Saved to: $Path"

                }

                $Results += $ResultObj

                # Handle -OutFile switch
                If ($OutputDirectory) {

                    # Determine filename based on certificate type and count
                    If ($CertType.Name -eq 'PK') {

                        $PkCount++
                        $Filename = If ($PkCount -eq 1) {

                            'pkcert.cer'

                        }

                        Else {

                            "pkcert$PkCount.cer"

                        }

                    }

                    Else {

                        $KekCount++
                        $Filename = If ($KekCount -eq 1) {

                            'kekcert.cer'

                        }

                        Else {

                            "kekcert$KekCount.cer"

                        }

                    }

                    $Filepath = Join-Path $OutputDirectory $Filename

                    # Save in proper PEM format using helper function
                    $Pem = ConvertTo-PemFormat -CertificateData $Sig.CertificateData
                    [System.IO.File]::WriteAllText($Filepath, $Pem)

                    # Store filepath for display at end
                    $SavedFiles += $Filepath

                }

            }

        }

        Catch {

            Write-Warning "Error reading $($CertType.Name): $($_.Exception.Message)"

        }

    }

    # Return results
    If ($Results.Count -eq 0) {

        Write-Warning 'No certificates were retrieved.'
        Exit 1

    }

    Write-Verbose "Successfully retrieved $($Results.Count) certificate(s)."

    # Output results first (they will be displayed)
    $Results

    # Display saved files after the objects
    If ($SavedFiles.Count -gt 0) {

        ForEach ($File in $SavedFiles) {

            Write-Output "Saved certificate to: $File"

        }

    }

}

Catch {

    Write-Warning "An unexpected error occurred: $($_.Exception.Message)"
    Write-Warning $_.ScriptStackTrace
    Exit 1

}

# SIG # Begin signature block
# MIIf2gYJKoZIhvcNAQcCoIIfyzCCH8cCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAxbqCPTa9PMkhg
# qklqsjN/Jx5DESp2LiyJf4Gzca7RVqCCGpkwggNZMIIC36ADAgECAhAPuKdAuRWN
# 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
# 6vdCvHlshtjdNXOCIUjsarfNZzGCBJcwggSTAgEBMHgwZDELMAkGA1UEBhMCVVMx
# FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTwwOgYDVQQDEzNEaWdpQ2VydCBHbG9i
# YWwgRzMgQ29kZSBTaWduaW5nIEVDQyBTSEEzODQgMjAyMSBDQTECEA1KNNqGkI/A
# Eyy8gTeTryQwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgd5qUV7kLkCKzY7b+84Q0qshn
# I1mK+BcPCcNhpDMsaMcwCwYHKoZIzj0CAQUABEcwRQIhAOF24xcZgqAfzw0wj6VR
# cs8NDRfXzj7o99TM8CNQSkE4AiBxu7ECBpJvaDE3OlufIYVSptMnI8ZnGx7T6WBR
# xQzyFqGCAyYwggMiBgkqhkiG9w0BCQYxggMTMIIDDwIBATB9MGkxCzAJBgNVBAYT
# AlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQg
# VHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEC
# EAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMx
# CwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNTExMTMyMjU1MDBaMC8GCSqG
# SIb3DQEJBDEiBCBE7Y4R6W99+Ja+lms27oPSBHIbOYIgbXltf30j0fJBCTANBgkq
# hkiG9w0BAQEFAASCAgBX6yykxBSGZlYUWs0QdFgZLEfgIpWt5SXdiNxvuzYbDpyz
# N8Ph2HplubTbe6bGhBQUw0updRLLGoE5KVwTooleLKdZoiRXvH9oNQb8IA/BiInM
# 4roHcVaJvnsivVy7csBeOvi/D/UkGhy8lgLJeioXM9EUxMa1Tzx9/PBKLDWh2ypw
# l76HuCH2Pj8r+PRgPfXvrUhkjpVlsm+Nl1D5ibRXAtjZZlpONfSeHtZRrjSOF0kM
# v9OFS/uxy++shLlzHSvn8CHiBkZAQ4QddnCWQ/ULppQRb1kWkIuAZaASbuBOAAXF
# G/PItFVNZwdJ5nMMw2DCLw7wf0oZFgY0iSDoI28FziLbHTziR6RHsRW4C1hZmjSL
# Lsoa2c7bJwJ2bbw39D7mefTK1mmN/B0u2JcDmykFNOWVvyQCrTJdzuJPEXFmEEfw
# EJzk2W806iXm24h7UuxQTCpiv6X/R72GKltfMMCv9qtt1Ew1PFWIFJlFb5bBYLM4
# vkZXVxKOZBexoRSDOSdw/54hYBLXVEdBZU5/DIaRedk3GBOA9m+JZr1oJTavV3Pc
# oGmfx+eqeALEtvQLSPZgieVkKMVPrg684avsoXlCyxD6qjGCCe+2KuilJHLFwkSw
# 44fGrewNi4NQ6iZNbMkStmGYWeP+/R30mV75YNvw5LZRRqVzm+u5fbQvnadqpg==
# SIG # End signature block