Public/SEPPmailAPI-Crypto.ps1

<#
.SYNOPSIS
    Lists public domain S/MIME and/or PGP keys across all domains.
 
.DESCRIPTION
    Retrieves the public S/MIME certificates and/or PGP keys for all domains
    known to the SEPPmail appliance. The result can be returned either in full
    detail or - when -list is used - as a compact array of serial numbers /
    key IDs.
 
    This is a read-only query (HTTP GET on /crypto/domain) and supports
    filtering by cryptographic technology and certificate type.
 
.PARAMETER list
    If $true, returns an array of serial numbers / key IDs instead of the full
    certificate/key details. Useful for fast inventory operations.
 
    Default: $false
 
.PARAMETER limit
    Limits the number of returned entries.
 
.PARAMETER technology
    Restricts the output to a single cryptographic technology.
    Valid values: 'both', 'smime', 'pgp'.
 
    Default: 'both'
 
.PARAMETER onlyMetaData
    If $true, returns only the certificate/key metadata without the actual
    certificate or key material.
 
    Default: $false
 
.PARAMETER public
    If $true, includes public S/MIME certificates in the result.
 
.PARAMETER hin
    If $true, includes HIN (Health Info Net) S/MIME certificates in the result.
 
.PARAMETER SMAHost
    SEPPmail API hostname. Defaults to the configured value.
 
.PARAMETER SMAPort
    SEPPmail API port. Defaults to the configured value.
 
.PARAMETER SMAVersion
    SEPPmail API version. Defaults to the configured value.
 
.PARAMETER SMACred
    API credentials (PSCredential). Defaults to the configured value.
 
.PARAMETER SMASkipCertCheck
    Skip SSL certificate validation. Use only in test environments.
 
.OUTPUTS
    System.Management.Automation.PSCustomObject
        Returns the domain crypto objects, or - when -list is set - an array of
        serial numbers / key IDs.
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - This is a read-only operation; -WhatIf shows the target URI only.
 
.LINK
    Get-SMACryptoDomain
 
.LINK
    Get-SMACryptoDomainKey
 
.EXAMPLE
    PS C:\> Find-SMACryptoDomain
    Returns the full crypto details for all domains. May take some time.
 
.EXAMPLE
    PS C:\> Find-SMACryptoDomain -list
    Returns only the serial numbers / key IDs of all domain keys.
 
.EXAMPLE
    PS C:\> Find-SMACryptoDomain -technology smime -onlyMetaData $true
    Returns only the metadata of all S/MIME certificates, without the
    certificate material itself.
#>

