Server/Install-CertificationAuthority.ps1

function Install-CertificationAuthority {
<#
.ExternalHelp PSPKI.Help.xml
#>

[OutputType('SysadminsLV.PKI.Utils.IServiceOperationResult')]
[CmdletBinding(
    DefaultParameterSetName = 'NewKeySet',
    ConfirmImpact = 'High',
    SupportsShouldProcess = $true
)]
    param(
        [Parameter(ParameterSetName = 'NewKeySet')]
        [string]$CAName,
        [Parameter(ParameterSetName = 'NewKeySet')]
        [string]$CADNSuffix,
        [Parameter(ParameterSetName = 'NewKeySet')]
        [ValidateSet("Standalone Root","Standalone Subordinate","Enterprise Root","Enterprise Subordinate")]
        [string]$CAType,
        [Parameter(ParameterSetName = 'NewKeySet')]
        [string]$ParentCA,
        [Parameter(ParameterSetName = 'NewKeySet')]
        [string]$CSP,
        [Parameter(ParameterSetName = 'NewKeySet')]
        [int]$KeyLength,
        [Parameter(ParameterSetName = 'NewKeySet')]
        [string]$HashAlgorithm,
        [Parameter(ParameterSetName = 'NewKeySet')]
        [int]$ValidForYears = 5,
        [Parameter(ParameterSetName = 'NewKeySet')]
        [string]$RequestFileName,
        [Parameter(Mandatory = $true, ParameterSetName = 'PFXKeySet')]
        [IO.FileInfo]$CACertFile,
        [Parameter(Mandatory = $true, ParameterSetName = 'PFXKeySet')]
        [Security.SecureString]$Password,
        [Parameter(Mandatory = $true, ParameterSetName = 'ExistingKeySet')]
        [string]$Thumbprint,
        [string]$DBDirectory = $(Join-Path $env:SystemRoot "System32\CertLog"),
        [string]$LogDirectory = $(Join-Path $env:SystemRoot "System32\CertLog"),
        [switch]$OverwriteExisting,
        [switch]$AllowCSPInteraction,
        [switch]$Force
    )

#region OS and existing CA checking
    # check if script running on Windows Server 2008 or Windows Server 2008 R2
    $OS = Get-WmiObject Win32_OperatingSystem -Property ProductType
    if ($OSVersion.Major -lt 6) {
        Write-Error -Category NotImplemented -ErrorId "NotSupportedException" `
        -Message "Windows XP, Windows Server 2003 and Windows Server 2003 R2 are not supported!"
        return
    }
    if ($OS.ProductType -eq 1) {
        Write-Error -Category NotImplemented -ErrorId "NotSupportedException" `
        -Message "Client operating systems are not supported!"
        return
    }
    $CertConfig = New-Object -ComObject CertificateAuthority.Config
    try {$ExistingDetected = $CertConfig.GetConfig(3)}
    catch {}
    if ($ExistingDetected) {
        Write-Error -Category ResourceExists -ErrorId "ResourceExistsException" `
        -Message "Certificate Services are already installed on this computer. Only one Certification Authority instance per computer is supported."
        return
    }
    
#endregion

#region Binaries checking and installation if necessary
    if ($OSVersion.Major -eq 6 -and $OSVersion.Minor -eq 0) {
        cmd /c "servermanagercmd -install AD-Certificate 2> null" | Out-Null
    } else {
        try {Import-Module ServerManager -ErrorAction Stop}
        catch {
            ocsetup 'ServerManager-PSH-Cmdlets' /quiet | Out-Null
            Start-Sleep 1
            Import-Module ServerManager -ErrorAction Stop
        }
        $status = (Get-WindowsFeature -Name AD-Certificate).Installed
        # if still no, install binaries, otherwise do nothing
        if (!$status) {$retn = Add-WindowsFeature -Name AD-Certificate -ErrorAction Stop
            if (!$retn.Success) {
                Write-Error -Category NotInstalled -ErrorId "NotInstalledException" `
                -Message "Unable to install ADCS installation packages due of the following error: $($retn.breakCode)"
                return
            }
        }
    }
    try {$CASetup = New-Object -ComObject CertOCM.CertSrvSetup.1}
    catch {
        Write-Error -Category NotImplemented -ErrorId "NotImplementedException" `
        -Message "Unable to load necessary interfaces. Your Windows Server operating system is not supported!"
        return
    }
    # initialize setup binaries
    try {$CASetup.InitializeDefaults($true, $false)}
    catch {
        Write-Error -Category InvalidArgument -ErrorId ParameterIncorrectException `
        -ErrorAction Stop -Message "Cannot initialize setup binaries!"
    }
