Public/SEPPmailAPI-SSL.ps1

<#
.SYNOPSIS
    Gets the active SSL/TLS certificate of the SEPPmail appliance.
 
.DESCRIPTION
    Retrieves the currently installed SSL/TLS (web/GUI) certificate of the
    appliance via a read-only query (HTTP GET on /ssl). The result is returned
    as the native JSON representation describing the certificate.
 
.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 active SSL/TLS certificate information.
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
 
.LINK
    New-SMASsl
 
.LINK
    Import-SMASsl
 
.LINK
    Export-SMASsl
 
.EXAMPLE
    PS C:\> Get-SMASsl
    Returns the appliance's active SSL/TLS certificate.
#>


function Get-SMASsl
{
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        #region API params
        #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}" -f 'ssl'

        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 -nativeJson
                return $returnData
            }
            else {
                Write-Information 'Nothing to return'
            }
        }
        else {
            Write-Verbose "WhatIf: Operation skipped by user"
        }
    }
    end {
    }
}

<#
.SYNOPSIS
    Exports the appliance SSL/TLS certificate as a password-protected PKCS#12.
 
.DESCRIPTION
    Exports the currently installed SSL/TLS certificate of the appliance,
    including its private key, as a PKCS#12 container protected by the supplied
    password (HTTP PUT on /ssl).
 
.PARAMETER password
    SecureString password used to protect the exported PKCS#12 container.
    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 exported certificate data (native JSON).
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - Supports -WhatIf and -Confirm.
 
.LINK
    Import-SMASsl
 
.LINK
    Get-SMASsl
 
.EXAMPLE
    PS C:\> $pw = 'MyPassword123!' | ConvertTo-SecureString -AsPlainText -Force
    PS C:\> Export-SMASsl -password $pw
    Exports the appliance SSL certificate as a PKCS#12 protected by the password.
#>


function Export-SMASsl #TODO: m.M. nach ein Bug in der API - ins WIKI geschrieben
{
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        #region API params
        [Parameter(
            Mandatory       = $true,
            HelpMessage     = 'Certificate password'
        )]
        [SMARestType('body')]
        [SMAParamFilter('password')]
        [secureString]$password,

        #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 = 'PUT'

        Write-Verbose "Creating URL base path"
        $uriPath = "{0}" -f 'ssl' 

        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 -nativeJson #TODO: object, StringPos3, StringPos2to4, StringPos0to9, objectArray, nattiveJson
                return $returnData
            }
            else {
                Write-Information 'Nothing to return'
            }
        }
        else {
            Write-Verbose "WhatIf: Operation skipped by user"
        }
    }
    end {
    }
}

<#
.SYNOPSIS
    Imports an SSL/TLS certificate from a PKCS#12 (.p12/.pfx) file.
 
.DESCRIPTION
    Imports a new SSL/TLS (web/GUI) certificate including its private key onto
    the appliance from a PKCS#12 file (HTTP POST on /ssl). The file is
    converted to Base64 and the private key is unlocked with -pkcs12Password.
 
.PARAMETER pkcs12File
    Path to the PKCS#12 (.p12/.pfx) file to import. The file must exist.
    Mandatory. Aliases: Path, FullName
 
.PARAMETER pkcs12Password
    SecureString password protecting the PKCS#12 file. 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 API result of the import operation.
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - Supports -WhatIf and -Confirm.
 
.LINK
    Export-SMASsl
 
.LINK
    New-SMASsl
 
.LINK
    Push-SMASslToCluster
 
.EXAMPLE
    PS C:\> $pw = 'MyPassword123!' | ConvertTo-SecureString -AsPlainText -Force
    PS C:\> Import-SMASsl -pkcs12File 'C:\certs\appliance.pfx' -pkcs12Password $pw
    Imports the certificate from the given PFX file onto the appliance.
#>


function Import-SMASsl
{
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        #region API params
        [Parameter(
            Mandatory       = $true,
            HelpMessage     = 'Path to PKCS#12 (.p12/.pfx) file'
        )]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ -PathType Leaf })]
        [Alias('Path', 'FullName')]
        [SMARestType('body')]
        [System.IO.FileInfo]$pkcs12File, 

        [Parameter(
            Mandatory       = $true,
            HelpMessage     = 'hostname shown in System / Hostname'
        )]
        [SMARestType('custom')]
        [SMAParamFilter('password')]
        [secureString]$pkcs12Password, 

        #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 = 'POST'

        Write-Verbose "Creating URL base path"
        $uriPath = "{0}" -f 'ssl'

        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.password.Count -gt 0) {
            Foreach ($passwordParam in $paramArrays.password) {
                $secureString = $PSBoundParameters[$passwordParam]
                $plainText = ConvertFrom-SecureString $secureString -AsPlainText
                $bodyHt[$passwordParam] = $plainText
            }
        }
        if ($paramArrays.Body.Count -gt 0) {
            Foreach ($bodyParam in $paramArrays.Body) {
                $value = $PSBoundParameters[$bodyParam]
                if ($value -is [System.IO.FileInfo]) {
                    $value = ConvertTo-PfxBase64 -PfxPath $value.FullName -Password $PSBoundParameters[$passwordParam]
                }
                $bodyHt[$bodyParam] = $value
            }
        }
        $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 -StringPos0to3
                return $returnData
            }
            else {
                Write-Information 'Nothing to return'
            }
        }
        else {
            Write-Verbose "WhatIf: Operation skipped by user"
        }
    }
    end {
    }
}