function Find-SMACryptoDomain
{
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        #region API params
        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'if true, return an array of serial keys instead of detailed information'
        )]
        [SMARestType('query')]
        [bool]$list,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'limit output to #'
        )]
        [SMARestType('query')]
        [int]$limit,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'limit output to both, smime of pgp'
        )]
        [ValidateSet('both','smime','pgp')]
        [SMARestType('query')]
        [string]$technology,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Parameter to specify if only the certificates/keys metadate should be returned without the certificate'
        )]
        [SMARestType('query')]
        [bool]$onlyMetaData,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Parameter to specify if public S/MIME certificate should be processed'
        )]
        [SMARestType('query')]
        [bool]$public,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Parameter to specify if HIN S/MIME certificate should be processed'
        )]
        [SMARestType('query')]
        [bool]$hin,
        #endregion API params
        
        #region Config parameters block
        [Parameter(Mandatory = $false)]
        [String]$SMAHost = $Script:activeCfg.SMAHost,

        [Parameter(Mandatory = $false)]
        [int]$SMAPort = $Script:activeCfg.SMAPort,

        [Parameter(Mandatory = $false)]
        [String]$SMAVersion = $Script:activeCfg.SMAPIVersion,

        [Parameter(Mandatory=$false)]
        [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred,
        
        [Parameter(Mandatory=$false)]
        [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck
        #endregion
    )

    begin {
        Write-verbose "Verifying required variables from SMA Config"
        if (! (verifyVars -VarList $Script:requiredVarList)) {Throw($missingVarsMessage)} # end if

        Write-verbose "Rest methdod is $restMethod"
        $RestMethod = 'GET'

        Write-Verbose "Creating URL base path"
        $uriPath = "{0}/{1}" -f 'crypto', 'domain'

        Write-Verbose "Adding SMA Params"
        $smaParams=@{
            Host    = $SMAHost
            Port    = $SMAPort
            Version = $SMAVersion
        }
    }
    process {   
        Write-Verbose "Getting parameter arrays for Body, Path and Query from $($myInvocation.Mycommand.Parameters.Values)"
        $paramArrays = Get-SMAParameterArray -ParentPSBoundParameters $PSBoundParameters -ParentInvocation $MyInvocation.MyCommand.Parameters.Values
        Write-Debug "ParamArrays: $($paramArrays | Out-String)"

        Write-verbose "Initializing REST-Data structures"
        [string]$uri                = $null
        [hashtable]$bodyHt          = @{}
        [hashtable]$queryParamHt    = @{}

        Write-verbose "Adding optional path to basepath based on $($paramArrays.Path) parameters"
        if ($paramArrays.Path.Count -gt 0) {
            Foreach ($pathParam in $paramArrays.Path) {
                $uriPath = "{0}/{1}" -f $uriPath, $PSBoundParameters[$pathParam]
            }
        }
        Write-Debug "Final uri path is: $uriPath"

        Write-verbose "Building body HashTable based on $($paramArrays.Body) parameters"
        if ($paramArrays.Body.Count -gt 0) {
            Foreach ($bodyParam in $paramArrays.Body) {
                $bodyHt[$bodyParam] = $PSBoundParameters[$bodyParam]
            }
        }
        if ($paramArrays.password.Count -gt 0) {
            Foreach ($passwordParam in $paramArrays.password) {
                $secureString = $PSBoundParameters[$passwordParam]
                $plainText = ConvertFrom-SecureString $secureString -AsPlainText
                $bodyHt[$passwordParam] = $plainText
            }
        }
        $body = $bodyHt | ConvertTo-Json
        Write-Debug "Body JSON: $body"

        Write-verbose "Building query string based on $($paramArrays.Query) parameters"
        if ($paramArrays.Query.Count -gt 0) {
            Foreach ($queryArrayParam in $paramArrays.Query) {
                $queryParamHt[$queryArrayParam] = $PSBoundParameters[$queryArrayParam]
            }
        }
        Write-Debug "QueryParams: $($queryParamHt | Out-String)"

        #TODO: Clean this as parameter added $queryParamHt['list'] = $false

        Write-Verbose "Constructing full URI based on parameters"
        if ($queryParamHt) {
            $uri = New-SMAQueryString -uriPath $uriPath -qParam $queryParamHt @smaParams
        }
        else {
            $uri = New-SMAQueryString -uriPath $uriPath @smaParams
        }
        Write-Debug "Final URI: $uri"

        Write-verbose "Crafting Invokeparam for Invoke-SMARestMethod"
        $invokeParam = @{
                      Uri = $uri 
                   Method = $RestMethod
                     Cred = $SMACred
                     body = $body
            SkipCertCheck = $SMASkipCertCheck
        }
        Write-Debug "InvokeParam: $($invokeParam | Out-String)"

        # Prepare WhatIf message based on invokeParam
        $whatIfTarget = $invokeParam.Uri
        $whatIfAction = "$($invokeParam.Method) request"
        if ($invokeParam.body) {
            $whatIfAction += " with body: $($invokeParam.body)"
        }

        if ($PSCmdlet.ShouldProcess($whatIfTarget, $whatIfAction)) {
            Write-Verbose "Call Invoke-SMARestMethod $uri" 
            $RestResult = Invoke-SMARestMethod @invokeParam   #|select-object -ExpandProperty domain #Change based on response structure, e.g. if list=true then do domain else do domains or something like that
            # Return object
            if ($RestResult) {
                $returnData = Format-SMARestResult $RestResult -object
                return $returnData
            }
            else {
                Write-Information 'Nothing to return'
            }
        }
        else {
            Write-Verbose "WhatIf: Operation skipped by user"
        }
    }
    end {
    }
}