#endregion

#region Property enums
    $CATypesByName = @{"Enterprise Root" = 0; "Enterprise Subordinate" = 1; "Standalone Root" = 3; "Standalone Subordinate" = 4}
    $CATypesByVal = @{}
    $CATypesByName.keys | ForEach-Object {$CATypesByVal.Add($CATypesByName[$_],$_)}
    $CAPRopertyByName = @{"CAType"=0;"CAKeyInfo"=1;"Interactive"=2;"ValidityPeriodUnits"=5;
        "ValidityPeriod"=6;"ExpirationDate"=7;"PreserveDataBase"=8;"DBDirectory"=9;"Logdirectory"=10;
        "ParentCAMachine"=12;"ParentCAName"=13;"RequestFile"=14;"WebCAMachine"=15;"WebCAName"=16
    }
    $CAPRopertyByVal = @{}
    $CAPRopertyByName.keys | ForEach-Object {$CAPRopertyByVal.Add($CAPRopertyByName[$_],$_)}
    $ValidityUnitsByName = @{"years" = 6}
    $ValidityUnitsByVal = @{6 = "years"}
#endregion
    $ofs = ", "
#region Key set processing functions

#region NewKeySet
function NewKeySet ($CAName, $CADNSuffix, $CAType, $ParentCA, $CSP, $KeyLength, $HashAlgorithm, $ValidForYears, $RequestFileName, $AllowCSPInteraction) {

#region CSP, key length and hashing algorithm verification
    $CAKey = $CASetup.GetCASetupProperty(1)
    if ($AllowCSPInteraction) {$CASetup.SetCASetupProperty(0x2,$true)}
    if ($CSP -ne "") {
        if ($CASetup.GetProviderNameList() -notcontains $CSP) {
            # TODO add available CSP list
            Write-Error -Category InvalidArgument -ErrorId "InvalidCryptographicServiceProviderException" `
            -ErrorAction Stop -Message "Specified CSP '$CSP' is not valid!"
        } else {
            $CAKey.ProviderName = $CSP
        }
    } else {
        $CAKey.ProviderName = "RSA#Microsoft Software Key Storage Provider"
    }
    if ($KeyLength -ne 0) {
        if ($CASetup.GetKeyLengthList($CAKey.ProviderName).Length -eq 1) {
            $CAKey.Length = $CASetup.GetKeyLengthList($CAKey.ProviderName)[0]
        } else {
            if ($CASetup.GetKeyLengthList($CAKey.ProviderName) -notcontains $KeyLength) {
                Write-Error -Category InvalidArgument -ErrorId "InvalidKeyLengthException" `
                -ErrorAction Stop -Message @"
The specified key length '$KeyLength' is not supported by the selected CSP '$($CAKey.ProviderName)' The following
key lengths are supported by this CSP: $($CASetup.GetKeyLengthList($CAKey.ProviderName))
"@

            }
            $CAKey.Length = $KeyLength
        }
    }
    if ($HashAlgorithm -ne "") {
        if ($CASetup.GetHashAlgorithmList($CAKey.ProviderName) -notcontains $HashAlgorithm) {
                Write-Error -Category InvalidArgument -ErrorId "InvalidHashAlgorithmException" `
                -ErrorAction Stop -Message @"
The specified hash algorithm is not supported by the selected CSP '$($CAKey.ProviderName)' The following
hash algorithms are supported by this CSP: $($CASetup.GetHashAlgorithmList($CAKey.ProviderName))
"@

        }
        $CAKey.HashAlgorithm = $HashAlgorithm
    }
    $CASetup.SetCASetupProperty(1,$CAKey)
#endregion

#region Setting CA type
    if ($CAType) {
        $SupportedTypes = $CASetup.GetSupportedCATypes()
        $SelectedType = $CATypesByName[$CAType]
        if ($SupportedTypes -notcontains $CATypesByName[$CAType]) {
            Write-Error -Category InvalidArgument -ErrorId "InvalidCATypeException" `
            -ErrorAction Stop -Message @"
Selected CA type: '$CAType' is not supported by current Windows Server installation.
The following CA types are supported by this installation: $([int[]]$CASetup.GetSupportedCATypes() | %{$CATypesByVal[$_]})
"@

        } else {$CASetup.SetCASetupProperty($CAPRopertyByName.CAType,$SelectedType)}
    }