<#
.SYNOPSIS
    Transfers the appliance SSL/TLS certificate to the cluster members.
 
.DESCRIPTION
    Pushes the currently installed SSL/TLS certificate to the other members of
    the SEPPmail cluster (HTTP POST on /ssl/transfer_to_cluster). Use this
    after importing or generating a new certificate so all cluster nodes use
    the same certificate.
 
.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 API result of the transfer operation.
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - Supports -WhatIf and -Confirm.
 
.LINK
    Import-SMASsl
 
.LINK
    New-SMASsl
 
.EXAMPLE
    PS C:\> Push-SMASslToCluster
    Transfers the active SSL certificate to all cluster members.
#>

function Push-SMASslToCluster
{
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        #region API params

        #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 = 'POST'

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

        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-Debug "Body JSON: $body"

        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 -StringPos0to3
                return $returnData
            }
            else {
                Write-Information 'Nothing to return'
            }
        }
        else {
            Write-Verbose "WhatIf: Operation skipped by user"
        }
    }
    end {
    }
}

<#
.SYNOPSIS
    Generates a new SSL/TLS certificate or certificate signing request (CSR).
 
.DESCRIPTION
    Creates a new SSL/TLS key pair on the appliance and, depending on
    -signature, either a self-signed certificate or a certificate signing
    request (CSR) for submission to a certificate authority
    (HTTP POST on /ssl/generate). The certificate subject is built from the
    supplied distinguished-name parts.
 
.PARAMETER kesSize
    Key size (in bits) of the generated key pair.
 
    Default: 4096
 
.PARAMETER validity
    Validity period of the certificate in days.
 
    Default: 398
 
.PARAMETER signature
    Signature / output type. 'csr' produces a certificate signing request.
 
    Default: 'csr'
 
.PARAMETER CN
    Common Name (CN) of the certificate subject (e.g. the appliance FQDN).
    Mandatory.
 
.PARAMETER emailAddress
    E-mail address of the certificate subject. Mandatory.
 
.PARAMETER OU
    Organizational Unit (OU) of the certificate subject.
 
.PARAMETER O
    Organization (O) of the certificate subject.
 
.PARAMETER L
    Locality (L) of the certificate subject.
 
.PARAMETER ST
    State or Province (ST) of the certificate subject.
 
.PARAMETER C
    Country (C) of the certificate subject (two-letter country code).
 
.PARAMETER SAN
    Subject Alternative Names (SAN). Array of additional host names.
 
.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 generated certificate or CSR.
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - Supports -WhatIf and -Confirm.
 
.LINK
    Import-SMASsl
 
.LINK
    Get-SMASsl
 
.EXAMPLE
    PS C:\> New-SMASsl -CN 'mail.contoso.com' -emailAddress 'admin@contoso.com' -O 'Contoso' -C 'CH'
    Generates a CSR for 'mail.contoso.com' with a 4096-bit key.
 
.EXAMPLE
    PS C:\> New-SMASsl -CN 'mail.contoso.com' -emailAddress 'admin@contoso.com' -SAN 'autodiscover.contoso.com','smtp.contoso.com'
    Generates a CSR including Subject Alternative Names.
#>