<#
.SYNOPSIS
    Gets the private and public S/MIME and/or PGP keys of a single domain.
 
.DESCRIPTION
    Retrieves all S/MIME certificates and/or PGP keys of the domain specified
    by -domainName. The result contains the full certificate/key details.
 
    Private key material is only included when a -password is supplied; the
    password is used to protect the exported private key.
 
    This is a read-only query (HTTP GET on /crypto/domain/{domainName}).
 
.PARAMETER onlyMetadata
    If $true, returns only the certificate/key metadata without the actual
    certificate or key material.
 
    Default: $false
 
.PARAMETER password
    SecureString password used to protect the exported PGP key or S/MIME
    private key. When omitted, only public key material is returned.
 
    Example: 'MyPassword123!' | ConvertTo-SecureString -AsPlainText -Force
 
.PARAMETER domainName
    The domain name whose keys should be returned. This parameter is mandatory.
 
.PARAMETER SMAHost
    SEPPmail API hostname. Defaults to the configured value.
 
.PARAMETER SMAPort
    SEPPmail API port. Defaults to the configured value.
 
.PARAMETER SMAVersion
    SEPPmail API version. Defaults to the configured value.
 
.PARAMETER SMACred
    API credentials (PSCredential). Defaults to the configured value.
 
.PARAMETER SMASkipCertCheck
    Skip SSL certificate validation. Use only in test environments.
 
.OUTPUTS
    System.Management.Automation.PSCustomObject
        Returns the S/MIME certificates and/or PGP keys of the domain.
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - Private keys are only included when -password is provided.
 
.LINK
    Find-SMACryptoDomain
 
.LINK
    Get-SMACryptoDomainKey
 
.EXAMPLE
    PS C:\> Get-SMACryptoDomain -domainName 'contoso.com'
    Returns the public S/MIME certificates and PGP keys of 'contoso.com'.
 
.EXAMPLE
    PS C:\> $pw = 'MyPassword123!' | ConvertTo-SecureString -AsPlainText -Force
    PS C:\> Get-SMACryptoDomain -domainName 'contoso.com' -password $pw
    Returns the keys of 'contoso.com' including the password-protected private
    key material.
#>

