Server/Install-CertificationAuthority.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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