Framework/Core/SVT/Services/KeyVault.ps1
using namespace Microsoft.Azure.Commands.KeyVault.Models Set-StrictMode -Version Latest class KeyVault: SVTBase { hidden [PSVault] $ResourceObject; KeyVault([string] $subscriptionId, [string] $resourceGroupName, [string] $resourceName): Base($subscriptionId, $resourceGroupName, $resourceName) { $this.GetResourceObject(); } KeyVault([string] $subscriptionId, [SVTResource] $svtResource): Base($subscriptionId, $svtResource) { $this.GetResourceObject(); } hidden [PSVault] GetResourceObject() { if (-not $this.ResourceObject) { $this.ResourceObject = Get-AzureRmKeyVault -VaultName $this.ResourceContext.ResourceName ` -ResourceGroupName $this.ResourceContext.ResourceGroupName if(-not $this.ResourceObject) { throw ("Resource '{0}' not found under Resource Group '{1}'" -f ($this.ResourceContext.ResourceName), ($this.ResourceContext.ResourceGroupName)) } } return $this.ResourceObject; } hidden [ControlResult] CheckAdvancedAccessPolicies([ControlResult] $controlResult) { $accessPolicies = @{}; $accessPolicies.Add("Enable access to Azure Virtual Machines for deployment", $this.ResourceObject.EnabledForDeployment); $accessPolicies.Add("Enable access to Azure Resource Manager for template deployment", $this.ResourceObject.EnabledForTemplateDeployment); $accessPolicies.Add("Enable access to Azure Disk Encryption for volume encryption", $this.ResourceObject.EnabledForDiskEncryption); $controlResult.SetStateData("Key Vault advanced access policies", $accessPolicies); if($this.ResourceObject.EnabledForDeployment -and $this.ResourceObject.EnabledForDiskEncryption -and $this.ResourceObject.EnabledForTemplateDeployment) { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("All Advanced Access Policies are enabled - ["+ $this.ResourceContext.ResourceName +"]" , $accessPolicies)); } elseif($this.ResourceObject.EnabledForDeployment -or $this.ResourceObject.EnabledForDiskEncryption -or $this.ResourceObject.EnabledForTemplateDeployment) { $controlResult.AddMessage([VerificationResult]::Verify, [MessageData]::new("Validate Advanced Access Policies - ["+ $this.ResourceContext.ResourceName +"]" , $accessPolicies)); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("All Advanced Access Policies are disabled - ["+ $this.ResourceContext.ResourceName +"]", $accessPolicies)); } return $controlResult; } hidden [ControlResult] CheckAccessPolicies([ControlResult] $controlResult) { $accessPolicies = $this.ResourceObject.AccessPolicies $allPermisionUsers = $accessPolicies | Where-Object { $_.PermissionsToKeys -eq "All" -and $_.PermissionsToSecrets -eq "All" -and $_.PermissionsToCertificates -eq "All" } if(($allPermisionUsers | Measure-Object).count -gt 0) { $controlResult.VerificationResult = [VerificationResult]::Failed; $controlResult.AddMessage([MessageData]::new("Applications/Users having full control to Key/Secret/Certificate - ["+ $this.ResourceContext.ResourceName +"]" , $allPermisionUsers)); } else { $controlResult.VerificationResult = [VerificationResult]::Verify; $controlResult.SetStateData("Access policies and their assigned permissions to Key/Secret/Certificate", $accessPolicies); } $controlResult.AddMessage([MessageData]::new("Validate access policies and their assigned permissions to Key/Secret/Certificate - ["+ $this.ResourceContext.ResourceName +"]" , $accessPolicies)); return $controlResult; } hidden [ControlResult] CheckKeyHSMProtected([ControlResult] $controlResult) { try { $allKeys = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -ErrorAction Stop if(($allKeys | Measure-Object).Count -gt 0) { $nonHsmKeys = @(); $allKeys | ForEach-Object { Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -IncludeVersions | ForEach-Object { $nonHsmKeys += Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -Version $_.Version | Where-Object { $_.Attributes.KeyType -ne $this.ControlSettings.KeyVault.KeyType }; } } if(($nonHsmKeys | Measure-Object).Count -eq 0) { $controlResult.AddMessage( [VerificationResult]::Passed, [MessageData]::new("All Keys, including previous versions, are protected by HSM for Key Vault - ["+ $this.ResourceContext.ResourceName +"]")); } else { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("Following Keys, including previous versions, are not protected by HSM. Delete the key versions in order to comply." , ($nonHsmKeys | Select-Object Name, Version -ExpandProperty Attributes ))); } } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No Keys found in - ["+ $this.ResourceContext.ResourceName +"]")); } } catch { if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Keys.")); } else { throw $_ } } return $controlResult; } hidden [ControlResult] CheckKeyMinimumOperations([ControlResult] $controlResult) { try { $allKeys = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -ErrorAction Stop if(($allKeys| Measure-Object).Count -gt 0) { $keyDetails = @(); $allKeys | ForEach-Object { Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -IncludeVersions | ForEach-Object { $key = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -Version $_.Version #| Where-Object {$null -eq $_.Attributes.Expires } $keyDetails += $key #rename variable } } $keyOperations = $keyDetails | Select-Object Name, Version, @{Label="Key Operations"; Expression={[system.string]::Join(", ",$_.Key.KeyOps)}} $controlResult.SetStateData("Key Vault key operations", $keyOperations); $controlResult.AddMessage([VerificationResult]::Verify, [MessageData]::new("Verify the operations permitted using Key on - ["+ $this.ResourceContext.ResourceName +"]", ($keyOperations )) ); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No Keys found in - ["+ $this.ResourceContext.ResourceName +"]")); } } catch { if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Keys.")); } else { throw $_ } } return $controlResult; } hidden [ControlResult] CheckAppAuthenticationCertificate([ControlResult] $controlResult) { try{ $outputList = @(); $appList = $this.GetAzureRmKeyVaultApplications() $appList | ForEach-Object { $credentials = Get-AzureRmADAppCredential -ApplicationId $_.ApplicationId $compliance = if (($credentials| Where-Object { $_.Type -eq $this.ControlSettings.KeyVault.ADAppCredentialTypePwd } | Measure-Object).Count -eq 0 ) { "Yes" } else { "No" } ; $output = New-Object System.Object $output | Add-Member -type NoteProperty -name AzureADAppName -Value $_.DisplayName $output | Add-Member -type NoteProperty -name ApplicationId -Value $_.ApplicationId $output | Add-Member -type NoteProperty -name CertificateCredentialCount -Value ($credentials | Where-Object { $_.Type -eq $this.ControlSettings.KeyVault.ADAppCredentialTypeCrt } | Measure-Object ).Count $output | Add-Member -type NoteProperty -name PasswordCredentialCount -Value ($credentials | Where-Object { $_.Type -eq $this.ControlSettings.KeyVault.ADAppCredentialTypePwd } | Measure-Object).Count $output | Add-Member -type NoteProperty -name Compliance -Value $compliance $outputList += $output; } if(($outputList| Measure-Object).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Compliance details of Azure Active Directory applications:", $outputList)); if (($outputList | Where-Object { ($_.Compliance -eq "No") } | Measure-Object ).Count -gt 0) { $controlResult.AddMessage([VerificationResult]::Failed , [MessageData]::new("Remove the password credentials from Azure AD Applications which are non-compliant.") ); } else { $controlResult.VerificationResult = [VerificationResult]::Passed } } else { $controlResult.AddMessage([VerificationResult]::Passed , [MessageData]::new("No Azure AD Applications have access to Key Vault.") ); } } catch { if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Keys.")); } else { throw $_ } } return $controlResult; } hidden [ControlResult] CheckAppsSharingKayVault([ControlResult] $controlResult) { $appList = $this.GetAzureRmKeyVaultApplications() $controlResult.SetStateData("Key Vault sharing app list", $appList); if( ($appList | Measure-Object ).Count -gt 1) { $controlResult.AddMessage([VerificationResult]::Verify, [MessageData]::new("Validate that Azure AD Applications requires access to Key Vault. Total:" + ($appList | Measure-Object ).Count , $appList)); } elseif( ($appList | Measure-Object ).Count -eq 1) { $controlResult.AddMessage([VerificationResult]::Passed, "Only 1 Azure AD Application has access to Key Vault.", $appList); } else { $controlResult.AddMessage([VerificationResult]::Passed, "No Azure AD Applications have access to Key Vault."); } return $controlResult; } hidden [ControlResult] CheckKeyExpirationDate([ControlResult] $controlResult) { $IsKeysCompliant = $True $IsAccessDenied = $False try { $allKeys = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -ErrorAction Stop if(($allKeys| Measure-Object).Count -gt 0) { $keysWithoutExpiry = @(); $keysWithExpiry = @(); $longDurationActiveKeys = @(); $allKeys | ForEach-Object { Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -IncludeVersions | ForEach-Object { $key = Get-AzureKeyVaultKey -VaultName $this.ResourceContext.ResourceName -Name $_.Name -Version $_.Version #| Where-Object { $_.Attributes.Expires -eq $null } if($null -eq $key.Attributes.Expires) { $keysWithoutExpiry += $key } else { $keysWithExpiry += $key } } } if(($keysWithoutExpiry | Measure-Object ).Count -gt 0) { $IsKeysCompliant = $False $controlResult.AddMessage([MessageData]::new("Following Keys, including previous versions, does not have expiry date. Set the expiry date in order to comply.", ($keysWithoutExpiry | Select-Object -Property Name, Version -ExpandProperty Attributes) )); } if(($keysWithExpiry | Measure-Object ).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Keys, including previous versions, have expiry date for Key Vault - ["+$this.ResourceContext.ResourceName+"]", ($keysWithExpiry | Select-Object -Property Name, Version -ExpandProperty Attributes ) )); $keysWithExpiry | ForEach-Object { $startDate = [DateTime] $_.Attributes.Created if($null -ne $_.Attributes.NotBefore) { $startDate = [DateTime] $_.Attributes.NotBefore; } if ((((([DateTime] $_.Attributes.Expires) - $startDate).TotalDays)/($this.ControlSettings.KeyVault.KeyRotationDuration_Days)) -gt 1) { $longDurationActiveKeys += $_ } } if (($longDurationActiveKeys| Measure-Object ).Count -gt 0) { $IsKeysCompliant = $False $controlResult.AddMessage([MessageData]::new("Following Keys, including previous versions, are active for more than $($this.ControlSettings.KeyVault.KeyRotationDuration_Days) Days. Please delete/rotate the keys in order to comply.", ($longDurationActiveKeys | Select-Object -Property Name, Version -ExpandProperty Attributes ) )); } } } else { $controlResult.AddMessage( [MessageData]::new("No Keys found in - ["+ $this.ResourceContext.ResourceName +"]")); } } catch { $IsKeysCompliant = $False $IsAccessDenied = $True if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Keys.")); } else { throw $_ } } $IsSecretCompliant = $True try { $allSecrets = Get-AzureKeyVaultSecret -VaultName $this.ResourceContext.ResourceName -ErrorAction Stop if(($allSecrets| Measure-Object).Count -gt 0) { $secretsWithoutExpiry = @(); $secretsWithExpiry = @(); $longDurationActiveSecrets = @(); $allSecrets | ForEach-Object { Get-AzureKeyVaultSecret -VaultName $this.ResourceContext.ResourceName -Name $_.Name -IncludeVersions | ForEach-Object { $secret = Get-AzureKeyVaultSecret -VaultName $this.ResourceContext.ResourceName -Name $_.Name -Version $_.Version #| Where-Object { $_.Attributes.Expires -eq $null } if( -not ([Helpers]::CheckMember($secret, "Attributes.Expires"))) { $secretsWithoutExpiry += $secret } else { $secretsWithExpiry += $secret } } } if(($secretsWithoutExpiry| Measure-Object ).Count -gt 0) { $IsSecretCompliant = $False $controlResult.AddMessage([MessageData]::new("Following Secrets, including previous versions, does not have expiry date. Set the expiry date in order to comply.", ($secretsWithoutExpiry | Select-Object Name, Version -ExpandProperty Attributes ) )); } if(($secretsWithExpiry| Measure-Object ).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Secrets, including previous versions, have expiry date for Key Vault - ["+ $this.ResourceContext.ResourceName +"]", ( $secretsWithExpiry | Select-Object Name, Version -ExpandProperty Attributes ) )); $secretsWithExpiry | ForEach-Object { $startDate = [DateTime] $_.Attributes.Created if($null -ne $_.Attributes.NotBefore) { $startDate = [DateTime] $_.Attributes.NotBefore; } if ((((([DateTime] $_.Attributes.Expires) - $startDate).TotalDays)/($this.ControlSettings.KeyVault.SecretRotationDuration_Days)) -gt 1) { $longDurationActiveSecrets += $_ } } if (($longDurationActiveSecrets| Measure-Object ).Count -gt 0) { $IsSecretCompliant = $False $controlResult.AddMessage([MessageData]::new("Following Secrets, including previous versions, are active for more than $($this.ControlSettings.KeyVault.SecretRotationDuration_Days) Days. Please delete/renew the secrets in order to comply.", ( $longDurationActiveSecrets | Select-Object Name, Version -ExpandProperty Attributes ) )); } } } else { $controlResult.AddMessage( [MessageData]::new("No Secrets found in - ["+ $this.ResourceContext.ResourceName +"]")); } } catch { $IsSecretCompliant = $False $IsAccessDenied = $True if ($_.Exception.GetType().FullName -eq "Microsoft.Azure.KeyVault.Models.KeyVaultErrorException") { $controlResult.AddMessage([MessageData]::new("Access denied: Read access is required on Key Vault Secrets.")); } else { throw $_ } } #Got exception because of access denied, Keeping status Verify. if(-not $IsAccessDenied) { if($IsKeysCompliant -and $IsSecretCompliant) { $controlResult.VerificationResult = [VerificationResult]::Passed } else { $controlResult.VerificationResult = [VerificationResult]::Failed } } return $controlResult; } hidden [PSObject] GetAzureRmKeyVaultApplications () { $applicationList = @(); $this.ResourceObject.AccessPolicies | ForEach-Object { $svcPrincipal= Get-AzureRmADServicePrincipal -ObjectId $_.ObjectId if($svcPrincipal){ $application = Get-AzureRmADApplication -ApplicationId $svcPrincipal.ApplicationId if($application){ $applicationList += $application } } } return $applicationList; } } # SIG # Begin signature block # MIIkAgYJKoZIhvcNAQcCoIIj8zCCI+8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCnuoLgR+DW6RXJ # a8A0pcCb9fwKUBLlHjlQGn3189ZJs6CCDZMwggYRMIID+aADAgECAhMzAAAAjoeR # pFcaX8o+AAAAAACOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMTYxMTE3MjIwOTIxWhcNMTgwMjE3MjIwOTIxWjCBgzEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9Q # UjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEA0IfUQit+ndnGetSiw+MVktJTnZUXyVI2+lS/qxCv # 6cnnzCZTw8Jzv23WAOUA3OlqZzQw9hYXtAGllXyLuaQs5os7efYjDHmP81LfQAEc # wsYDnetZz3Pp2HE5m/DOJVkt0slbCu9+1jIOXXQSBOyeBFOmawJn+E1Zi3fgKyHg # 78CkRRLPA3sDxjnD1CLcVVx3Qv+csuVVZ2i6LXZqf2ZTR9VHCsw43o17lxl9gtAm # +KWO5aHwXmQQ5PnrJ8by4AjQDfJnwNjyL/uJ2hX5rg8+AJcH0Qs+cNR3q3J4QZgH # uBfMorFf7L3zUGej15Tw0otVj1OmlZPmsmbPyTdo5GPHzwIDAQABo4IBgDCCAXww # HwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0OBBYEFKvI1u2y # FdKqjvHM7Ww490VK0Iq7MFIGA1UdEQRLMEmkRzBFMQ0wCwYDVQQLEwRNT1BSMTQw # MgYDVQQFEysyMzAwMTIrYjA1MGM2ZTctNzY0MS00NDFmLWJjNGEtNDM0ODFlNDE1 # ZDA4MB8GA1UdIwQYMBaAFEhuZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEsw # SaBHoEWGQ2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0Nv # ZFNpZ1BDQTIwMTFfMjAxMS0wNy0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsG # AQUFBzAChkVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p # Y0NvZFNpZ1BDQTIwMTFfMjAxMS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkq # hkiG9w0BAQsFAAOCAgEARIkCrGlT88S2u9SMYFPnymyoSWlmvqWaQZk62J3SVwJR # avq/m5bbpiZ9CVbo3O0ldXqlR1KoHksWU/PuD5rDBJUpwYKEpFYx/KCKkZW1v1rO # qQEfZEah5srx13R7v5IIUV58MwJeUTub5dguXwJMCZwaQ9px7eTZ56LadCwXreUM # tRj1VAnUvhxzzSB7pPrI29jbOq76kMWjvZVlrkYtVylY1pLwbNpj8Y8zon44dl7d # 8zXtrJo7YoHQThl8SHywC484zC281TllqZXBA+KSybmr0lcKqtxSCy5WJ6PimJdX # jrypWW4kko6C4glzgtk1g8yff9EEjoi44pqDWLDUmuYx+pRHjn2m4k5589jTajMW # UHDxQruYCen/zJVVWwi/klKoCMTx6PH/QNf5mjad/bqQhdJVPlCtRh/vJQy4njpI # BGPveJiiXQMNAtjcIKvmVrXe7xZmw9dVgh5PgnjJnlQaEGC3F6tAE5GusBnBmjOd # 7jJyzWXMT0aYLQ9RYB58+/7b6Ad5B/ehMzj+CZrbj3u2Or2FhrjMvH0BMLd7Hald # G73MTRf3bkcz1UDfasouUbi1uc/DBNM75ePpEIzrp7repC4zaikvFErqHsEiODUF # he/CBAANa8HYlhRIFa9+UrC4YMRStUqCt4UqAEkqJoMnWkHevdVmSbwLnHhwCbww # ggd6MIIFYqADAgECAgphDpDSAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv # ZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5 # MDlaFw0yNjA3MDgyMTA5MDlaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIw # MTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQ # TTS68rZYIZ9CGypr6VpQqrgGOBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULT # iQ15ZId+lGAkbK+eSZzpaF7S35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYS # L+erCFDPs0S3XdjELgN1q2jzy23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494H # DdVceaVJKecNvqATd76UPe/74ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZ # PrGMXeiJT4Qa8qEvWeSQOy2uM1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5 # bmR/U7qcD60ZI4TL9LoDho33X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGS # rhwjp6lm7GEfauEoSZ1fiOIlXdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADh # vKwCgl/bwBWzvRvUVUvnOaEP6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON # 7E1JMKerjt/sW5+v/N2wZuLBl4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xc # v3coKPHtbcMojyyPQDdPweGFRInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqw # iBfenk70lrC8RqBsmNLg1oiMCwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMC # AQAwHQYDVR0OBBYEFEhuZOVQBdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQM # HgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1Ud # IwQYMBaAFHItOgIxkEO5FAVO4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0 # dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0Nl # ckF1dDIwMTFfMjAxMV8wM18yMi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUF # BzAChkJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0Nl # ckF1dDIwMTFfMjAxMV8wM18yMi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGC # Ny4DMIGDMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # b3BzL2RvY3MvcHJpbWFyeWNwcy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcA # YQBsAF8AcABvAGwAaQBjAHkAXwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZI # hvcNAQELBQADggIBAGfyhqWY4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4s # PvjDctFtg/6+P+gKyju/R6mj82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKL # UtCw/WvjPgcuKZvmPRul1LUdd5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7 # pKkFDJvtaPpoLpWgKj8qa1hJYx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft # 0N3zDq+ZKJeYTQ49C/IIidYfwzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4 # MnEnGn+x9Cf43iw6IGmYslmJaG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxv # FX1Fp3blQCplo8NdUmKGwx1jNpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG # 0QaxdR8UvmFhtfDcxhsEvt9Bxw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf # 0AApxbGbpT9Fdx41xtKiop96eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkY # S//WsyNodeav+vyL6wuA6mk7r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrv # QQqxP/uozKRdwaGIm1dxVk5IRcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIV # xTCCFcECAQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAA # AI6HkaRXGl/KPgAAAAAAjjANBglghkgBZQMEAgEFAKCBsDAZBgkqhkiG9w0BCQMx # DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq # hkiG9w0BCQQxIgQge9RHvjWx6WBh/RathuAMfeS8Tdm7TAr/uF6K7PXY+UkwRAYK # KwYBBAGCNwIBDDE2MDSgEoAQAEEAegBTAEQASwAyADUAMqEegBxodHRwczovL2Fr # YS5tcy9henNka29zc2RvY3MgMA0GCSqGSIb3DQEBAQUABIIBAJG9ePO1MlB9M4F2 # V83NnEmIUXDdNTQf5ETs7/+XYkCsvPiIX9VOEKr9D9YG9fsBbIj+KFFaIw7O+JOM # NoJ5RBsozubjRfP6prg/NGp90dLqf5kWv4un3FoCPP7hNRYI9Z5XStT4n4W/K476 # ziZblRKaDa86b/ciAoYB6yiD6YI9P8DWDuuy4aWm8fKJhMlxzj4I8Iv0vvn/inYU # SM5q+DZciRcgIOqqxnHudQM3TRi+fM1iCITA5y1KVHGw3yCnSy4RJ9yuX3rb8NWR # WD5+Ks+sHSUnAimC/z7O+anYVWuS4HJQwUgxxEpWtiwvxORQq6JzkAMhMu6Dkzdw # m37o0G6hghNNMIITSQYKKwYBBAGCNwMDATGCEzkwghM1BgkqhkiG9w0BBwKgghMm # MIITIgIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBPQYLKoZIhvcNAQkQAQSgggEsBIIB # KDCCASQCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQg1GzYb5GcjNff # vyVqNjABdVLxnTotDNzFWVYm47Q1DOgCBlmSMx1TlBgTMjAxNzA5MDUwOTM3MTMu # OTk2WjAHAgEBgAIB9KCBuaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0Ug # RVNOOjMxQzUtMzBCQS03QzkxMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNloIIO0DCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcN # AQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAw # BgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEw # MB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCp # HQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVT # JwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q # 6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h # /EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+ # 79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4 # zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAd # BgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBT # AHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgw # FoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov # L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0 # XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0 # cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAx # MC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0G # CCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BT # L2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBs # AGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4IC # AQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efw # eL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt0 # 70IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQi # PM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93F # SguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4a # rgRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qA # xdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995y # fmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaY # LeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL # 32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4 # L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvTX4/edIhJEjCCBNowggPCoAMCAQIC # EzMAAACgGph4PmbYqtcAAAAAAKAwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTAwHhcNMTYwOTA3MTc1NjQ4WhcNMTgwOTA3MTc1NjQ4 # WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UE # CxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOjMxQzUtMzBCQS03Qzkx # MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjANBgkq # hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA52wU4jDRZ9KtBK622yI96CopNe6hQakb # U8w8PfgtvRENeMUEEQVrWEOU5GMkHWUj6cUz05BrFQ7RcEnqs+QiapTlbWMS5s9o # 1H1oNneIeM1ZtkbGvMhKOX/0a4QxOnGr9Ajeaxy3JO4fi1+H6HphTDlmr06jco1D # DXWcuw5AqgsT5SzQdaXICnaH8d0V6T7ovybN4WpSGAKTd/0PEV6J8bYIXqNTJVK+ # lWVDAPS9O6o6tEtsBFJpKi2fzjh/Hjc/OoeQsR5jHlah2GDLv9jRWkR8cIoeU/on # qawxpmsKnXysMJejJraEpcSl8bx1yMmM8BgRQeXPz9gYP00zBCOcGwIDAQABo4IB # GzCCARcwHQYDVR0OBBYEFMiwxPehFQIOYhdwOn5SNpFalHYBMB8GA1UdIwQYMBaA # FNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0YVBDQV8y # MDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENBXzIwMTAt # MDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJ # KoZIhvcNAQELBQADggEBABAJeQeXD5MucL1J1jbwVDnyQPaPP9IJdRzzWudobl06 # 1TgqE2sirzCtFR9B1MxTRbn4v+coz1BfKzXm6h77818Q9ievvSB50lpsvkMITije # +mTemKXeWyZNw2lJJztKPLCgwEyTfUadJoI8vC6Bfca1gjILJald4Grg2+Lhe6oi # YssFtwY9bkW+8sGZfrAL0CyuggARt6snq64iDIQJM9B1ATsKsMuqIi/kBE2nrpD1 # ZHT4zFZsYS6+IVefhBdcu3KRrXngpCDrLgH0H9L/KQyb2vgm4striVBVomm15MBY # aaQoLn0AZfK9ScP6vzPGmUc+G2aJzVZqqzvrZ7V9UlKhggN5MIICYQIBATCB46GB # uaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsG # A1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOjMxQzUtMzBCQS03 # QzkxMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiUKAQEw # CQYFKw4DAhoFAAMVAIQVUWUii4Xre1VA0VgDHA3Fcm/AoIHCMIG/pIG8MIG5MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BS # MScwJQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046NTdGNi1DMUUwLTU1NEMxKzApBgNV # BAMTIk1pY3Jvc29mdCBUaW1lIFNvdXJjZSBNYXN0ZXIgQ2xvY2swDQYJKoZIhvcN # AQEFBQACBQDdWK1ZMCIYDzIwMTcwOTA1MDQ1ODAxWhgPMjAxNzA5MDYwNDU4MDFa # MHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFAN1YrVkCAQAwCgIBAAICCvkCAf8wBwIB # AAICGDswCgIFAN1Z/tkCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoD # AaAKMAgCAQACAxbjYKEKMAgCAQACAwehIDANBgkqhkiG9w0BAQUFAAOCAQEAMiwG # xMv2sBvhEofejqAcEtUvQWYNW9NN81UIv4OySJZz08kx50azc6afv9kwoKM+hTeo # 70gH0Fi+6QFAnhEHgCmBKfaSD7NLpARgOWGdQHXkKoxVuBtBjKJp7jYXgguoYodZ # KLkePFiEgunXZI+EXsrhdmMkeAOeXZtOR0WSU4DzCRxKoeBtUiLN4ouSaQQ5+TPa # gbQrT5R5+HlsrZXQUO/Q059SJuz6nBGKY5pmZW8RFiy+lPyiNwgghDR2fOsRDjbO # 1XChhcDvrPZcECXua4zfS/M/qT48GLPJLgFt+qnj5C7xNiicVn9fMtDk4Gp++lEM # k5SJTl1ZNSPw1lpJ3TGCAvUwggLxAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAAAoBqYeD5m2KrXAAAAAACgMA0GCWCGSAFlAwQCAQUAoIIB # MjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIBIe # ghpIjRCZo0zWQeIRkNyzbgaWDCdxpDXmucT7zvyMMIHiBgsqhkiG9w0BCRACDDGB # 0jCBzzCBzDCBsQQUhBVRZSKLhet7VUDRWAMcDcVyb8AwgZgwgYCkfjB8MQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3Nv # ZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAKAamHg+Ztiq1wAAAAAAoDAWBBTA # 85YJQWPAB1Gd0BMAT0WYJRkPXTANBgkqhkiG9w0BAQsFAASCAQBsY6//SMKGNoFY # fdUE7Jd4xdcF5XFmSFjwggxsRZgXxLsCzo8j/VXzLqD/CL4ScH4fUf+LoHnXkZrh # HCVhxQTgIhKGqdtZy7Xt6wVY0qbsModjycfoycX6tUM9lVf/o57tCVNOipG9AzES # +NxCGHjSrlNqA6/DLPRgOOuOBbl6GfXh2t//PCZ9WFWcnT6atqQlD2sRJmDgUsH7 # Q+4M+16uuaMr+JBryROLgW0dDupQ1soFNcji55Ifk5r2n0bNyQeMd+QfF3R0fidm # za/K9GfXYBVKuwLkvc+x4gF5EiW5DKqbwc7xQ2cuVFtFisG/EnrmbykXn80jiTXQ # z5t1fKeS # SIG # End signature block |