function Get-SMACryptoDomain
{
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        #region API params
        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Limit the number of results'
        )]
        [SMARestType('query')]
        [bool]$onlyMetadata,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Password with which a PGP key or S/MIME private key will be protected'
        )]
        [SMAParamFilter('password')]
        [SMARestType('query')]
        [secureString]$password,

        [Parameter(
            Mandatory       = $true,
            HelpMessage     = 'limit output to a specific domain name.'
        )]
        [SMARestType('path')]
        [String]$domainName,
        #endregion API params
        
        #region Config parameters block
        [Parameter(Mandatory = $false)]
        [String]$SMAHost = $Script:activeCfg.SMAHost,

        [Parameter(Mandatory = $false)]
        [int]$SMAPort = $Script:activeCfg.SMAPort,

        [Parameter(Mandatory = $false)]
        [String]$SMAVersion = $Script:activeCfg.SMAPIVersion,

        [Parameter(Mandatory=$false)]
        [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred,
        
        [Parameter(Mandatory=$false)]
        [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck
        #endregion
    )

    begin {
        Write-verbose "Verifying required variables from SMA Config"
        if (! (verifyVars -VarList $Script:requiredVarList)) {Throw($missingVarsMessage)} # end if

        Write-verbose "Rest methdod is $restMethod"
        $RestMethod = 'GET'

        Write-Verbose "Creating URL base path"
        $uriPath = "{0}/{1}" -f 'crypto', 'domain'

        Write-Verbose "Adding SMA Params"
        $smaParams=@{
            Host    = $SMAHost
            Port    = $SMAPort
            Version = $SMAVersion
        }
    }
    process {   
        Write-Verbose "Getting parameter arrays for Body, Path and Query from $($myInvocation.Mycommand.Parameters.Values)"
        $paramArrays = Get-SMAParameterArray -ParentPSBoundParameters $PSBoundParameters -ParentInvocation $MyInvocation.MyCommand.Parameters.Values
        Write-Debug "ParamArrays: $($paramArrays | Out-String)"

        Write-verbose "Initializing REST-Data structures"
        [string]$uri                = $null
        [hashtable]$bodyHt          = @{}
        [hashtable]$queryParamHt    = @{}

        Write-verbose "Adding optional path to basepath based on $($paramArrays.Path) parameters"
        if ($paramArrays.Path.Count -gt 0) {
            Foreach ($pathParam in $paramArrays.Path) {
                $uriPath = "{0}/{1}" -f $uriPath, $PSBoundParameters[$pathParam]
            }
        }
        Write-Debug "Final uri path is: $uriPath"

        Write-verbose "Building body HashTable based on $($paramArrays.Body) parameters"
        if ($paramArrays.Body.Count -gt 0) {
            Foreach ($bodyParam in $paramArrays.Body) {
                $bodyHt[$bodyParam] = $PSBoundParameters[$bodyParam]
            }
        }
        if ($paramArrays.password.Count -gt 0) {
            Foreach ($passwordParam in $paramArrays.password) {
                $secureString = $PSBoundParameters[$passwordParam]
                $plainText = ConvertFrom-SecureString $secureString -AsPlainText
                $bodyHt[$passwordParam] = $plainText
            }
        }
        $body = $bodyHt | ConvertTo-Json
        Write-Debug "Body JSON: $body"

        Write-verbose "Building query string based on $($paramArrays.Query) parameters"
        if ($paramArrays.Query.Count -gt 0) {
            Foreach ($queryArrayParam in $paramArrays.Query) {
                $queryParamHt[$queryArrayParam] = $PSBoundParameters[$queryArrayParam]
            }
        }
        Write-Debug "QueryParams: $($queryParamHt | Out-String)"

        Write-Verbose "Constructing full URI based on parameters"
        if ($queryParamHt) {
            $uri = New-SMAQueryString -uriPath $uriPath -qParam $queryParamHt @smaParams
        }
        else {
            $uri = New-SMAQueryString -uriPath $uriPath @smaParams
        }
        Write-Debug "Final URI: $uri"

        Write-verbose "Crafting Invokeparam for Invoke-SMARestMethod"
        $invokeParam = @{
                      Uri = $uri 
                   Method = $RestMethod
                     Cred = $SMACred
                     body = $body
            SkipCertCheck = $SMASkipCertCheck
        }
        Write-Debug "InvokeParam: $($invokeParam | Out-String)"

        # Prepare WhatIf message based on invokeParam
        $whatIfTarget = $invokeParam.Uri
        $whatIfAction = "$($invokeParam.Method) request"
        if ($invokeParam.body) {
            $whatIfAction += " with body: $($invokeParam.body)"
        }

        if ($PSCmdlet.ShouldProcess($whatIfTarget, $whatIfAction)) {
            Write-Verbose "Call Invoke-SMARestMethod $uri" 
            $RestResult = Invoke-SMARestMethod @invokeParam
            # Return object
            if ($RestResult) {
                $returnData = Format-SMARestResult $RestResult -object
                return $returnData
            }
            else {
                Write-Information 'Nothing to return'
            }
        }
        else {
            Write-Verbose "WhatIf: Operation skipped by user"
        }
    }
    end {
    }
}

<#
.SYNOPSIS
    Gets a single domain S/MIME certificate or PGP key by serial / key ID.
 
