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
# MIIfhgYJKoZIhvcNAQcCoIIfdzCCH3MCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCPTIx5EjytWljF
# 2XinppWVuPHP0X/kcINUJ6VHT/eLL6CCGYYwggX1MIID3aADAgECAhAdokgwb5sm
# GNCC4JZ9M9NqMA0GCSqGSIb3DQEBDAUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRo
# ZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0
# aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xODExMDIwMDAwMDBaFw0zMDEyMzEyMzU5
# NTlaMHwxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIx
# EDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEkMCIG
# A1UEAxMbU2VjdGlnbyBSU0EgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0B
# AQEFAAOCAQ8AMIIBCgKCAQEAhiKNMoV6GJ9J8JYvYwgeLdx8nxTP4ya2JWYpQIZU
# RnQxYsUQ7bKHJ6aZy5UwwFb1pHXGqQ5QYqVRkRBq4Etirv3w+Bisp//uLjMg+gwZ
# iahse60Aw2Gh3GllbR9uJ5bXl1GGpvQn5Xxqi5UeW2DVftcWkpwAL2j3l+1qcr44
# O2Pej79uTEFdEiAIWeg5zY/S1s8GtFcFtk6hPldrH5i8xGLWGwuNx2YbSp+dgcRy
# QLXiX+8LRf+jzhemLVWwt7C8VGqdvI1WU8bwunlQSSz3A7n+L2U18iLqLAevRtn5
# RhzcjHxxKPP+p8YU3VWRbooRDd8GJJV9D6ehfDrahjVh0wIDAQABo4IBZDCCAWAw
# HwYDVR0jBBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFA7hOqhT
# OjHVir7Bu61nGgOFrTQOMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/
# AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMDBggrBgEFBQcDCDARBgNVHSAECjAIMAYG
# BFUdIAAwUAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29t
# L1VTRVJUcnVzdFJTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUF
# BwEBBGowaDA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VT
# RVJUcnVzdFJTQUFkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2Nz
# cC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBDAUAA4ICAQBNY1DtRzRKYaTb3moq
# jJvxAAAeHWJ7Otcywvaz4GOz+2EAiJobbRAHBE++uOqJeCLrD0bs80ZeQEaJEvQL
# d1qcKkE6/Nb06+f3FZUzw6GDKLfeL+SU94Uzgy1KQEi/msJPSrGPJPSzgTfTt2Sw
# piNqWWhSQl//BOvhdGV5CPWpk95rcUCZlrp48bnI4sMIFrGrY1rIFYBtdF5KdX6l
# uMNstc/fSnmHXMdATWM19jDTz7UKDgsEf6BLrrujpdCEAJM+U100pQA1aWy+nyAl
# EA0Z+1CQYb45j3qOTfafDh7+B1ESZoMmGUiVzkrJwX/zOgWb+W/fiH/AI57SHkN6
# RTHBnE2p8FmyWRnoao0pBAJ3fEtLzXC+OrJVWng+vLtvAxAldxU0ivk2zEOS5LpP
# 8WKTKCVXKftRGcehJUBqhFfGsp2xvBwK2nxnfn0u6ShMGH7EezFBcZpLKewLPVdQ
# 0srd/Z4FUeVEeN0B3rF1mA1UJP3wTuPi+IO9crrLPTru8F4XkmhtyGH5pvEqCgul
# ufSe7pgyBYWe6/mDKdPGLH29OncuizdCoGqC7TtKqpQQpOEN+BfFtlp5MxiS47V1
# +KHpjgolHuQe8Z9ahyP/n6RRnvs5gBHN27XEp6iAb+VT1ODjosLSWxr6MiYtaldw
# HDykWC6j81tLB9wyWfOHpxptWDCCBkowggUyoAMCAQICEBdBS6OH2/E/xEs3Bf5c
# krcwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0
# ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGln
# byBMaW1pdGVkMSQwIgYDVQQDExtTZWN0aWdvIFJTQSBDb2RlIFNpZ25pbmcgQ0Ew
# HhcNMTkwODEzMDAwMDAwWhcNMjIwODEyMjM1OTU5WjCBmTELMAkGA1UEBhMCVVMx
# DjAMBgNVBBEMBTk3MjE5MQ8wDQYDVQQIDAZPcmVnb24xETAPBgNVBAcMCFBvcnRs
# YW5kMRwwGgYDVQQJDBMxNzEwIFNXIE1pbGl0YXJ5IFJkMRswGQYDVQQKDBJQS0kg
# U29sdXRpb25zIEluYy4xGzAZBgNVBAMMElBLSSBTb2x1dGlvbnMgSW5jLjCCAiIw
# DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANC9ao+Uw7Owaxi+v5FF1+eKGIpv
# QnKBFu61VsoHFyotJ8yoeC8tiRjmHggRbmQm0sTAdAXw23Rj5ZW6ndMWgA258car
# a6+oWB071e3ctsHoavc7NkDoCkKS2uh5tTmqclNMg6xaU1IIp9IWFq00K1jkeXex
# HIFLjTF2AA2SEteJO6VY08EiN6ktAOa1P4NbB0fTRUmca0j3W552hvU5Ig8G0DJt
# b4IDMMnu6WllNuxfqyNJiUOYkDET1p52XzvhMFMFnhbsH9JPcR4IA7Pp4xc1mRhe
# D9uE+KVx1astA/GvWtkpeZy/efbaMOxY4VuTW9kdgc8tB4VPamQQpoVmD3ULsaPz
# iv8cOum0CMrTtwKA/meas20A69u3xg8KeuDwxE0rysT4a68lXjFZViyHQQQzeZi4
# wAifk3URIABuKy6DQdQ4FJRjIvAXh5PD2WatY7aJJw9nc0biEB7bEjDNYufJ4OL9
# M9ibVqQxpLz0Vm9D+aCD1CJFySCcIOg7VRWCNyTqtDxDlWd6I7H1s2QwsiEWIOCE
# MtOlve+rZi9RgJhtrdoINgmgSPNH+lITexCMrNDvpEzYxggsTLcEs4jq6XzoD/bR
# G9gvSv/d5Di8Js0gjaqpwDZbLsProdRFX0AlAROarTVW0m9nqVHcP4o0Lc/jKCJ6
# 8073khO+aMOJKW/9AgMBAAGjggGoMIIBpDAfBgNVHSMEGDAWgBQO4TqoUzox1Yq+
# wbutZxoDha00DjAdBgNVHQ4EFgQUd9YCgc1i67qdUtY6jeRnT0YzsVAwDgYDVR0P
# AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJ
# YIZIAYb4QgEBBAQDAgQQMEAGA1UdIAQ5MDcwNQYMKwYBBAGyMQECAQMCMCUwIwYI
# KwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMEMGA1UdHwQ8MDowOKA2
# oDSGMmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1JTQUNvZGVTaWduaW5n
# Q0EuY3JsMHMGCCsGAQUFBwEBBGcwZTA+BggrBgEFBQcwAoYyaHR0cDovL2NydC5z
# ZWN0aWdvLmNvbS9TZWN0aWdvUlNBQ29kZVNpZ25pbmdDQS5jcnQwIwYIKwYBBQUH
# MAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMCAGA1UdEQQZMBeBFWluZm9AcGtp
# c29sdXRpb25zLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAa4IZBlHU1V6Dy+atjrwS
# YugL+ryvzR1eGH5+nzbwxAi4h3IaknQBIuWzoamR+hRUga9/Rd4jrBbXGTgkqM7A
# tnzXP7P5NZOmxOdFOl1UfgNIv5MfJNPzsvn54bnx9rgKWJlpmKPCr1xtfj2ERlhA
# f6ADOfUyCcTnSwlBi1Bai60wqqDPuj1zcDaD2XGddVmqVrplx1zNoX7vhyErA7V9
# psRWQYIflYY0L58gposEUVMKM6TJRRjndibRnO2CI9plXDBz4j3cTni3fXGM3UuB
# VInKSeC+mTsvJVYTHjBowWohhxMBdqD0xFVbysoRKGtWSJwErdAomjMCrY2q6oYc
# xzCCBmowggVSoAMCAQICEAMBmgI6/1ixa9bV6uYX8GYwDQYJKoZIhvcNAQEFBQAw
# YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBD
# QS0xMB4XDTE0MTAyMjAwMDAwMFoXDTI0MTAyMjAwMDAwMFowRzELMAkGA1UEBhMC
# VVMxETAPBgNVBAoTCERpZ2lDZXJ0MSUwIwYDVQQDExxEaWdpQ2VydCBUaW1lc3Rh
# bXAgUmVzcG9uZGVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2Rd
# /Hyz4II14OD2xirmSXU7zG7gU6mfH2RZ5nxrf2uMnVX4kuOe1VpjWwJJUNmDzm9m
# 7t3LhelfpfnUh3SIRDsZyeX1kZ/GFDmsJOqoSyyRicxeKPRktlC39RKzc5YKZ6O+
# YZ+u8/0SeHUOplsU/UUjjoZEVX0YhgWMVYd5SEb3yg6Np95OX+Koti1ZAmGIYXIY
# aLm4fO7m5zQvMXeBMB+7NgGN7yfj95rwTDFkjePr+hmHqH7P7IwMNlt6wXq4eMfJ
# Bi5GEMiN6ARg27xzdPpO2P6qQPGyznBGg+naQKFZOtkVCVeZVjCT88lhzNAIzGvs
# YkKRrALA76TwiRGPdwIDAQABo4IDNTCCAzEwDgYDVR0PAQH/BAQDAgeAMAwGA1Ud
# EwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwggG/BgNVHSAEggG2MIIB
# sjCCAaEGCWCGSAGG/WwHATCCAZIwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRp
# Z2ljZXJ0LmNvbS9DUFMwggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBz
# AGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBv
# AG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAg
# AHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAg
# AHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBt
# AGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0
# AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABo
# AGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9
# bAMVMB8GA1UdIwQYMBaAFBUAEisTmLKZB+0e36K+Vw0rZwLNMB0GA1UdDgQWBBRh
# Wk0ktkkynUoqeRqDS/QeicHKfTB9BgNVHR8EdjB0MDigNqA0hjJodHRwOi8vY3Js
# My5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNybDA4oDagNIYy
# aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5j
# cmwwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCd
# JX4bM02yJoFcm4bOIyAPgIfliP//sdRqLDHtOhcZcRfNqRu8WhY5AJ3jbITkWkD7
# 3gYBjDf6m7GdJH7+IKRXrVu3mrBgJuppVyFdNC8fcbCDlBkFazWQEKB7l8f2P+fi
# EUGmvWLZ8Cc9OB0obzpSCfDscGLTYkuw4HOmksDTjjHYL+NtFxMG7uQDthSr849D
# p3GdId0UyhVdkkHa+Q+B0Zl0DSbEDn8btfWg8cZ3BigV6diT5VUW8LsKqxzbXEgn
# Zsijiwoc5ZXarsQuWaBh3drzbaJh6YoLbewSGL33VVRAA5Ira8JRwgpIr7DUbuD0
# FAo6G+OPPcqvao173NhEMIIGzTCCBbWgAwIBAgIQBv35A5YDreoACus/J7u6GzAN
# BgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg
# SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2Vy
# dCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMjExMTEwMDAw
# MDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBBc3N1cmVk
# IElEIENBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDogi2Z+crC
# QpWlgHNAcNKeVlRcqcTSQQaPyTP8TUWRXIGf7Syc+BZZ3561JBXCmLm0d0ncicQK
# 2q/LXmvtrbBxMevPOkAMRk2T7It6NggDqww0/hhJgv7HxzFIgHweog+SDlDJxofr
# Nj/YMMP/pvf7os1vcyP+rFYFkPAyIRaJxnCI+QWXfaPHQ90C6Ds97bFBo+0/vtuV
# SMTuHrPyvAwrmdDGXRJCgeGDboJzPyZLFJCuWWYKxI2+0s4Grq2Eb0iEm09AufFM
# 8q+Y+/bOQF1c9qjxL6/siSLyaxhlscFzrdfx2M8eCnRcQrhofrfVdwonVnwPYqQ/
# MhRglf0HBKIJAgMBAAGjggN6MIIDdjAOBgNVHQ8BAf8EBAMCAYYwOwYDVR0lBDQw
# MgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUHAwQGCCsGAQUF
# BwMIMIIB0gYDVR0gBIIByTCCAcUwggG0BgpghkgBhv1sAAEEMIIBpDA6BggrBgEF
# BQcCARYuaHR0cDovL3d3dy5kaWdpY2VydC5jb20vc3NsLWNwcy1yZXBvc2l0b3J5
# Lmh0bTCCAWQGCCsGAQUFBwICMIIBVh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAg
# AHQAaABpAHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0AGUAIABjAG8AbgBzAHQAaQB0
# AHUAdABlAHMAIABhAGMAYwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUAIABE
# AGkAZwBpAEMAZQByAHQAIABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABS
# AGUAbAB5AGkAbgBnACAAUABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3
# AGgAaQBjAGgAIABsAGkAbQBpAHQAIABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBk
# ACAAYQByAGUAIABpAG4AYwBvAHIAcABvAHIAYQB0AGUAZAAgAGgAZQByAGUAaQBu
# ACAAYgB5ACAAcgBlAGYAZQByAGUAbgBjAGUALjALBglghkgBhv1sAxUwEgYDVR0T
# AQH/BAgwBgEB/wIBADB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6
# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0f
# BHoweDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNz
# dXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDAdBgNVHQ4EFgQUFQASKxOYspkH
# 7R7for5XDStnAs0wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJ
# KoZIhvcNAQEFBQADggEBAEZQPsm3KCSnOB22WymvUs9S6TFHq1Zce9UNC0Gz7+x1
# H3Q48rJcYaKclcNQ5IK5I9G6OoZyrTh4rHVdFxc0ckeFlFbR67s2hHfMJKXzBBlV
# qefj56tizfuLLZDCwNK1lL1eT7EF0g49GqkUW6aGMWKoqDPkmzmnxPXOHXh2lCVz
# 5Cqrz5x2S+1fwksW5EtwTACJHvzFebxMElf+X+EevAJdqP77BzhPDcZdkbkPZ0XN
# 1oPt55INjbFpjE/7WeAjD9KqrgB87pxCDs+R1ye3Fu4Pw718CqDuLAhVhSK46xga
# TfwqIa1JMYNHlXdx3LEbS0scEJx3FMGdTy9alQgpECYxggVWMIIFUgIBATCBkDB8
# MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
# VQQHEwdTYWxmb3JkMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxJDAiBgNVBAMT
# G1NlY3RpZ28gUlNBIENvZGUgU2lnbmluZyBDQQIQF0FLo4fb8T/ESzcF/lyStzAN
# BglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqG
# SIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3
# AgEVMC8GCSqGSIb3DQEJBDEiBCBKV1bC6X+wwR0TVMm+fqTDEIcJVivE3Wo/8sEq
# G6/AVzANBgkqhkiG9w0BAQEFAASCAgC595Hx26oolkKy556BNjIDVpUdaYEMQ5JA
# uQTZrdxCoMgyHQh/6y17IkU1UrrkFErVqNNx9MDuqaN0jiI8ItrFEVpyCURskGHn
# fd9N+PjKiOgTB/KybNmeNhDH4PtwYjLbj8D04kYeJUyJ26jtO/gmLlXRe8+4HNd/
# lVI2hsZzzoJwV7WoiZVSZdBTQaUYxAWNCjsYE/WQl/Zm3zuNHrC/Uoc/X2PZex6A
# 9l0dDTuCry0Zwy+jG2fcRrV9xqKpqzVlRc7WqA4E/r3+JNkzsR1s3PpXGbBqT2Hm
# 4ZpE6zi2AfyvK9MSb4LU0pslW9DLu/fE00Y7X4MY4wX334Ud20jHxz7WR21NPKPi
# OuWNb3p5nlTwTLJk/HXoK5SB78+PHHqyNu9sexEQLGgjLj7TeGb1qesNynLAySGN
# i+r+FUl6sXnQkIZNWYaoXAk4liszJ3Pa1RvmkmtZn5LjYS3jBo3MuHKCdXzHNuGs
# rK3P+He/kOUi1xWRPrQrIwYENmikIT9x+3qiRvhaItJJq3oohJQdMjGTRWuL7ZZd
# N+SKRIDOoMztaHifXp0UCPMdOJnVf67cPwpW6PsDZz/BZ8zQoqf38paUgUclrvj/
# /O8e/zBYWHGQzHI3jwhumUJKxwdiRSkd0VIomPNdBBEeRiThEuY0FYJzquCjcbzS
# eonP1MrhnqGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNV
# BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp
# Y2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGa
# Ajr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3
# DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwODI2MTAxOTE0WjAjBgkqhkiG9w0BCQQx
# FgQUCDKRoiHsB3jzV/ByzQIH5/kmnoMwDQYJKoZIhvcNAQEBBQAEggEAhk3pLlHL
# YzSPZKKUtk577mJDPGZSgKRJ2BjQQWhTFdjWUzl4AqV9O/6X8nSOXF2yhsCkluTX
# FIcWF19TcRYLtZy621F1S8PEiyui7t6CSKxyw6IlrogIJpq0ULjNT0vk2R7WnUoX
# wlcFcQFJ3FZbOZhjTmvkVkXiKWI38IxptTw9njOxNKvwMliHAbRw8bE79nnyTaRB
# YZlvbu0Hg8A+xv79ZwKPeHQpHyGxaWCVLd2Us+h3cqvmFeZol9gX1f6bQNAprM7f
# wlP18LmRu80/2SyLjFS15dQu0kQIkCDwHF/+lhuDK6c0c8HOodPYL0Z/lwmyhPCN
# OckD/WvZ5vrQKg==
# SIG # End signature block