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 # ul2pwIJUXfgUaI8NYmyEIjDqcXvyE6CCDXYwggX0MIID3KADAgECAhMzAAADTrU8 # esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU # p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 # 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm # WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa # +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq # jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk # mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 # TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 # kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d # hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM # pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh # JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX # UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir # IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 # 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A # Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H # tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # 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 # Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMNnVJsyD1vKRMPc1Eky3cVD # 3ljlB1cHVoNqZVxs9r89MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEA1lpmrQKo0g8Lt+kga/0j8FBD6P2/F+c29Cfe3ZRg5wkh4mCoAIsJJilw # Rd+OEAKMjZqs7W3P2D3GqmVXCchjVnKDD+lwZ9nOVTzYVpsUG5nFozi4YySC52gW # dPQr/kOgjn9irKFvJ9AXKeXugTPZEx632CeWnfDJgiUK/pHr7FTbsMUa317185Db # JvkEypSuck5NMRBkhKSFgVVjIJuaEjModP8pDRZrzFKnyJCWIkllAuOaggUpi80B # OYC8D+PrOT7AO8cQp28p29M5P2T6u2vLorDIxkT1ZpIFDkBEW6ykRonLgWTtLg5r # Lu8x25OtKhL8AEHov1zR2CwKg1TwNqGCFykwghclBgorBgEEAYI3AwMBMYIXFTCC # FxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq # hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCAFS4q6sgjelgYiUkpoGFm15UEpqFYXnHjh9Q0ALdI6JQIGZN/N/1lg # GBMyMDIzMDkxODE4MzIxOS4zMTNaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO # OkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT # ZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAG6Hz8Z98F1vXwAAQAAAbowDQYJ # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjIw # OTIwMjAyMjE5WhcNMjMxMjE0MjAyMjE5WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl # cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEMDgyLTRC # RkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIhOFYMzkjWAE9UVnXF9hRGv # 0xBRxc+I5Hu3hxVFXyK3u38xusEb0pLkwjgGtDsaLLbrlMxqX3tFb/3BgEPEC3L0 # wX76gD8zHt+wiBV5mq5BWop29qRrgMJKKCPcpQnSjs9B/4XMFFvrpdPicZDv43FL # gz9fHqMq0LJDw5JAHGDS30TCY9OF43P4d44Z9lE7CaVS2pJMF3L453MXB5yYK/KD # bilhERP1jxn2yl+tGCRguIAsMG0oeOhXaw8uSGOhS6ACSHb+ebi0038MFHyoTNhK # f+SYo4OpSY3xP4+swBBTKDoYP1wH+CfxG6h9fymBJQPQZaqfl0riiDLjmDunQtH1 # GD64Air5k9Jdwhq5wLmSWXjyFVL+IDfOpdixJ6f5o+MhE6H4t31w+prygHmd2UHQ # 657UGx6FNuzwC+SpAHmV76MZYac4uAhTgaP47P2eeS1ockvyhl9ya+9JzPfMkug3 # xevzFADWiLRMr066EMV7q3JSRAsnCS9GQ08C4FKPbSh8OPM33Lng0ffxANnHAAX/ # DE7cHcx7l9jaV3Acmkj7oqir4Eh2u5YxwiaTE37XaMumX2ES3PJ5NBaXq7YdLJwy # SD+U9pk/tl4dQ1t/Eeo7uDTliOyQkD8I74xpVB0T31/67KHfkBkFVvy6wye21V+9 # IC8uSD++RgD3RwtN2kE/AgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUimLm8QMeJa25 # j9MWeabI2HSvZOUwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD # VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG # CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw # MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD # CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAF/I8U6hbZhvDcn9 # 6nZ6tkbSEjXPvKZ6wroaXcgstEhpgaeEwleLuPXHLzEWtuJuYz4eshmhXqFr49lb # AcX5SN5/cEsP0xdFayb7U5P94JZd3HjFvpWRNoNBhF3SDM0A38sI2H+hjhB/VfX1 # XcZiei1ROPAyCHcBgHLyQrEu6mnb3HhbIdr8h0Ta7WFylGhLSFW6wmzKusP6aOlm # nGSac5NMfla6lRvTYHd28rbbCgfSm1RhTgoZj+W8DTKtiEMwubHJ3mIPKmo8xtJI # WXPnXq6XKgldrL5cynLMX/0WX65OuWbHV5GTELdfWvGV3DaZrHPUQ/UP31Keqb2x # jVCb30LVwgbjIvYS77N1dARkN8F/9pJ1gO4IvZWMwyMlKKFGojO1f1wbjSWcA/57 # tsc+t2blrMWgSNHgzDr01jbPSupRjy3Ht9ZZs4xN02eiX3eG297NrtC6l4c/gzn2 # 0eqoqWx/uHWxmTgB0F5osBuTHOe77DyEA0uhArGlgKP91jghgt/OVHoH65g0QqCt # gZ+36mnCEg6IOhFoFrCc0fJFGVmb1+17gEe+HRMM7jBk4O06J+IooFrI3e3PJjPr # Qano/MyE3h+zAuBWGMDRcUlNKCDU7dGnWvH3XWwLrCCIcz+3GwRUMsLsDdPW2OVv # 7v1eEJiMSIZ2P+M7L20Q8aznU4OAMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ # 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 # bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpE # MDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAdqNHe113gCJ87aZIGa5QBUqIwvKggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOiyf1swIhgPMjAyMzA5MTgxNTQ4NDNaGA8yMDIzMDkxOTE1NDg0M1owdDA6Bgor # BgEEAYRZCgQBMSwwKjAKAgUA6LJ/WwIBADAHAgEAAgIJSjAHAgEAAgISJDAKAgUA # 6LPQ2wIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAGK+6UVMbVgt4qWdPk/1 # tYxGjavQWgZ3LPfp9l3mh/tQK2RhpjsBgKJO+VVBXcUW3YQb5qP9g40+jrcIFlfy # vrAK3UpbfuIZ6DJ6AayEF30fseVPvwaqjl/BJlKUL3ofsjEMcZPdpfHQv4Zdj3rr # cWGEIG68RqDIePRRKRZEJtI0MYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAAG6Hz8Z98F1vXwAAQAAAbowDQYJYIZIAWUDBAIB # BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx # IgQgRLQ+TpUSLyf/5lIdXtIhYoiZlhVn2kYCQ9TY3Y8s4RgwgfoGCyqGSIb3DQEJ # EAIvMYHqMIHnMIHkMIG9BCApVb08M25w+tYGWsmlGtp1gy1nPcqWfqgMF3nlWYVz # BTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABuh8/ # GffBdb18AAEAAAG6MCIEIMHkOGC427PqmUI7Oe7xVuezks+e+hMM+17Nfgn9Gbmw # MA0GCSqGSIb3DQEBCwUABIICACdFpLVGNAAuAYPeVdt0O/x0N0d+TF2k0iiijXEd # KfyV7mAgj28424/UFkvheLgMdGwfosQPnGfvlMVvX3yV587a2nFQiHTkyg3W/bKM # pmpHrAjO2B5bAotuaKUO/L/VHkPol5Ya8yrbmXBjvQoUhkoIRSzncANZTl3zR0VL # 3XgXnUJ6yujGdVldWvhCOjBwgA7BgTOIyT6tC9Hy24PD6VgkqGfxVg2ipyzFMS2F # 4TL/ZkSViTYgjalQpSX+IJPkJYSz0LhP5Q+efhhqLCEeFsc92xYUWAB+6VaA2CRo # vlHsqjaW9wpP0bbBAIr0TqxpqLAVH2I7xlBlXwxbRaGJbYMF+aFwrJ0EiKDlDakV # TGFsTY6FtZu+g2D3KGfkZGck7zZsLC4u8Rejo+d8FiqDENijYXoYz2UxsU7FPZzT # KVdgAFH1L/zLDXC2h+g/eMdtb5J3snNujtRRhlRvjYrnA+bW2hh26Bo9Su2ebrXk # +l13GtuwzKTjA/7Z/spfebXOmKqjQP7I2i3M7Du2Q3C7q9qF8yCEen1PJ2HPofJe # Prx0yEY16cX26Zyo/MuedVKfGlbuJ96A+0UOOwzm6TJGrbdKH/yGc8RFnOAAUxwU # LMHhsf1VOyq8b3fedo7S/dAkf9gucfIzMI/1bE4Apof/ksuAaF6oc22GS7sYCHe7 # xtJz # SIG # End signature block |