Microsoft.AzureStack.ReadinessChecker.Utilities.psm1
function ConvertTo-DeploymentData { <# .SYNOPSIS Validate JSON file provided .DESCRIPTION Validate JSON file can parsed. .EXAMPLE Test-ValidationJSON -path .\some.json .INPUTS path to json file .OUTPUTS None - logging only #> [CmdletBinding()] param ($path) $thisFunction = $MyInvocation.MyCommand.Name Try { $deploymentData = Get-Content $path -ErrorAction Stop | ConvertFrom-Json Write-AzsReadinessLog -Message ('Validated JSON: {0}' -f $path) -Type Info -Function $thisFunction } Catch { if ($_.exception -like '*Invalid JSON primitive*') { Write-AzsReadinessLog -Message ('Invalid JSON file provided: {0}' -f $path) -Type Error -Function $thisFunction throw ('Invalid JSON file provided: {0}' -f $path) } else { Write-AzsReadinessLog -Message ('Reading JSON file {0} failed with error: {1}' -f $_.exception.message) -Type Error -Function $thisFunction throw ('Reading JSON file {0} failed with error: {1}' -f $_.exception) } } $deploymentData } function Test-CertificateReuse { <# .SYNOPSIS Checks if certificate validation output contains certificates that are reused. .DESCRIPTION During validation certificate are given a unique id, that unique id is compared against the certificate thumbprints to detect reuse. .EXAMPLE PS C:\> Test-CertificateReuse -validationResult $paasCertificateValidationResult Checks if certificate validation output contains certificates that are reused. #> param ($validationResult) $thisFunction = $MyInvocation.MyCommand.Name Write-AzsReadinessLog -Message 'Certificate Reuse Detection started' -Type Info -Function $thisFunction # Write new property to result with ReuseCount $thumbprintHash = @{} $group = $validationResult | Group-Object Thumbprint, CertificateId | Select-Object Name | ForEach-Object {$_.name.split(',')[0]} | Group-Object | Select-Object Name, Count $group | ForEach-Object { $thumbprintHash[$_.Name] = $_.count} foreach ($key in $thumbprintHash.keys) { $validationResult | Where-Object thumbprint -eq $key | Add-Member -NotePropertyName ReuseCount -NotePropertyValue $thumbprintHash[$key] } if ($thumbprintHash.Values -gt 1) { $duplicateErrorMsg = 'Duplicate Certificate Detected. We recommend using seperate certificates for each endpoint.' Write-AzsReadinessLog -Message "`nWARNING: $duplicateErrorMsg `n" -Type Warning -Function $thisFunction -toScreen foreach ($key in $thumbprintHash.keys) { if ($thumbprintHash[$key] -gt 1) { Write-AzsReadinessLog ("`t Thumbprint {0} : Count {1}" -f $key, $thumbprintHash[$key]) -Type Warning -Function $thisFunction -toScreen #inject warning result and failuredetail on ParsePFX test for certificate, for reporting purposes. $validationResult | Where-Object {$_.Thumbprint -eq $key -and $_.Test -eq 'Parse PFX'} | ForEach-Object {$_.result = 'Warning'} $validationResult | Where-Object {$_.Thumbprint -eq $key -and $_.Test -eq 'Parse PFX'} | ForEach-Object {$_.FailureDetail += $duplicateErrorMsg} } } } Write-AzsReadinessLog -Message 'Certificate Reuse Detection Completed' -Type Info -Function $thisFunction $validationResult } function Test-PasswordLength { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [int] $MinimumCharactersInPassword, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [SecureString] $Password, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $CredentialDescription ) if ($Password.Length -lt $MinimumCharactersInPassword) { throw ("Password length cannot be fewer than '{0}' characters, for '{1}'" -f $MinimumCharactersInPassword, $CredentialDescription) } return $true } # Test that the Password has only valid characters, does not contain the username, and satisfies the complexity requirements function Test-PasswordComplexity { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String] $Username, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [SecureString] $Password, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $CredentialDescription ) $unmanagedString = [System.IntPtr]::Zero; try { $unmanagedString = [Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($Password) $plainPassword = [Runtime.InteropServices.Marshal]::PtrToStringUni($unmanagedString) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($unmanagedString) } # Letter, Mark, Symbol, Number, Punctuation allowed if ($plainPassword -cnotmatch "^[\p{L}\p{M}\p{S}\p{N}\p{P}]+$") { throw ("Password contains bad characters. Only Letters, Marks, Symbols, Numbers and Punctuations are allowed. For '{0}'" -f $CredentialDescription) } # Password should not contain the entire username or part of the username if ($Username) { # Passwords may not contain the user's samAccountName (Account Name) value or entire displayName (Full Name value). Both checks are not case sensitive. # The samAccountName is checked in its entirety only to determine whether it is part of the password. # If the samAccountName is less than three characters long, this check is skipped. if ($Username.Length -ge 3 -and $plainPassword.ToLower().Contains($Username.ToLower())) { throw ("Password should not contain username or part of username. For '{0}'" -f $CredentialDescription) } # The displayName is parsed for delimiters: commas, periods, dashes or hyphens, underscores, spaces, pound signs, and tabs. # If any of these delimiters are found, the displayName is split and all parsed sections (tokens) are confirmed to not be included in the password. $usernameTokens = $Username.Split( [char]0x2010, # Hyphen [char]0x0009, # Tab [char]0x002C, # Comma [char]0x002E, # Period [char]0x2012, # Figure Dash [char]0x2013, # EN Dash [char]0x2014, # EM Dash [char]0x2015, # Horizontal bar [char]0x2053, # Swung dash [char]0x002D, # Hyphen-Minus [char]0x005F, # Low line [char]0x0020, # Space [char]0x00A3) # Pound Sign foreach ($usernameToken in $usernameTokens) { # Tokens that are less than three characters are ignored, and substrings of the tokens are not checked. if ($usernameToken.Length -ge 3 -and $plainPassword.ToLower().Contains($usernameToken.ToLower())) { throw ("Password should not contain username or part of username. For '{0}'" -f $CredentialDescription) } } } # Validate that password satisifies at least 3 of 5 categories to meet complexity requirements $category_count = 0, 0, 0, 0, 0 for ($i = 0; $i -lt $plainPassword.length; $i++) { # Uppercase letters of European languages (A through Z, with diacritic marks, Greek and Cyrillic characters) if ($plainPassword[$i] -cmatch "^[\p{Lu}]+$") { $category_count[0]++ } # Lowercase letters of European languages (a through z, sharp-s, with diacritic marks, Greek and Cyrillic characters) if ($plainPassword[$i] -cmatch "^[\p{Ll}]+$") { $category_count[1]++ } # Base 10 digits (0 through 9) if ($plainPassword[$i] -cmatch "^[0-9]+$") { $category_count[2]++ } # Non-alphanumeric characters (special characters) (for example, !, $, #, %) if ($plainPassword[$i] -cmatch "^[\p{P}]+$" -or $plainPassword[$i] -cmatch "^[\p{S}]+$") { $category_count[3]++ } # Any Unicode character that is categorized as an alphabetic character but is not uppercase or lowercase. This includes Unicode characters from Asian languages. if ($plainPassword[$i] -cmatch "^[\p{L}]+$" -and $plainPassword[$i] -cnotmatch "^[\p{Lu}]+$" -and $plainPassword[$i] -cnotmatch "^[\p{Ll}]+$") { $category_count[4]++ } } $plainPassword = "" # reset the value, in case it persists $total_category_count = 0 foreach ($count in $category_count) { if ($count -gt 0) { $total_category_count++ } } if ($total_category_count -lt 3) { throw ("Password does not meet complexity requirements. It should contain at least 3 of the following: Uppercase letter, lowercase letter, numbers from 0-9, special characters, alphabetical character that is neither uppercase nor lowercase. For '{0}'" -f $CredentialDescription) } return $true } function Set-SecurityProtocol { param ([Net.SecurityProtocolType]$securityProtocol) $thisFunction = $MyInvocation.MyCommand.Name if ([Net.ServicePointManager]::SecurityProtocol -notmatch $securityProtocol) { Write-AzsReadinessLog -Message ("{0} not found in current Service Point Manager. Current protocol(s): {1}. Attempting to add for session." -f $securityProtocol, [Net.ServicePointManager]::SecurityProtocol) -Type Info -Function $thisFunction try { [Net.ServicePointManager]::SecurityProtocol = $securityProtocol Write-AzsReadinessLog -Message ("Successfully added {0} to Service Point Manager." -f $securityProtocol) -Type Info -Function $thisFunction } catch { Write-AzsReadinessLog -Message ("Setting {0} failed with {1}. Script will continue with existing Security Protocol: {2}" -f $securityProtocol, $_.exception, [Net.ServicePointManager]::SecurityProtocol) -Type Warning -Function $thisFunction } } else { Write-AzsReadinessLog -Message ("{0} found in current Service Point Manager. No action required." -f $securityProtocol) -Type Info -Function $thisFunction } } function Get-SslCertificateChain { <# .SYNOPSIS Retrieve remote ssl certificate & chain from https endpoint for Desktop and Core .NOTES Credit: https://github.com/markekraus #> param ( [system.uri] $url ) try { $cs = @' using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Security; using System.Security.Cryptography.X509Certificates; namespace CertificateCapture { public class Utility { public static Func<HttpRequestMessage,X509Certificate2,X509Chain,SslPolicyErrors,Boolean> ValidationCallback = (message, cert, chain, errors) => { CapturedCertificates.Clear(); var newCert = new X509Certificate2(cert); var newChain = new X509Chain(); newChain.Build(newCert); CapturedCertificates.Add(new CapturedCertificate(){ Certificate = newCert, CertificateChain = newChain, PolicyErrors = errors, URI = message.RequestUri }); return true; }; public static List<CapturedCertificate> CapturedCertificates = new List<CapturedCertificate>(); } public class CapturedCertificate { public X509Certificate2 Certificate { get; set; } public X509Chain CertificateChain { get; set; } public SslPolicyErrors PolicyErrors { get; set; } public Uri URI { get; set; } } } '@ if ($PSEdition -ne 'Core') { Add-Type -AssemblyName System.Net.Http Add-Type $cs -ReferencedAssemblies System.Net.Http } else { Add-Type $cs } Write-AzsReadinessLog -Message ("Reading remote SSL certificates for {0}" -f $url.AbsoluteUri) -Type Info $Certs = [CertificateCapture.Utility]::CapturedCertificates $Handler = [System.Net.Http.HttpClientHandler]::new() $Handler.ServerCertificateCustomValidationCallback = [CertificateCapture.Utility]::ValidationCallback $Client = [System.Net.Http.HttpClient]::new($Handler) $null = $Client.GetAsync($url).Result return $Certs.Certificate } catch { Write-AzsReadinessLog -Message ("Reading remote SSL certificate failed with {0}" -f $_.exception) -Type Error -toScreen } } function Test-Elevation { ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') } # SIG # Begin signature block # MIInvwYJKoZIhvcNAQcCoIInsDCCJ6wCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA2p95N83O2YHqQ # ul2pwIJUXfgUaI8NYmyEIjDqcXvyE6CCDXYwggX0MIID3KADAgECAhMzAAADrzBA # 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 # /Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMNnVJsyD1vKRMPc1Eky3cVD # 3ljlB1cHVoNqZVxs9r89MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAa8GDU6xxCLNceplih0AYx2t8DkwmLE/hq23H0rapgySX42B0OVP+UiCE # Bne900x+ppf3gyRk9FLpI83PREgqSq8s3JtwPYmqRO2ompcVqo0G6IDnDzeO0zRZ # Rq7zOz0Jg5Ih2P6moFs2fBJHnwMQAyvcJtwk0o1KfrBbSr6NEg0vWX/Gsatui1oV # f37nUqSX19ScWiXJ4v5d43kMAXq5huoX7iQDw2+dANrWnhf7ml2ZKEH0aY2Z+5K2 # DfHwcrOJpNrmihZMmdNRjHdbUrZbo5qhJqnZKxSMhj6B1S5AtdJGIhAdevvKxz9E # 3OPl9uAv2r5ye0ja1iCEofr4CHG2JaGCFykwghclBgorBgEEAYI3AwMBMYIXFTCC # FxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq # hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCClFTKQdT9+e/opRGA5ZBH5fHyeN/Kaj6fXT7BBeIdAiQIGZlcWLlbY # GBMyMDI0MDYwODA3MDkwMy4wMDZaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO # OjhENDEtNEJGNy1CM0I3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT # ZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAHj372bmhxogyIAAQAAAeMwDQYJ # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjMx # MDEyMTkwNzI5WhcNMjUwMTEwMTkwNzI5WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl # cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4RDQxLTRC # RjctQjNCNzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL6kDWgeRp+fxSBUD6N/yuEJ # pXggzBeNG5KB8M9AbIWeEokJgOghlMg8JmqkNsB4Wl1NEXR7cL6vlPCsWGLMhyqm # scQu36/8h2bx6TU4M8dVZEd6V4U+l9gpte+VF91kOI35fOqJ6eQDMwSBQ5c9ElPF # UijTA7zV7Y5PRYrS4FL9p494TidCpBEH5N6AO5u8wNA/jKO94Zkfjgu7sLF8SUdr # c1GRNEk2F91L3pxR+32FsuQTZi8hqtrFpEORxbySgiQBP3cH7fPleN1NynhMRf6T # 7XC1L0PRyKy9MZ6TBWru2HeWivkxIue1nLQb/O/n0j2QVd42Zf0ArXB/Vq54gQ8J # IvUH0cbvyWM8PomhFi6q2F7he43jhrxyvn1Xi1pwHOVsbH26YxDKTWxl20hfQLdz # z4RVTo8cFRMdQCxlKkSnocPWqfV/4H5APSPXk0r8Cc/cMmva3g4EvupF4ErbSO0U # NnCRv7UDxlSGiwiGkmny53mqtAZ7NLePhFtwfxp6ATIojl8JXjr3+bnQWUCDCd5O # ap54fGeGYU8KxOohmz604BgT14e3sRWABpW+oXYSCyFQ3SZQ3/LNTVby9ENsuEh2 # UIQKWU7lv7chrBrHCDw0jM+WwOjYUS7YxMAhaSyOahpbudALvRUXpQhELFoO6tOx # /66hzqgjSTOEY3pu46BFAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUsa4NZr41Fbeh # Z8Y+ep2m2YiYqQMwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD # VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG # CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw # MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD # CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBALe+my6p1NPMEW1t # 70a8Y2hGxj6siDSulGAs4UxmkfzxMAic4j0+GTPbHxk193mQ0FRPa9dtbRbaezV0 # GLkEsUWTGF2tP6WsDdl5/lD4wUQ76ArFOencCpK5svE0sO0FyhrJHZxMLCOclvd6 # vAIPOkZAYihBH/RXcxzbiliOCr//3w7REnsLuOp/7vlXJAsGzmJesBP/0ERqxjKu # dPWuBGz/qdRlJtOl5nv9NZkyLig4D5hy9p2Ec1zaotiLiHnJ9mlsJEcUDhYj8PnY # nJjjsCxv+yJzao2aUHiIQzMbFq+M08c8uBEf+s37YbZQ7XAFxwe2EVJAUwpWjmtJ # 3b3zSWTMmFWunFr2aLk6vVeS0u1MyEfEv+0bDk+N3jmsCwbLkM9FaDi7q2HtUn3z # 6k7AnETc28dAvLf/ioqUrVYTwBrbRH4XVFEvaIQ+i7esDQicWW1dCDA/J3xOoCEC # V68611jriajfdVg8o0Wp+FCg5CAUtslgOFuiYULgcxnqzkmP2i58ZEa0rm4LZymH # BzsIMU0yMmuVmAkYxbdEDi5XqlZIupPpqmD6/fLjD4ub0SEEttOpg0np0ra/MNCf # v/tVhJtz5wgiEIKX+s4akawLfY+16xDB64Nm0HoGs/Gy823ulIm4GyrUcpNZxnXv # E6OZMjI/V1AgSAg8U/heMWuZTWVUMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ # mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh # dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1 # WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB # BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK # NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg # fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp # rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d # vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9 # 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR # Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu # qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO # ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb # oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6 # bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t # AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW # BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb # UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku # aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA # QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2 # VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu # bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw # LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt # MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q # XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6 # U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt # I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis # 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp # kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0 # sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e # W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ # sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7 # Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0 # dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ # tB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh # bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4 # RDQxLTRCRjctQjNCNzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAPYiXu8ORQ4hvKcuE7GK0COgxWnqggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOoOGVUwIhgPMjAyNDA2MDgwNzQyMTNaGA8yMDI0MDYwOTA3NDIxM1owdDA6Bgor # BgEEAYRZCgQBMSwwKjAKAgUA6g4ZVQIBADAHAgEAAgIGCTAHAgEAAgIRrDAKAgUA # 6g9q1QIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAIcfeXzdfmHxoZVFTN/I # pIcs8JZflgvYIGQsYO21xVx6ApDfCJejtgM2oLyk5ZSRpP9YuHdwyLeTI3p8ZubG # TFtMvE16gdB7cDh4WBt/hWC5elC/iRqBkEbKYPOaR5yKcTqoLb0ZYV50cS6rcFIR # AisyZcqsZEozFUSab2OJiIFGMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAAHj372bmhxogyIAAQAAAeMwDQYJYIZIAWUDBAIB # BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx # IgQgBjKhSU68ztIzku9bqXtEfq2Q5ueW+0usVR2i4SbHl9MwgfoGCyqGSIb3DQEJ # EAIvMYHqMIHnMIHkMIG9BCAz1COr5bD+ZPdEgQjWvcIWuDJcQbdgq8Ndj0xyMuYm # KjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB49+9 # m5ocaIMiAAEAAAHjMCIEIAAOeqmekOZd2Sv68LuVIsqCiAAxk9dffanqjwM3DYI3 # MA0GCSqGSIb3DQEBCwUABIICABAPV4Ht6KkX4AkUbBNFIA/bwAKOJDPDXnUTYz3i # 5moDh2O18IfZkHtGOnvQxrLcyeMM7xSzAcic3QBboPO0r6MMGiAWw/fZRzh8mV1z # bPL7RfkJPP66QBiQ4z9KaG5bEePobdOE5/FLKrLhGcdBS5u8RmnH6JN1+umJk4a2 # phQ6orp0M157bXrpxSUhbKzFltiAbbEnNYmUHxONKnQEUxSuzBTa2bIHqjIo4OXT # QVdaWo7tVD/pn6zQv7D8BBoc29pPuVVs7Tt+y+8N4aMNkI2VWwlBDZKwKKDdTqE8 # mepOaYvkjWPLMqQ4pOa3G0LMfZzR2xOpeXpbXZAGvagSqulXjgig9ZXv3qKIHXaJ # XzSwNRAej0BQ5vfrN7BRv0FNaorkn/11CYz9tODJonGiobGNWAdiTe17hPTYUz04 # ldghZaZxtxXOd727iup3V195KOKWMkxgLv+zkLS3791oFer4K2XdDdaGezIKbpS+ # tkmOTm477MqxQZWKvEDw6WskVJ5ZsHC9qalS8buz64a0DagLsB7rgPwb3T9utxhU # aQOHySRBNBIUeugyVSOKD/2kKewS4petQGQXTmTWLpVJe96JaKdE9zBWBwxEfHBi # pRtVpRoUe3cmXyKld+IlEuIndyNiv1i0Nss/eOpKyfNgdX6RepUqVssfBNmkPEdH # bMxp # SIG # End signature block |