.DESCRIPTION
    Retrieves one specific S/MIME certificate or PGP key of the domain
    specified by -domainName, identified by its serial number (S/MIME) or
    key ID (PGP) via -serial_or_keyid.
 
    Private key material is only included when a -password is supplied; the
    password is used to protect the exported private key.
 
    This is a read-only query
    (HTTP GET on /crypto/domain/{domainName}/{serial_or_keyid}).
 
.PARAMETER onlyMetadata
    If $true, returns only the certificate/key metadata without the actual
    certificate or key material.
 
    Default: $false
 
.PARAMETER hin
    If $true, the key is treated as a HIN (Health Info Net) S/MIME certificate.
 
.PARAMETER password
    SecureString password used to protect the exported PGP key or S/MIME
    private key. When omitted, only public key material is returned.
 
    Example: 'MyPassword123!' | ConvertTo-SecureString -AsPlainText -Force
 
.PARAMETER domainName
    The domain name the key belongs to. This parameter is mandatory.
 
.PARAMETER serial_or_keyid
    The S/MIME certificate serial number or the PGP key ID identifying the key
    to return. This parameter is mandatory.
 
.PARAMETER SMAHost
    SEPPmail API hostname. Defaults to the configured value.
 
.PARAMETER SMAPort
    SEPPmail API port. Defaults to the configured value.
 
.PARAMETER SMAVersion
    SEPPmail API version. Defaults to the configured value.
 
.PARAMETER SMACred
    API credentials (PSCredential). Defaults to the configured value.
 
.PARAMETER SMASkipCertCheck
    Skip SSL certificate validation. Use only in test environments.
 
.OUTPUTS
    System.Management.Automation.PSCustomObject
        Returns the requested S/MIME certificate or PGP key.
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - Use Get-SMACryptoDomain or Find-SMACryptoDomain -list to discover the
      available serial numbers / key IDs.
    - Private keys are only included when -password is provided.
 
.LINK
    Find-SMACryptoDomain
 
.LINK
    Get-SMACryptoDomain
 
.EXAMPLE
    PS C:\> Get-SMACryptoDomainKey -domainName 'contoso.com' -serial_or_keyid '1A2B3C4D'
    Returns the certificate/key with serial number / key ID '1A2B3C4D' of
    'contoso.com'.
 
.EXAMPLE
    PS C:\> $pw = 'MyPassword123!' | ConvertTo-SecureString -AsPlainText -Force
    PS C:\> Get-SMACryptoDomainKey -domainName 'contoso.com' -serial_or_keyid '1A2B3C4D' -password $pw
    Returns the key including the password-protected private key material.
#>