#endregion

#region setting CA certificate validity
    if ($SelectedType -eq 0 -or $SelectedType -eq 3 -and $ValidForYears -ne 0) {
        try{$CASetup.SetCASetupProperty(6,$ValidForYears)}
        catch {
            Write-Error -Category InvalidArgument -ErrorId "InvalidCAValidityException" `
            -ErrorAction Stop -Message "The specified CA certificate validity period '$ValidForYears' is invalid."
        }
    }
#endregion

#region setting CA name
    if ($CAName -ne "") {
        if ($CADNSuffix -ne "") {$Subject = "CN=$CAName" + ",$CADNSuffix"} else {$Subject = "CN=$CAName"}
        $DN = New-Object -ComObject X509Enrollment.CX500DistinguishedName
        # validate X500 name format
        try {$DN.Encode($Subject,0x0)}
        catch {
            Write-Error -Category InvalidArgument -ErrorId "InvalidX500NameException" `
            -ErrorAction Stop -Message "Specified CA name or CA name suffix is not correct X.500 Distinguished Name."
        }
        $CASetup.SetCADistinguishedName($Subject, $true, $true, $true)
    }
#endregion

#region set parent CA/request file properties
    if ($CASetup.GetCASetupProperty(0) -eq 1 -and $ParentCA) {
        [void]($ParentCA -match "^(.+)\\(.+)$")
        try {$CASetup.SetParentCAInformation($ParentCA)}
        catch {
            Write-Error -Category ObjectNotFound -ErrorId "ObjectNotFoundException" `
            -ErrorAction Stop -Message @"
The specified parent CA information '$ParentCA' is incorrect. Make sure if parent CA
information is correct (you must specify existing CA) and is supplied in a 'CAComputerName\CASanitizedName' form.
"@

        }
    } elseif ($CASetup.GetCASetupProperty(0) -eq 1 -or $CASetup.GetCASetupProperty(0) -eq 4 -and $RequestFileName -ne "") {
        $CASetup.SetCASetupProperty(14,$RequestFileName)
    }
#endregion
}

#endregion

#region PFXKeySet
function PFXKeySet ($CACertFile, $Password) {
    $FilePath = Resolve-Path $CACertFile -ErrorAction Stop
    try {[void]$CASetup.CAImportPFX(
        $FilePath.Path,
        [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)),
        $true)
    } catch {Write-Error $_ -ErrorAction Stop}
}
#endregion

#region ExistingKeySet
function ExistingKeySet ($Thumbprint) {
    $ExKeys = $CASetup.GetExistingCACertificates() | Where-Object {
        ([Security.Cryptography.X509Certificates.X509Certificate2]$_.ExistingCACertificate).Thumbprint -eq $Thumbprint
    }
    if (!$ExKeys) {
        Write-Error -Category ObjectNotFound -ErrorId "ElementNotFoundException" `
        -ErrorAction Stop -Message "The system cannot find a valid CA certificate with thumbprint: $Thumbprint"
    } else {$CASetup.SetCASetupProperty(1,@($ExKeys)[0])}
}
#endregion

