Obs/bin/ObsDep/content/Powershell/Roles/Common/CertHelpers.psm1
# -------------------------------------------------------------- # Copyright � Microsoft Corporation. All Rights Reserved. # Microsoft Corporation (or based on where you live, one of its affiliates) licenses this sample code for your internal testing purposes only. # Microsoft provides the following sample code AS IS without warranty of any kind. The sample code arenot supported under any Microsoft standard support program or services. # Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. # The entire risk arising out of the use or performance of the sample code remains with you. # In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the code be liable for any damages whatsoever # (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) # arising out of the use of or inability to use the sample code, even if Microsoft has been advised of the possibility of such damages. # --------------------------------------------------------------- Function PrettyTime() { return "[" + (Get-Date -Format o) + "]" } Function Log($msg) { Write-Verbose $( $(PrettyTime) + " " + $msg) -Verbose } function GetSubjectName([bool] $UseManagementAddress) { if ($UseManagementAddress -eq $true) { # When IP Address is specified, we are currently looking just for IPv4 corpnet ip address # In the final design, only computer names will be used for subject names $corpIPAddresses = get-netIpAddress -AddressFamily IPv4 -PrefixOrigin Dhcp -ErrorAction Ignore if ($corpIPAddresses -ne $null -and $corpIPAddresses[0] -ne $null) { $mesg = [System.String]::Format("Using IP Address {0} for certificate subject name", $corpIPAddresses[0].IPAddress); Log $mesg return $corpIPAddresses[0].IPAddress } else { Log "Unable to find management IP address "; } } $hostFqdn = [System.Net.Dns]::GetHostByName(($env:computerName)).HostName; $mesg = [System.String]::Format("Using computer name {0} for certificate subject name", $hostFqdn); Log $mesg return $hostFqdn ; } function GenerateSelfSignedCertificate([string] $subjectName) { $cryptographicProviderName = "Microsoft Base Cryptographic Provider v1.0"; [int] $privateKeyLength = 1024; $sslServerOidString = "1.3.6.1.5.5.7.3.1"; $sslClientOidString = "1.3.6.1.5.5.7.3.2"; [int] $validityPeriodInYear = 5; $name = new-object -com "X509Enrollment.CX500DistinguishedName.1" $name.Encode("CN=" + $SubjectName, 0) $mesg = [System.String]::Format("Generating certificate with subject Name {0}", $subjectName); Log $mesg #Generate Key $key = new-object -com "X509Enrollment.CX509PrivateKey.1" $key.ProviderName = $cryptographicProviderName $key.KeySpec = 1 #X509KeySpec.XCN_AT_KEYEXCHANGE $key.Length = $privateKeyLength $key.MachineContext = 1 $key.ExportPolicy = 0x2 #X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG $key.Create() #Configure Eku $serverauthoid = new-object -com "X509Enrollment.CObjectId.1" $serverauthoid.InitializeFromValue($sslServerOidString) $clientauthoid = new-object -com "X509Enrollment.CObjectId.1" $clientauthoid.InitializeFromValue($sslClientOidString) $ekuoids = new-object -com "X509Enrollment.CObjectIds.1" $ekuoids.add($serverauthoid) $ekuoids.add($clientauthoid) $ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1" $ekuext.InitializeEncode($ekuoids) # Set the hash algorithm to sha512 instead of the default sha1 $hashAlgorithmObject = New-Object -ComObject X509Enrollment.CObjectId $hashAlgorithmObject.InitializeFromAlgorithmName( $ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, $ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, $AlgorithmFlags.AlgorithmFlagsNone, "SHA512") #Request Cert $cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1" $cert.InitializeFromPrivateKey(2, $key, "") $cert.Subject = $name $cert.Issuer = $cert.Subject $cert.NotBefore = (get-date).ToUniversalTime() $cert.NotAfter = $cert.NotBefore.AddYears($validityPeriodInYear); $cert.X509Extensions.Add($ekuext) $cert.HashAlgorithm = $hashAlgorithmObject $cert.Encode() $enrollment = new-object -com "X509Enrollment.CX509Enrollment.1" $enrollment.InitializeFromRequest($cert) $certdata = $enrollment.CreateRequest(0) $enrollment.InstallResponse(2, $certdata, 0, "") Log "Successfully added cert to local machine store"; } function GivePermissionToNetworkService($targetCert) { $targetCertPrivKey = $targetCert.PrivateKey $privKeyCertFile = Get-Item -path "$ENV:ProgramData\Microsoft\Crypto\RSA\MachineKeys\*" | where {$_.Name -eq $targetCertPrivKey.CspKeyContainerInfo.UniqueKeyContainerName} $privKeyAcl = Get-Acl $privKeyCertFile $permission = "NT AUTHORITY\NETWORK SERVICE","Read","Allow" $accessRule = new-object System.Security.AccessControl.FileSystemAccessRule $permission $privKeyAcl.AddAccessRule($accessRule) Set-Acl $privKeyCertFile.FullName $privKeyAcl } Function AddCertToLocalMachineStore($certFullPath, $storeName, $securePassword) { $rootName = "LocalMachine" # create a representation of the certificate file $certificate = new-object System.Security.Cryptography.X509Certificates.X509Certificate2 if($securePassword -eq $null) { $certificate.import($certFullPath) } else { # https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509keystorageflags(v=vs.110).aspx $certificate.import($certFullPath, $securePassword, "MachineKeySet,PersistKeySet") } # import into the store $store = new-object System.Security.Cryptography.X509Certificates.X509Store($storeName, $rootName) $store.open("MaxAllowed") $store.add($certificate) $store.close() } Function GetSubjectFqdnFromCertificatePath($certFullPath) { # create a representation of the certificate file $certificate = new-object System.Security.Cryptography.X509Certificates.X509Certificate2 $certificate.import($certFullPath) return GetSubjectFqdnFromCertificate $certificate ; } Function GetSubjectFqdnFromCertificate([System.Security.Cryptography.X509Certificates.X509Certificate2] $certificate) { $mesg = [System.String]::Format("Parsing Subject Name {0} to get Subject Fqdn ", $certificate.Subject) Log $mesg $subjectFqdn = $certificate.Subject.Split('=')[1] ; return $subjectFqdn; } function Get-Certs($path){ $flags = [System.Security.Cryptography.X509Certificates.OpenFlags]"ReadOnly" $rootName = [System.Security.Cryptography.X509Certificates.StoreLocation]"LocalMachine" $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($path, $rootName) $store.Open($flags) $certs = $store.Certificates $store.Close() return $certs } <# .Synopsis Export the Azure Stack cert to the file share #> function Export-AzSCertificateToShare { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, HelpMessage="The CustomerConfiguration.xml")] [ValidateNotNull()] [CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $CertBase64String, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $ProtectedCertPwd, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $CertificateName, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $CertificateRoleId ) Import-Module Microsoft.AzureStack.Security.CngDpapi $clusterName = Get-ManagementClusterName $Parameters $externalCertificatesInfo = $Parameters.Roles['CertificateManagement'].PublicConfiguration.PublicInfo.ExternalCertConfigurations.Certificates | Where Type -EQ 'AzureAD' | Select -First 1 $certificateConfigXml = $externalCertificatesInfo.Certificate | Where Name -EQ $CertificateName $domainRole = $Parameters.Roles["Domain"].PublicConfiguration $domainName = $domainRole.PublicInfo.DomainConfiguration.DomainName Trace-Execution "$($MyInvocation.InvocationName) : Retreiving the cert information with cert name '$CertificateName' and cert role Id '$CertificateRoleId'..." $certificateInfo = (($externalCertificatesInfo.Certificate | Where Name -EQ $CertificateName).Consumers.Consumer | Where Name -EQ $CertificateRoleId | Select -First 1) Trace-Execution "$($MyInvocation.InvocationName) : Retrieved the unprocessed extension host cert path: $($certificateInfo.Location)" $certLocation = Get-SharePath $Parameters $certificateInfo.Location $clusterName Trace-Execution "$($MyInvocation.InvocationName) : Retrieved the extension host cert path: $certLocation" $protectToGMSA = $certificateInfo.ProtectTo if($protectToGMSA -ne $null) { Trace-Execution "$($MyInvocation.InvocationName) : ProtectTo parameter specified: $protectToGMSA" } Trace-Execution "$($MyInvocation.InvocationName) : Retreiving the Azure Stack internal CA Certificate password..." $securityInfo = $Parameters.Roles["Cloud"].PublicConfiguration.PublicInfo.SecurityInfo $certUser = $securityInfo.CACertUsers.User | Where Role -EQ "CACertificateUser" $certCredential = $Parameters.GetCredential($certUser.Credential) $exportCertPassword = $certCredential.GetNetworkCredential().SecurePassword if ($exportCertPassword.Length -eq 0) { throw "The Azure Stack internal cert password should not be empty" } Trace-Execution "$($MyInvocation.InvocationName) : Retrive the Azure Stack internal CA Certificate password." Trace-Execution "$($MyInvocation.InvocationName) : Converting cert from base64 string to binary..." $certBytes = [System.Convert]::FromBase64String($CertBase64String) Trace-Execution "$($MyInvocation.InvocationName) : Converting the protected password to local secure string..." $certPwdCipher = [System.Convert]::FromBase64String($ProtectedCertPwd) $certPwdCipherUnprotected = [Microsoft.AzureStack.Security.CngDpapi.ProtectionDescriptor]::UnprotectSecret($certPwdCipher) $certPwdString = [System.Text.Encoding]::UTF8.GetString($certPwdCipherUnprotected) $certPwd = ConvertTo-SecureString $certPwdString -AsPlainText -Force Trace-Execution "$($MyInvocation.InvocationName) : decrypting the cert with input password..." try { # SyslogClient cert location may have not been been provided. If this is the first time # processing a SyslogClient cert - by PEP cmdlet Set-SyslogClient - the path # must be checked and created before exporting the cert to the share location. # Also, doing the same for Container Registry certificate as it will be supplied # by the customer via Import-AzsContainerRegistrySslCertificate after deployment. if (($CertificateName -eq "SyslogClient") -or ($CertificateName -eq "Container Registry") -and !(Test-Path -Path $certLocation)) { Trace-Execution "$($MyInvocation.InvocationName) : $CertificateName certificate location: $certLocation was not found" $CertSharePath = Split-Path -Path $certLocation Trace-Execution "$($MyInvocation.InvocationName) : Checking $CertificateName certificate share path: $CertSharePath" if (!(Test-Path -Path $CertSharePath)) { Trace-Execution "$($MyInvocation.InvocationName) : $CertificateName certificate share path: $CertSharePath was not found" Trace-Execution "$($MyInvocation.InvocationName) : Creating $CertificateName certificate share path: $CertSharePath" New-Item -Path $CertSharePath -ItemType Directory -Force -ErrorAction Stop Trace-Execution "$($MyInvocation.InvocationName) : Verifying $CertificateName certificate share path: $CertSharePath was created" if (!(Test-Path -Path $CertSharePath)) { Throw "Failed to find and create $CertificateName certificate share path: $CertSharePath" } Trace-Execution "$($MyInvocation.InvocationName) : $CertificateName certificate share path: $CertSharePath was created successfully" } else { Trace-Execution "$($MyInvocation.InvocationName) : $CertificateName certificate share path: $CertSharePath was found" } } Trace-Execution "$($MyInvocation.InvocationName) : exporting the original binary to location $certLocation ..." Set-Content -Value $certBytes -Path $certLocation -Encoding Byte -Force if (!(Test-Path -Path $certLocation)) { Throw "Failed temporary creation of $CertificateName using cert bytes into $certLocation" } Trace-Execution "$($MyInvocation.InvocationName) : importing the original cert ..." $cert = Import-PfxCertificateSafe -Filepath $certLocation -CertStoreLocation cert:\LocalMachine\My -Password $certPwd -Exportable if ($null -eq $cert) { Throw "Failed local import of $CertificateName from share: $certLocation using provided certificate password" } if($protectToGMSA -ne $null) { Trace-Execution "$($MyInvocation.InvocationName) : exporting the cert binary to location $certLocation with Azure Stack internal password and $protectToGMSA account protection..." Export-PfxCertificate -Cert $cert -FilePath $certLocation -Password $exportCertPassword -ProtectTo @("$domainName\$protectToGMSA") } else { Trace-Execution "$($MyInvocation.InvocationName) : exporting the cert binary to location $certLocation with Azure Stack internal password..." Export-PfxCertificate -Cert $cert -FilePath $certLocation -Password $exportCertPassword } if (!(Test-Path -Path $certLocation)) { Throw "Failed exporting $CertificateName to certificate location $certLocation" } } catch { Remove-Item $certLocation -Force -Confirm:$false -Verbose -ErrorAction SilentlyContinue throw } } <# .Synopsis Check whether a dev cert is present indicating this is an internal lab environment. #> function Test-DevCertPresent { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [CloudEngine.Configurations.EceInterfaceParameters] $Parameters ) $invokeCommandParams = @{ ScriptBlock = { Get-ChildItem "Cert:\LocalMachine\My" | where Subject -match "microsoftazurestacksupportteam" } } # Depending on where this code is running, we can check for the dev certificate in a different location. if ($env:ComputerName -eq $Parameters.Roles["DeploymentMachine"].PublicConfiguration.Nodes.Node.Name) { $invokeCommandParams.ComputerName = $Parameters.Roles["BareMetal"].PublicConfiguration.Nodes.Node.Name | select -First 1 Trace-Execution "Testing for presence of dev certificate on $($invokeCommandParams.ComputerName)." } else { Trace-Execution "Testing for presence of dev certificate on $env:ComputerName." } if (Invoke-Command @invokeCommandParams) { Trace-Execution "Found the dev certificate, indicating this is an internal environment." return $true } Trace-Execution "Dev certificate not found." return $false } <# Workaround for NC certificates requiring CRL. Publish CRL directly to local "Root" store on NC. #> function Publish-CertificateRevocationLists([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string[]] $computerNames, [PSCredential] $Credential) { Import-Module -Name "$PSScriptRoot\..\..\Common\StorageHelpers.psm1" -Force -DisableNameChecking -Verbose:$false | Out-Null $ErrorActionPreference = "Stop" # Prepare parameters: $ascaRole = $Parameters.Roles["ASCA"].PublicConfiguration $ASCAConfiguration = $ascaRole.PublicInfo.ASCAConfiguration $clusterName = Get-ManagementClusterName $Parameters $originCRLFolder = Get-SharePath $Parameters $ASCAConfiguration.ArtifactsDirectory $clusterName -FromNoneClusterNode $originCRL = "$originCRLFolder\*.crl" $Session = New-PSSession -ComputerName $computerNames -Credential $Credential -Authentication Credssp Invoke-Command -Session $Session -ScriptBlock { if (-not (Test-Path $using:originCRL)) { throw "Unable to access the certificate revocation list: $using:originCRL" } Trace-execution "Calling certutil to publish CRL" certutil -addstore -f Root $using:originCRL } Trace-Execution "Publication of CRL to Root store completed" } Export-ModuleMember -Function AddCertToLocalMachineStore Export-ModuleMember -Function Export-AzSCertificateToShare Export-ModuleMember -Function GenerateSelfSignedCertificate Export-ModuleMember -Function Get-Certs Export-ModuleMember -Function GetSubjectFqdnFromCertificate Export-ModuleMember -Function GetSubjectFqdnFromCertificatePath Export-ModuleMember -Function GetSubjectName Export-ModuleMember -Function GivePermissionToNetworkService Export-ModuleMember -Function Log Export-ModuleMember -Function PrettyTime Export-ModuleMember -Function Publish-CertificateRevocationLists Export-ModuleMember -Function Test-DevCertPresent # SIG # Begin signature block # MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA8+WUYaosYZT2o # XdDSLImsJXHDhOetkWmXzhLDrk1sfKCCDXYwggX0MIID3KADAgECAhMzAAADrzBA # DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA # hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG # 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN # xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL # go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB # tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd # mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ # 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY # 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp # XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn # TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT # e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG # OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O # PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk # ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx # HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt # CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIK/QQQlJnwcCLsYAe2/8aKZN # xwG9DZ3hwAKqdSPZTaywMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAV1/QDvPzKNd+6tM3SJbbOeKFiXqyRlQjbUPyeD0ZprBgkQ4q7J4OEIBN # +p4GEFIXUNQaaMRYcXGlSaM832WvwuCjof3UrcTABqytBZaLgCzqSVjjsbExTdt0 # I1TDmE/+wRVhELu/vzbikdYXT7PmxVJS56YmtZY2USJwLeEW1N2ryoI1cv+5yzz8 # rpg/ILD1rP2jjQEqe54qqV2eJJ/xfTWAXyZQ+xMPru0/YqClbjZwDlPZGspwX7GN # tpTjOr+SgUQFm1njZOauQBMDaNlzeew9SxlVdsFCqgyRhG6UdB4wxDihTkUQPCvb # W8Mt92US8MW3tcndID0VahwBq7LsM6GCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC # F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCCpUF7x+vffXL7PXkJDfJdI1q+R0U6/NxkFAOiOgTE9QQIGZmr7DoRG # GBMyMDI0MDcwOTA4NTQzMC42NzhaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046ODYwMy0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHqMIIHIDCCBQigAwIBAgITMwAAAfGzRfUn6MAW1gABAAAB8TANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1 # NTVaFw0yNTAzMDUxODQ1NTVaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046ODYwMy0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCxulCZttIf8X97rW9/J+Q4Vg9PiugB1ya1/DRxxLW2 # hwy4QgtU3j5fV75ZKa6XTTQhW5ClkGl6gp1nd5VBsx4Jb+oU4PsMA2foe8gP9bQN # PVxIHMJu6TYcrrn39Hddet2xkdqUhzzySXaPFqFMk2VifEfj+HR6JheNs2LLzm8F # DJm+pBddPDLag/R+APIWHyftq9itwM0WP5Z0dfQyI4WlVeUS+votsPbWm+RKsH4F # QNhzb0t/D4iutcfCK3/LK+xLmS6dmAh7AMKuEUl8i2kdWBDRcc+JWa21SCefx5SP # hJEFgYhdGPAop3G1l8T33cqrbLtcFJqww4TQiYiCkdysCcnIF0ZqSNAHcfI9SAv3 # gfkyxqQNJJ3sTsg5GPRF95mqgbfQbkFnU17iYbRIPJqwgSLhyB833ZDgmzxbKmJm # dDabbzS0yGhngHa6+gwVaOUqcHf9w6kwxMo+OqG3QZIcwd5wHECs5rAJZ6PIyFM7 # Ad2hRUFHRTi353I7V4xEgYGuZb6qFx6Pf44i7AjXbptUolDcVzYEdgLQSWiuFajS # 6Xg3k7Cy8TiM5HPUK9LZInloTxuULSxJmJ7nTjUjOj5xwRmC7x2S/mxql8nvHSCN # 1OED2/wECOot6MEe9bL3nzoKwO8TNlEStq5scd25GA0gMQO+qNXV/xTDOBTJ8zBc # GQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFLy2xe59sCE0SjycqE5Erb4YrS1gMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQDhSEjSBFSCbJyl3U/QmFMW2eLPBknnlsfI # D/7gTMvANEnhq08I9HHbbqiwqDEHSvARvKtL7j0znICYBbMrVSmvgDxU8jAGqMyi # LoM80788So3+T6IZV//UZRJqBl4oM3bCIQgFGo0VTeQ6RzYL+t1zCUXmmpPmM4xc # ScVFATXj5Tx7By4ShWUC7Vhm7picDiU5igGjuivRhxPvbpflbh/bsiE5tx5cuOJE # JSG+uWcqByR7TC4cGvuavHSjk1iRXT/QjaOEeJoOnfesbOdvJrJdbm+leYLRI67N # 3cd8B/suU21tRdgwOnTk2hOuZKs/kLwaX6NsAbUy9pKsDmTyoWnGmyTWBPiTb2rp # 5ogo8Y8hMU1YQs7rHR5hqilEq88jF+9H8Kccb/1ismJTGnBnRMv68Ud2l5LFhOZ4 # nRtl4lHri+N1L8EBg7aE8EvPe8Ca9gz8sh2F4COTYd1PHce1ugLvvWW1+aOSpd8N # nwEid4zgD79ZQxisJqyO4lMWMzAgEeFhUm40FshtzXudAsX5LoCil4rLbHfwYtGO # pw9DVX3jXAV90tG9iRbcqjtt3vhW9T+L3fAZlMeraWfh7eUmPltMU8lEQOMelo/1 # ehkIGO7YZOHxUqeKpmF9QaW8LXTT090AHZ4k6g+tdpZFfCMotyG+E4XqN6ZWtKEB # QiE3xL27BDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy # MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg # M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF # dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 # GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp # Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu # yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E # XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 # lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q # GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ # +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA # PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw # EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG # NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV # MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK # BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG # 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x # M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC # VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 # xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM # nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS # PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d # Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn # GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs # QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL # jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL # 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN # MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjg2MDMtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQD7 # n7Bk4gsM2tbU/i+M3BtRnLj096CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6jcVxjAiGA8yMDI0MDcwOTAxNDk1 # OFoYDzIwMjQwNzEwMDE0OTU4WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDqNxXG # AgEAMAcCAQACAmAhMAcCAQACAhNnMAoCBQDqOGdGAgEAMDYGCisGAQQBhFkKBAIx # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI # hvcNAQELBQADggEBAH1meOWbRbgkpJMb4MIsjtn76EFDhx7b56yDTentE/0L6N10 # YhyT43SFWajQqPW9Tgk9Jq+dOt5CFAzTd1hk1rhNfbRjpZxPpaaGGapSG5Y6UyPW # GNkCcuYo8uF0X1DXv9d9J93ZxYsfKQUVvNTvGaRBTzGGywv5JmHvMyc6VhwaiTPb # id03YGg2guVRvqRZ9ytX75X/fJ40FFxfUyN+1xGXbbhgtr4dh9o2Oo2ZPylKS+PW # DMBOpkS52hLBaoXukUDZop04ZGUPXxGmclrNp63DXatJOrf3SAiWzINR7a8GALrp # CsnkHOkV2b4t5DKw8E+ZP+FVYybLP7UJWUwLnEMxggQNMIIECQIBATCBkzB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAfGzRfUn6MAW1gABAAAB8TAN # BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G # CSqGSIb3DQEJBDEiBCB8+1m1GlOD+xFNvAucgEWNoQ8ntXeSO5VV5ZVUPo9EYTCB # +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EINV3/T5hS7ijwao466RosB7wwEib # t0a1P5EqIwEj9hF4MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTACEzMAAAHxs0X1J+jAFtYAAQAAAfEwIgQgMF0774NGV8qYIjfJqxxIlRtCvi6+ # qC3wdiM7hmk8c2YwDQYJKoZIhvcNAQELBQAEggIArgXulZg+hCsxggRT6Kj4g07a # ND+4PuTrvQp5XSdJ+EbukizJ0+US5M9UwEKDNU8dqxFAfKS+QgK3qXVtiJU17qow # WBQ3MtRC/k8RyWbqf0SFf7pRG/AetYpqJKoRRfHyLiFZgWheZgTYQJyey/XPw+Wf # c3IMgwOjCmzHLrOYAuOtL2ToqUMSbvp5ceAnB6aLzN8FoOYbxuEugXjJ6n1ZbQ4y # UqjVVO2oFOMkLo2e+2WSeRn0+CJQeAvvAYXSEoFju8gSpWhgxxYEz+QnIcGmJNbc # t/6GzACbsOT7b96L1SVzXHX1biX4drvgL9IxDO+2WW+JsCt91g2H9u8haWxrxOSQ # XsVyszSJw6VTpH3PZD6B5hYExY7cq/LQ+uv/afrDdhTctfVUddBzB8ZPOO+Dy9m+ # WvgBOzBk3q4AlcwCHYwnE28lX8keopyNjskKgpmiA6ZqO185WgoaEM9w7L7N57MR # /VSgrwyCQHCkA8yMcJaXd+PFvSsECR7ab1QN1Hr4NUK6StjKCq/2NS6L5UZ+8q8u # p2Gn9Pw2j4cQhBudmgSh1XcvIP+y6Wkagqm757FTa09AK6QMpk6rA5MDlieNqleO # 0/tIcqFJmTsvWnV4fJxWXsN/kryJ25Y6ll8Bc1/aD9Jjx35Q2FHMwM0c1H00jOIR # tymtmt5f2EMut6/7J6A= # SIG # End signature block |