function Get-SMACryptoDomainKey
{
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        #region API params
        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Limit the number of results'
        )]
        [SMARestType('query')]
        [bool]$onlyMetadata,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'If set to true an add call will only import the public key material.'
        )]
        [SMARestType('query')]
        [bool]$hin,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Password with which a PGP key or S/MIME private key will be protected'
        )]
        [SMAParamFilter('password')]
        [SMARestType('query')]
        [secureString]$password,

        [Parameter(
            Mandatory       = $true,
            HelpMessage     = 'specify domain name.'
        )]
        [SMARestType('path')]
        [String]$domainName,

        [Parameter(
            Mandatory       = $true,
            HelpMessage     = 'specifies the S/MIME certificates serial number or the PGP keys key ID'
        )]
        [SMARestType('path')]
        [String]$serial_or_keyid,

        #endregion API params
        
        #region Config parameters block
        [Parameter(Mandatory = $false)]
        [String]$SMAHost = $Script:activeCfg.SMAHost,

        [Parameter(Mandatory = $false)]
        [int]$SMAPort = $Script:activeCfg.SMAPort,

        [Parameter(Mandatory = $false)]
        [String]$SMAVersion = $Script:activeCfg.SMAPIVersion,

        [Parameter(Mandatory=$false)]
        [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred,
        
        [Parameter(Mandatory=$false)]
        [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck
        #endregion
    )

    begin {
        Write-verbose "Verifying required variables from SMA Config"
        if (! (verifyVars -VarList $Script:requiredVarList)) {Throw($missingVarsMessage)} # end if

        Write-verbose "Rest methdod is $restMethod"
        $RestMethod = 'GET'

        Write-Verbose "Creating URL base path"
        $uriPath = "{0}/{1}" -f 'crypto', 'domain'

        Write-Verbose "Adding SMA Params"
        $smaParams=@{
            Host    = $SMAHost
            Port    = $SMAPort
            Version = $SMAVersion
        }
    }
    process {   
        Write-Verbose "Getting parameter arrays for Body, Path and Query from $($myInvocation.Mycommand.Parameters.Values)"
        $paramArrays = Get-SMAParameterArray -ParentPSBoundParameters $PSBoundParameters -ParentInvocation $MyInvocation.MyCommand.Parameters.Values
        Write-Debug "ParamArrays: $($paramArrays | Out-String)"

        Write-verbose "Initializing REST-Data structures"
        [string]$uri                = $null
        [hashtable]$bodyHt          = @{}
        [hashtable]$queryParamHt    = @{}

        Write-verbose "Adding optional path to basepath based on $($paramArrays.Path) parameters"
        if ($paramArrays.Path.Count -gt 0) {
            Foreach ($pathParam in $paramArrays.Path) {
                $uriPath = "{0}/{1}" -f $uriPath, $PSBoundParameters[$pathParam]
            }
        }
        Write-Debug "Final uri path is: $uriPath"

        Write-verbose "Building body HashTable based on $($paramArrays.Body) parameters"
        if ($paramArrays.Body.Count -gt 0) {
            Foreach ($bodyParam in $paramArrays.Body) {
                $bodyHt[$bodyParam] = $PSBoundParameters[$bodyParam]
            }
        }
        if ($paramArrays.password.Count -gt 0) {
            Foreach ($passwordParam in $paramArrays.password) {
                $secureString = $PSBoundParameters[$passwordParam]
                $plainText = ConvertFrom-SecureString $secureString -AsPlainText
                $bodyHt[$passwordParam] = $plainText
            }
        }
        $body = $bodyHt | ConvertTo-Json
        Write-Debug "Body JSON: $body"

        Write-verbose "Building query string based on $($paramArrays.Query) parameters"
        if ($paramArrays.Query.Count -gt 0) {
            Foreach ($queryArrayParam in $paramArrays.Query) {
                $queryParamHt[$queryArrayParam] = $PSBoundParameters[$queryArrayParam]
            }
        }
        Write-Debug "QueryParams: $($queryParamHt | Out-String)"

        Write-Verbose "Constructing full URI based on parameters"
        if ($queryParamHt) {
            $uri = New-SMAQueryString -uriPath $uriPath -qParam $queryParamHt @smaParams
        }
        else {
            $uri = New-SMAQueryString -uriPath $uriPath @smaParams
        }
        Write-Debug "Final URI: $uri"

        Write-verbose "Crafting Invokeparam for Invoke-SMARestMethod"
        $invokeParam = @{
                      Uri = $uri 
                   Method = $RestMethod
                     Cred = $SMACred
                     body = $body
            SkipCertCheck = $SMASkipCertCheck
        }
        Write-Debug "InvokeParam: $($invokeParam | Out-String)"

        # Prepare WhatIf message based on invokeParam
        $whatIfTarget = $invokeParam.Uri
        $whatIfAction = "$($invokeParam.Method) request"
        if ($invokeParam.body) {
            $whatIfAction += " with body: $($invokeParam.body)"
        }

        if ($PSCmdlet.ShouldProcess($whatIfTarget, $whatIfAction)) {
            Write-Verbose "Call Invoke-SMARestMethod $uri" 
            $RestResult = Invoke-SMARestMethod @invokeParam
            # Return object
            if ($RestResult) {
                $returnData = Format-SMARestResult $RestResult -object
                return $returnData
            }
            else {
                Write-Information 'Nothing to return'
            }
        }
        else {
            Write-Verbose "WhatIf: Operation skipped by user"
        }
    }
    end {
    }
}