#endregion

#region set database settings
    if ($DBDirectory -ne "" -and $LogDirectory -ne "") {
        try {$CASetup.SetDatabaseInformation($DBDirectory,$LogDirectory,$null,$OverwriteExisting)}
        catch {
            Write-Error -Category InvalidArgument -ErrorId "InvalidPathException" `
            -ErrorAction Stop -Message "Specified path to either database directory or log directory is invalid."
        }
    } elseif ($DBDirectory -ne "" -and $LogDirectory -eq "") {
        Write-Error -Category InvalidArgument -ErrorId "InvalidPathException" `
        -ErrorAction Stop -Message "CA Log file directory cannot be empty."
    } elseif ($DBDirectory -eq "" -and $LogDirectory -ne "") {
        Write-Error -Category InvalidArgument -ErrorId "InvalidPathException" `
        -ErrorAction Stop -Message "CA database directory cannot be empty."
    }

#endregion
    # process parametersets.
    switch ($PSCmdlet.ParameterSetName) {
        "ExistingKeySet" {ExistingKeySet $Thumbprint}
        "PFXKeySet" {PFXKeySet $CACertFile $Password}
        "NewKeySet" {NewKeySet $CAName $CADNSuffix $CAType $ParentCA $CSP $KeyLength $HashAlgorithm $ValidForYears $RequestFileName $AllowCSPInteraction}
    }
    try {
        Write-Verbose "Installing Certification Authority role on $env:computername ..." -ForegroundColor Cyan
        if ($Force -or $PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Install Certification Authority")) {
            $CASetup.Install()
            $PostRequiredMsg = @"
Certification Authority role was successfully installed, but not completed. To complete
installation submit request file '$($CASetup.GetCASetupProperty(14))' to parent Certification Authority
and install issued certificate by running the following command: certutil -installcert 'PathToACertFile'
"@

            if ($CASetup.GetCASetupProperty(0) -eq 1 -and $ParentCA -eq "") {
                Write-Warning $PostRequiredMsg
            } elseif ($CASetup.GetCASetupProperty(0) -eq 1 -and $PSCmdlet.ParameterSetName -eq "NewKeySet" -and $ParentCA -ne "") {
                $CASName = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration).Active
                $SetupStatus = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration\$CASName).SetupStatus
                $RequestID = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration\$CASName).RequestID
                if ($SetupStatus -ne 1) {
                    Write-Warning $PostRequiredMsg
                }
            } elseif ($CASetup.GetCASetupProperty(0) -eq 4) {
                Write-Warning $PostRequiredMsg
            } else {New-Object SysadminsLV.PKI.Utils.ServiceOperationResult 0}
        }
    } catch {New-Object SysadminsLV.PKI.Utils.ServiceOperationResult $_.Exception.HResult}
    Remove-Module ServerManager -ErrorAction SilentlyContinue
}
# SIG # Begin signature block
# MIIcgAYJKoZIhvcNAQcCoIIccTCCHG0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCPTIx5EjytWljF
# 2XinppWVuPHP0X/kcINUJ6VHT/eLL6CCF4owggUTMIID+6ADAgECAhAJwnVp5a70
# RHscglFEfEqLMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV
# BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwHhcN
# MTcwNDE3MDAwMDAwWhcNMjAwNDIxMTIwMDAwWjBQMQswCQYDVQQGEwJMVjENMAsG
# A1UEBxMEUmlnYTEYMBYGA1UEChMPU3lzYWRtaW5zIExWIElLMRgwFgYDVQQDEw9T
# eXNhZG1pbnMgTFYgSUswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo
# NCCuzEogktL+1+lvPHu7ctNtCD7wA5Nalebh0FaKz3v1944APtg7A5oQfh6c20f7
# xYyTw4wVuo6L6S3dlMUa+bfXvTXIco0ilTIz0uqUKW8WGYwJtbFpu6PvCs0LHDRD
# rD8sEFgGHQhbz+J4gtV8BI7OID+yNfgbUk4JeSBGNzgeqZMdf/xceMoLx+fHi9tU
# OdTtgs/dXQYg3M3J+rGxFdpxOO7JmUZ8nqVALlnU9cHBGKUY4hDvDxfp7EukhnHv
# RpkhacZB1RBw0q8q+ekvLVCZwpG4N1Pnq2ksHiBzqRWQQE89iV+UwgRnLx2igywk
# 2kX+JPSZYsQCbDGo4DqBAgMBAAGjggHFMIIBwTAfBgNVHSMEGDAWgBRaxLl7Kgqj
# pepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQU9Mh+66y4uf1WQl9FmsWMHdk2HrswDgYD
# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4wNaAz
# oDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEu
# Y3JsMDWgM6Axhi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVk
# LWNzLWcxLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwDATAqMCgGCCsGAQUFBwIB
# FhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAEEATCBhAYIKwYB
# BQUHAQEEeDB2MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w
# TgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dFNIQTJBc3N1cmVkSURDb2RlU2lnbmluZ0NBLmNydDAMBgNVHRMBAf8EAjAAMA0G
# CSqGSIb3DQEBCwUAA4IBAQCfpLMUerP0WXkcb9+dunMLt3jowZEd8X6ISxxzdsdB
# 8jOZ92L88qKqjWD1I9HBceba4tdJZCLV33S9a3eoxUunIlJH4GmYH/HSrc2qgNxg
# PyobmWf556c7Wd3q6ZUKgos0bw++TtLqb/jvoKN19epTEkwQDIwVFzOAxZ4T+sYr
# jmFhd9KeaRhTLZRBVdKNTKtXaoWFrfNSQTp8NcNYdkEM05cUnEUMDOoeLSmxPnIv
# pl8KbripxtVQ591rCLJN2uMtrtSE1nvjiYfSFQI00EiB33ZoI2T1eCNuP1M6c+ex
# KzQQC8UDp7J+duzl1j605TwSfLS/MJsaiwftNzc3FfgSMIIFMDCCBBigAwIBAgIQ
# BAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMDIy
# MTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM
# RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQD
# EyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9ndwfTCzFJGc/Q+0WZ
# sTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ1DcZ17aq8JyGpdglrA55KDp+6dFn
# 08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS8hRniolF1C2ho+mI
# LCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6scKKrzn/pfMuSoeU7MRzP6vIK5Fe7
# SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEgJSPrCGQ+UpbB8g8S
# 9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg0A9kczyen6Yzqf0Z3yWT0QIDAQAB
# o4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYD
# VR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEG
# A1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwTwYDVR0gBEgwRjA4Bgpg
# hkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv
# bS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYEFFrEuXsqCqOl6nEDwGD5LfZldQ5Y
# MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBCwUA
# A4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06GsTvMGHXfgtg/cM9D8Svi/3vKt8g
# VTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5jDhNLrddfRHnzNhQGivecRk5c/5Cx
# GwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgCPC6Ro8AlEeKcFEehemhor5unXCBc
# 2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIysjaKJAL+L3J+HNdJRZboWR3p+nRk
# a7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4GbT8aTEAb8B4H6i9r5gkn3Ym6hU/oS
# lBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIGajCCBVKgAwIBAgIQAwGaAjr/WLFr1tXq
# 5hfwZjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln
# aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhE
# aWdpQ2VydCBBc3N1cmVkIElEIENBLTEwHhcNMTQxMDIyMDAwMDAwWhcNMjQxMDIy
# MDAwMDAwWjBHMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQxJTAjBgNV
# BAMTHERpZ2lDZXJ0IFRpbWVzdGFtcCBSZXNwb25kZXIwggEiMA0GCSqGSIb3DQEB
# AQUAA4IBDwAwggEKAoIBAQCjZF38fLPggjXg4PbGKuZJdTvMbuBTqZ8fZFnmfGt/
# a4ydVfiS457VWmNbAklQ2YPOb2bu3cuF6V+l+dSHdIhEOxnJ5fWRn8YUOawk6qhL
# LJGJzF4o9GS2ULf1ErNzlgpno75hn67z/RJ4dQ6mWxT9RSOOhkRVfRiGBYxVh3lI
# RvfKDo2n3k5f4qi2LVkCYYhhchhoubh87ubnNC8xd4EwH7s2AY3vJ+P3mvBMMWSN
# 4+v6GYeofs/sjAw2W3rBerh4x8kGLkYQyI3oBGDbvHN0+k7Y/qpA8bLOcEaD6dpA
# oVk62RUJV5lWMJPzyWHM0AjMa+xiQpGsAsDvpPCJEY93AgMBAAGjggM1MIIDMTAO
# BgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEF
# BQcDCDCCAb8GA1UdIASCAbYwggGyMIIBoQYJYIZIAYb9bAcBMIIBkjAoBggrBgEF
# BQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCCAWQGCCsGAQUFBwIC
# MIIBVh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABDAGUAcgB0
# AGkAZgBpAGMAYQB0AGUAIABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABhAGMAYwBl
# AHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUAIABEAGkAZwBpAEMAZQByAHQAIABD
# AFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUAbAB5AGkAbgBnACAAUABh
# AHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkAbQBp
# AHQAIABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABpAG4AYwBv
# AHIAcABvAHIAYQB0AGUAZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBlAGYAZQBy
# AGUAbgBjAGUALjALBglghkgBhv1sAxUwHwYDVR0jBBgwFoAUFQASKxOYspkH7R7f
# or5XDStnAs0wHQYDVR0OBBYEFGFaTSS2STKdSip5GoNL9B6Jwcp9MH0GA1UdHwR2
# MHQwOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRENBLTEuY3JsMDigNqA0hjJodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGln
# aUNlcnRBc3N1cmVkSURDQS0xLmNybDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcnQw
# DQYJKoZIhvcNAQEFBQADggEBAJ0lfhszTbImgVybhs4jIA+Ah+WI//+x1GosMe06
# FxlxF82pG7xaFjkAneNshORaQPveBgGMN/qbsZ0kfv4gpFetW7easGAm6mlXIV00
# Lx9xsIOUGQVrNZAQoHuXx/Y/5+IRQaa9YtnwJz04HShvOlIJ8OxwYtNiS7Dgc6aS
# wNOOMdgv420XEwbu5AO2FKvzj0OncZ0h3RTKFV2SQdr5D4HRmXQNJsQOfxu19aDx
# xncGKBXp2JPlVRbwuwqrHNtcSCdmyKOLChzlldquxC5ZoGHd2vNtomHpigtt7BIY
# vfdVVEADkitrwlHCCkivsNRu4PQUCjob4489yq9qjXvc2EQwggbNMIIFtaADAgEC
# AhAG/fkDlgOt6gAK6z8nu7obMA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0wNjEx
# MTAwMDAwMDBaFw0yMTExMTAwMDAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQAD
# ggEPADCCAQoCggEBAOiCLZn5ysJClaWAc0Bw0p5WVFypxNJBBo/JM/xNRZFcgZ/t
# LJz4FlnfnrUkFcKYubR3SdyJxArar8tea+2tsHEx6886QAxGTZPsi3o2CAOrDDT+
# GEmC/sfHMUiAfB6iD5IOUMnGh+s2P9gww/+m9/uizW9zI/6sVgWQ8DIhFonGcIj5
# BZd9o8dD3QLoOz3tsUGj7T++25VIxO4es/K8DCuZ0MZdEkKB4YNugnM/JksUkK5Z
# ZgrEjb7SzgaurYRvSISbT0C58Uzyr5j79s5AXVz2qPEvr+yJIvJrGGWxwXOt1/HY
# zx4KdFxCuGh+t9V3CidWfA9ipD8yFGCV/QcEogkCAwEAAaOCA3owggN2MA4GA1Ud
# DwEB/wQEAwIBhjA7BgNVHSUENDAyBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUF
# BwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwggHSBgNVHSAEggHJMIIBxTCCAbQGCmCG
# SAGG/WwAAQQwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0LmNv
# bS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBB
# AG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBh
# AHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBj
# AGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABT
# ACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABB
# AGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBh
# AGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBh
# AHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAu
# MAsGCWCGSAGG/WwDFTASBgNVHRMBAf8ECDAGAQH/AgEAMHkGCCsGAQUFBwEBBG0w
# azAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUF
# BzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk
# SURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRw
# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3Js
# MB0GA1UdDgQWBBQVABIrE5iymQftHt+ivlcNK2cCzTAfBgNVHSMEGDAWgBRF66Kv
# 9JLLgjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEARlA+ybcoJKc4HbZb
# Ka9Sz1LpMUerVlx71Q0LQbPv7HUfdDjyslxhopyVw1Dkgrkj0bo6hnKtOHisdV0X
# FzRyR4WUVtHruzaEd8wkpfMEGVWp5+Pnq2LN+4stkMLA0rWUvV5PsQXSDj0aqRRb
# poYxYqioM+SbOafE9c4deHaUJXPkKqvPnHZL7V/CSxbkS3BMAIke/MV5vEwSV/5f
# 4R68Al2o/vsHOE8Nxl2RuQ9nRc3Wg+3nkg2NsWmMT/tZ4CMP0qquAHzunEIOz5HX
# J7cW7g/DvXwKoO4sCFWFIrjrGBpN/CohrUkxg0eVd3HcsRtLSxwQnHcUwZ1PL1qV
# CCkQJjGCBEwwggRIAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERp
# Z2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0ECEAnCdWnlrvRE
# exyCUUR8SoswDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgSldWwul/sMEdE1TJvn6kwxCH
# CVYrxN1qP/LBKhuvwFcwDQYJKoZIhvcNAQEBBQAEggEAljwohWmEQ93XizQzgr0O
# hqld0WN+YvxiPuvQPG4OdXbUK1ngvwMMMqlcA7agEqrJzn7Z6OIrByuna2WLhR4I
# QeyN5gOsbcEs+cZizVBdiJD9JI/mKoR3T9seXZvOmbfpczJlg3+4UkA90/jxOgct
# NsJfIwjcJFUrRyqADMTCeR2tU7fiq34UiUQGLrLUXKyJhyls8DiDW88NtssCfKr7
# HmcFlr4qZDOS/VIHULYe8sKYyXy8L5hjhDxXkFA3ZlEA3xw3tATRjku5A2agU11Z
# 342/nkP3j3NiahHqGVEaJfi9NT8stjCvjn99uavLZCYmV7aOUAYwgZrVmrDwD3pp
# I6GCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr
# 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc
# BgkqhkiG9w0BCQUxDxcNMTgxMDIyMTYyNTA3WjAjBgkqhkiG9w0BCQQxFgQUkggc
# 4aU7EbaN3dynPCoi4ntcIxYwDQYJKoZIhvcNAQEBBQAEggEASU5kDP4E1wHYMatY
# 260z6+kfqRuLPnxgnEtOSxK4oG+FLn2I42GwM3+u0cpDJCmmQRRUFNpxl33Z75Kc
# Q5zUlQHSzJyJ/CcXJKowJTykG9XWdTIdLvg3ZeKJ3tzHw5TDEXPAfUv8209FyjC+
# bGGCu1DgNJ/zoNn4T2rBQZxjsbqo+0TmsPstDDk4TjWlwILpzr8g7rznJ3lhn59I
# UFArP2kUk3OjHemE8ORnr8tCeVjDlUDj/VuVODKkf3TXpfnoqxPtWhVOwmaPQMCH
# 1Pr47j5HOG8WFwEU0g0i6j+V8jNetgWi6bVgf3ZwkhaaCV40Ka9NjPjp83JaI1Pz
# 7siqcw==
# SIG # End signature block