function New-SMASsl
{
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        #region API params
        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Key size for the new SSL certificate'
        )]
        [SMARestType('body')]
        [Int]$kesSize = 4096,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Key size for the new SSL certificate'
        )]
        [SMARestType('body')]
        [Int]$validity = 398,


        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Signature type'
        )]
        [SMARestType('body')]
        [String]$signature = 'csr',

        [Parameter(
            Mandatory       = $true,
            HelpMessage     = 'Common Name'
        )]
        [SMARestType('custom')]
        [String]$CN,

        [Parameter(
            Mandatory       = $true,
            HelpMessage     = 'email Address'
        )]
        [SMARestType('custom')]
        [String]$emailAddress,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Organization Unit' 
        )]
        [SMARestType('custom')]
        [String]$OU,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Organization'
        )]
        [SMARestType('custom')]
        [String]$O,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Locality'
        )]
        [SMARestType('custom')]
        [String]$L,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'State or Province Name'
        )]
        [SMARestType('custom')]
        [String]$ST,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Country Name'
        )]
        [SMARestType('custom')]
        [String]$C,

        [Parameter(
            Mandatory       = $false,
            HelpMessage     = 'Subject Alternative Names'
        )]
        [SMARestType('custom')]
        [String[]]$SAN,

        #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 = 'POST'

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

        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]
            }
        }

        Write-verbose "Adding default values for keySize, validity and signature if not provided by user"
        # Ensure default body params are always included
        if (-not $PSBoundParameters.ContainsKey('kesSize')) {
            $bodyHt['kesSize'] = $kesSize
        }
        if (-not $PSBoundParameters.ContainsKey('validity')) {
            $bodyHt['validity'] = $validity
        }
        if (-not $PSBoundParameters.ContainsKey('signature')) {
            $bodyHt['signature'] = $signature
        }

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

        Write-verbose "Building custom nested objects for forwarding and tls"
        if ($PSBoundParameters.ContainsKey('CN') -or 
            $PSBoundParameters.ContainsKey('emailAddress') -or 
            $PSBoundParameters.ContainsKey('OU') -or 
            $PSBoundParameters.ContainsKey('O') -or 
            $PSBoundParameters.ContainsKey('L') -or 
            $PSBoundParameters.ContainsKey('ST') -or 
            $PSBoundParameters.ContainsKey('C') -or 
            $PSBoundParameters.ContainsKey('SAN')) {
            $certAttribute = [ordered]@{}
            if ($PSBoundParameters.ContainsKey('CN')) {
                $certAttribute['CN'] = $CN
            }
            if ($PSBoundParameters.ContainsKey('emailAddress')) {
                $certAttribute['emailAddress'] = $emailAddress
            }
            if ($PSBoundParameters.ContainsKey('OU')) {
                $certAttribute['OU'] = $OU
            }
            if ($PSBoundParameters.ContainsKey('O')) {
                $certAttribute['O'] = $O
            }
            if ($PSBoundParameters.ContainsKey('L')) {
                $certAttribute['L'] = $L
            }
            if ($PSBoundParameters.ContainsKey('ST')) {
                $certAttribute['ST'] = $ST
            }
            if ($PSBoundParameters.ContainsKey('C')) {
                $certAttribute['C'] = $C
            }
            if ($PSBoundParameters.ContainsKey('SAN')) {
                $certAttribute['SAN'] = $SAN
            }
            $bodyHt['certAttribute'] = $certAttribute
        }

        $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 -nativeJson
                return $returnData
            }
            else {
                Write-Information 'Nothing to return'
            }
        }
        else {
            Write-Verbose "WhatIf: Operation skipped by user"
        }
    }
    end {
    }
}

# SIG # Begin signature block
# MIIVyAYJKoZIhvcNAQcCoIIVuTCCFbUCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDbC84bRgOrZgZN
# zDa1EP9oC+hZ2YNHaZiEMstHEmTekaCCEgQwggVvMIIEV6ADAgECAhBI/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
# AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIPCL5bBVKDON9N7DYJDhLaq7jBmynNFz
# ouhevub0VfvoMA0GCSqGSIb3DQEBAQUABIICAIjdfT4STlX1oq15FLJnphlEqO0v
# zPvzybHfrrkT8oYqtGg0k9/0k5+TnpWxqPKrBHTexv5ybaDtJVUSH+m5DQARgbpB
# 5HUQE6tCDciaANNUj+w/JwC+SHzjuP1dNfOQTO2GJ87eMXGl5loT4Wm2pKfqq5Ix
# UmJ/ZG+VbDDXrAPKCbcd8oL3InUycl+cEcZvNa78KXqy5PPW9sjxs8jbaFB18PGJ
# yt0QSKRLNa4BJSHfImkQhpEDGeDUGy/bMkMzLOuPRHyKYCCaH/Kjb3dDA/JJL3/z
# wmDASR61M8i7TWc1Qh8LWpGW+pGYec9a+GQP2xvzo8igybAODupAGfwAOEAGjci4
# uFbPCdHJF5yHOTOU16UpfW3Zmp7nqydb4cGJhmMgkhogg/emk1/LR1QnQXaXMHX0
# k2zeFeMpYza+XVySAFOvDUI0M+ifImTpXzURTgN7Ev4BrwDCQGH/xvLWS4LOUDmj
# Ln5vxX4++WgDEMmda1/XEoRjSYrasd8ym3EyKCIceIpDnroDZ8JivarvJ+vaaz6l
# gSAnYsv8K+oWFj20LloGRLp9vupBajYrDiaetSdzn/zqa3RErcFZJdw7bQioFxkk
# HBmdygX8rGtf3cWPIU6qQmizYfbisjQrRfbn11c1/WWkWtWyaicvvnm0S42Pvvx3
# KJ/qk29/PyW3W7yX
# SIG # End signature block