# SIG # Begin signature block
# MIIVyAYJKoZIhvcNAQcCoIIVuTCCFbUCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAIuXmelzXYrMgq
# F/F+QT5Pn0LtU8O/Qw9JnzoZkYQSbKCCEgQwggVvMIIEV6ADAgECAhBI/JO0YFWU
# jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI
# DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM
# EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy
# dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG
# EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv
# IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s
# hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD
# J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7
# P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme
# me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz
# T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q
# RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz
# mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc
# QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T
# OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/
# AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID
# AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD
# VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV
# HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE
# VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v
# ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE
# KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI
# hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF
# OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC
# J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ
# pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl
# d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH
# +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M
# UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD
# VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv
# ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5
# NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp
# BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G
# CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI
# ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV
# DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3
# 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw
# mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm
# +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe
# dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4
# 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM
# dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY
# MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU
# pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV
# HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG
# A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1
# YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG
# AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl
# U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0
# aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh
# w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd
# OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj
# cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc
# WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO
# hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs
# zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7
# 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J
# KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH
# j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2
# Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/
# L9Uo2bC5a4CH2RwwggZvMIIE16ADAgECAhBIqMP3CCLHOHtOKuaWNyeFMA0GCSqG
# SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0
# ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw
# HhcNMjYwNDE1MDAwMDAwWhcNMjcwNzE0MjM1OTU5WjBmMQswCQYDVQQGEwJERTEP
# MA0GA1UECAwGQmF5ZXJuMSIwIAYDVQQKDBlTRVBQbWFpbCBEZXV0c2NobGFuZCBH
# bWJIMSIwIAYDVQQDDBlTRVBQbWFpbCBEZXV0c2NobGFuZCBHbWJIMIICIjANBgkq
# hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvAFzE8MbJpvQt+IdIh1M+bKYsJBFDk4b
# 9ySe25IrCi00B9o5XmQtIw42MqyIKbUq1tDARtp9KTQedEP9W+rflAF2l+0Z046J
# kiqumU9/enbqWLDyln1aS/p7HOgwZFMhnsR9zH0MfFckiklUmkzJO+vmzYAK7ZmD
# xajNLJs0gkGRU2/BecAx/TSvLXMaKONsKZCyMKQCnwo1mCY/tFl5EgUz7YQFrPOR
# BQGfQke/hkdBfQDqNRsi/J6+KhJWc6LvgQihdRg/INQbQsTxlow18NWvyFsjjueH
# 7kG6HR4YKfbv07xgrsIh8xvq9ZJ1SBhLXmkg4SdoQGASjqR6o3keAX+bDRFf+hml
# WWJp/FqVHR5QomF3vbK2/bbz4jAclYSPx/sPasNJ0YnKFkgmowZ7Ysa0KA0/egBg
# tI4gJ+8V7zrqIVEG3rMQh9KCdMnJqP2aM9o4gUzQvE1M4x606liX9EWwdLLS+fe7
# 9o+Fzo5oH4wBE/En6hQQkzseHHu+TXCDd6zUUZ/PlTK0gTaDIRXt6UzPNqJ4RiRC
# W2pNFcPt078qqVTuwKUXoE4ufxGgXKFrZlCYST/9eG1TnW2oq19nz8A333GCsL3g
# poNIKvfmDyGMMNzvx2aeqn2v6e75z8kH19iGSNZ51xT+WgS9F1aIvjz08/T7XAv7
# iDPF1/gPIp8CAwEAAaOCAakwggGlMB8GA1UdIwQYMBaAFA8qyyCHKLjsb0iuK1Sm
# KaoXpM0MMB0GA1UdDgQWBBS30/Tq+alF3j2BY5up8n5zpAU23DAOBgNVHQ8BAf8E
# BAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzBKBgNVHSAE
# QzBBMDUGDCsGAQQBsjEBAgEDAjAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3Rp
# Z28uY29tL0NQUzAIBgZngQwBBAEwSQYDVR0fBEIwQDA+oDygOoY4aHR0cDovL2Ny
# bC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcmww
# eQYIKwYBBQUHAQEEbTBrMEQGCCsGAQUFBzAChjhodHRwOi8vY3J0LnNlY3RpZ28u
# Y29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNydDAjBggrBgEFBQcw
# AYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wHgYDVR0RBBcwFYETc3VwcG9ydEBz
# ZXBwbWFpbC5jaDANBgkqhkiG9w0BAQwFAAOCAYEAi7fmb5UYoemWG3CC4K2UZWVr
# R6GOfi8gbJKgjPbKO4zrCrU/x6cOdyp6scKZfUEGFDf8KH6pP4pAQv1Hsbi49gU2
# kxoUWLlCiipn05qJY663DHx9hlStej/ZdEatou0wyCDiG5xD7kmG+1t6iLyyCBgE
# B88tJpzTjI61qXmBTS/FGEOAsB4SDEW1ngA7bc5FOv4IUKA43hp8M+N3GeYFzDqw
# JELYEfVVYheBW3o7q4VrCdfFEuaQihOtvfDfYpP6ANgekNn8HdsMT8rx9D1I50Rl
# i/qQFo2BOuPyb2SIQPzJvCs5wgi5qgp1nHiN6igumu2Cz7BmGjOazGUgCSUY5Qwy
# E8+F+R2tVM+2O15rfX01+e56ZfojBEiEjMwfPHs3fa3V3gokWWNwUMkton/v0R/n
# l2zjmOr2okohOINZEDh9frg21zUCN5ZD8Y4zQWuiJLCvvvBZs0JR4c9xl2k2wtw/
# QLPhGU69zM3smGpRoLE8M6zvUvSU7jXjvefazUniMYIDGjCCAxYCAQEwaDBUMQsw
# CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJT
# ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2AhBIqMP3CCLHOHtOKuaW
# NyeFMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG
# AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIHjHVkWX6L0UApnNp3hnQQim2fgJg+Xk
# O7ANK7VswNryMA0GCSqGSIb3DQEBAQUABIICAFlaV8WPEH0vyQ0gSU440kuxOn1W
# hrQJIBZ6rmgDJnx03BdoO2Ca28zsVJk+rhURlY5YW97/lMLLrsb5Fo632ImzYOP3
# h8e4bo1DDTsr8AqNzViTqL+uH4umaQoTTmnCJkTvAtXL8Yfv11LEj18zClOfuQGA
# ueseo+O5Ako+OgYW5bbNPHrhxBdx+rDjdL2gDaKfDAGkRzIsSK3cITFYPTigJk4V
# 3j0m6SaYayhcRbw4lje3ZQE7wDHNO3G0ylrzr9g2g5ygHaCcShz5RlpxtpfdtF+1
# jzh4Fty7RlqUVvQtlIOGbjqy2nEj1KB7Gnv0b10msCYI72qBadEZyzyPcoURqYtp
# JTe80BLC5uQihLeiL+rBui1ebOFCdTXIAf3RLQCS52yiJJ9MhmR4LHrR4lNDrMD9
# HIfpcmuYg5uvaHD7KzOwcm/kkaSIM2JvaBBmFv9MDo9pftpaOmJL7/P7ZVpSrYpt
# jV5rOdmdeOitwqUOSE6O2dITMOXWeR7n+6+HP0BdmmNp8kZNlL7dsZNqa4xlmJ7Y
# iX2jdQguuJyLAqnnMcB7tCKmte8OF9Ka5oWU7HLmMfHlLqCcihihEOoKxDwMR5vv
# skeEtKfyOCRaeuy/LoKI6Iy8lLJQkWK7g38569UeDOZNvwJUqLkfS4sqXmh4ZZIR
# kSf0tUhY2EAHHKPb
# SIG # End signature block