RecordCommands.ps1
#requires -Version 5.1 function Get-KeeperRecord { <# .Synopsis Get Keeper Records .Parameter Uid Record UID .Parameter Filter Return matching records only #> [CmdletBinding()] [OutputType([KeeperSecurity.Vault.KeeperRecord[]])] Param ( [string] $Uid, [string] $Filter ) [KeeperSecurity.Vault.VaultOnline]$vault = getVault if ($Uid) { [KeeperSecurity.Vault.KeeperRecord] $record = $null if ($vault.TryGetKeeperRecord($uid, [ref]$record)) { $record } } else { foreach ($record in $vault.KeeperRecords) { if ($Filter) { $match = $($record.Uid, $record.TypeName, $record.Title, $record.Notes) | Select-String $Filter | Select-Object -First 1 if (-not $match) { continue } } $record } } } New-Alias -Name kr -Value Get-KeeperRecord function Copy-KeeperToClipboard { <# .Synopsis Copy record password to clipboard or output .Parameter Record Record UID or any object containing property Uid .Parameter Field Record field to copy to clipboard. Record password is default. .Parameter Output Password output destination. Clipboard is default. Use "Stdout" for scripting #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] $Record, [string] [ValidateSet('Login' , 'Password', 'URL')] $Field = 'Password', [string] [ValidateSet('Clipboard' , 'Stdout')] $Output = 'Clipboard' ) Process { if ($Record -is [Array]) { if ($Record.Count -ne 1) { Write-Error -Message 'Only one record is expected' return } $Record = $Record[0] } [KeeperSecurity.Vault.VaultOnline]$vault = getVault $uid = $null if ($Record -is [String]) { $uid = $Record } elseif ($null -ne $Record.Uid) { $uid = $Record.Uid } $found = $false if ($uid) { [KeeperSecurity.Vault.KeeperRecord] $rec = $null if (-not $vault.TryGetKeeperRecord($uid, [ref]$rec)) { $entries = Get-KeeperChildItem -Filter $uid -ObjectType Record if ($entries.Uid) { $vault.TryGetRecord($entries[0].Uid, [ref]$rec) | Out-Null } } if ($rec) { $found = $true $value = '' if ($rec -is [KeeperSecurity.Vault.PasswordRecord]) { switch ($Field) { 'Login' { $value = $rec.Login } 'Password' { $value = $rec.Password } 'URL' { $value = $rec.Link } } } elseif ($rec -is [KeeperSecurity.Vault.TypedRecord]) { $fieldType = '' switch ($Field) { 'Login' { $fieldType = 'login' } 'Password' { $fieldType = 'password' } 'URL' { $fieldType = 'url' } } if ($fieldType) { $recordField = $rec.Fields | Where-Object FieldName -eq $fieldType | Select-Object -First 1 if (-not $recordField) { $recordField = $rec.Custom | Where-Object FieldName -eq $fieldType | Select-Object -First 1 } if ($recordField) { $value = $recordField.ObjectValue } } } if ($value) { if ($Output -eq 'Stdout') { $value } else { if ([System.Threading.Thread]::CurrentThread.GetApartmentState() -eq [System.Threading.ApartmentState]::MTA) { powershell -sta "Set-Clipboard -Value '$value'" } else { Set-Clipboard -Value $value } Write-Output "Copied to clipboard: $Field for $($rec.Title)" } if ($Field -eq 'Password') { $vault.AuditLogRecordCopyPassword($rec.Uid) } } else { Write-Output "Record $($rec.Title) has no $Field" } } } if (-not $found) { Write-Error -Message "Cannot find a Keeper record: $Record" } } } New-Alias -Name kcc -Value Copy-KeeperToClipboard function Get-KeeperPasswordVisible { <# .Synopsis Show/hide secret fields #> if ($Script:PasswordVisible) { $true } else { $false } } function Set-KeeperPasswordVisible { [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] Param ([switch] $Visible) $Script:PasswordVisible = $Visible.IsPresent } function Show-TwoFactorCode { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] $Records ) Begin { [KeeperSecurity.Vault.VaultOnline]$vault = getVault $totps = @() } Process { foreach ($r in $Records) { $uid = $null if ($r -is [String]) { $uid = $r } elseif ($null -ne $r.Uid) { $uid = $r.Uid } if ($uid) { [KeeperSecurity.Vault.KeeperRecord] $rec = $null if ($vault.TryGetKeeperRecord($uid, [ref]$rec)) { if ($rec -is [KeeperSecurity.Vault.PasswordRecord]) { if ($rec.ExtraFields) { foreach ($ef in $rec.ExtraFields) { if ($ef.FieldType -eq 'totp') { $totps += [PSCustomObject]@{ RecordUid = $rec.Uid Title = $rec.Title TotpData = $ef.Custom['data'] } } } } } elseif ($rec -is [KeeperSecurity.Vault.TypedRecord]) { $recordTypeField = New-Object KeeperSecurity.Vault.RecordTypeField 'oneTimeCode', $null [KeeperSecurity.Vault.ITypedField]$recordField = $null if ([KeeperSecurity.Vault.VaultDataExtensions]::FindTypedField($rec, $recordTypeField, [ref]$recordField)) { $data = $recordField.Value if ($data) { $totps += [PSCustomObject]@{ RecordUid = $rec.Uid Title = $rec.Title TotpData = $data } } } } } } } } End { $output = @() foreach ($totp in $totps) { [Tuple[string, int, int]]$code = [KeeperSecurity.Utils.CryptoUtils]::GetTotpCode($totp.TotpData) if ($code) { $output += [PSCustomObject]@{ PSTypeName = 'TOTP.Codes' RecordTitle = $totp.Title TOTPCode = $code.Item1 Elapsed = $code.Item2 Left = $code.Item3 - $code.Item2 } } } $output | Format-Table } } New-Alias -Name 2fa -Value Show-TwoFactorCode $Keeper_RecordTypeNameCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $result = @() [KeeperSecurity.Vault.VaultOnline]$vault = $Script:Context.Vault if ($vault) { $toComplete = $wordToComplete + '*' foreach ($rt in $vault.RecordTypes) { if ($rt.Name -like $toComplete) { $result += $rt.Name } } } if ($result.Count -gt 0) { return $result } else { return $null } } function Add-KeeperRecord { <# .Synopsis Creates or Modifies a Keeper record in the current folder. .Parameter Uid Record UID. If provided the existing record to be updated. Otherwise record is added. .Parameter RecordType Record Type (if account supports record types). .Parameter Title Record Title. Mandatory field for added record. .Parameter Notes Record Notes. .Parameter GeneratePassword Generate random password. .Parameter Fields A list of record Fields. See DESCRIPTION .DESCRIPTION Record field format [NAME=VALUE] or [-name $value] if field starts with `-` then the following parameter contains field value otherwise NAME=VALUE pattern is assumed Predefined fields are login Login Name password Password url Web Address Any other name is added to Custom Fields Typed records only: A field has [TYPE.LABEL] format. A TYPE or LABEL can be omitted. Field Type Description Value Type Examples =========== ================== ========== ===================================== date Unix epoch time. integer 1668639533000 | 03/23/2022 host host name / port object @{hostName=''; port=''} 192.168.1.2:4321 address Address object @{street1=""; street2=""; city=""; state=""; zip=""; country=""} 123 Main St, SmallTown, CA 12345, USA phone Phone object @{region=""; number=""; ext=""; type=""} Mobile: US (555)555-1234 name Person name object @{first=""; middle=""; last=""} Doe, John Jr. | Jane Doe paymentCard Payment Card object @{cardNumber=""; cardExpirationDate=""; cardSecurityCode=""} 4111111111111111 04/2026 123 bankAccount Bank Account object @{accountType=""; routingNumber=""; accountNumber=""} Checking: 123456789 987654321 keyPair Key Pair object @{publicKey=""; privateKey=""} oneTimeCode TOTP URL string otpauth://totp/Example?secret=JBSWY3DPEHPK3PXP note Masked multiline text string multiline Multiline text string secret Masked text string login Login string email Email string 'name@company.com' password Password string url URL string https://google.com/ text Free form text string This field type generally has a label .EXAMPLE PS> $password = Read-Host -AsSecureString -Prompt "Enter Password" PS> Add-KeeperRecord -Title "New Record" login=username -password $password .EXAMPLE PS> $h = @{hostName='google.com'; port='123'} PS> Add-KeeperRecord -Uid ... -"host.Google Host" $h .EXAMPLE PS> Add-KeeperRecord -Uid ... "host.Google Host=google.com:123" .EXAMPLE PS> $rsa = [System.Security.Cryptography.RSA]::Create(2048) PS> $privateKey = [Convert]::ToBase64String($rsa.ExportPkcs8PrivateKey()) PS> $publicKey = [Convert]::ToBase64String($rsa.ExportRSAPublicKey()) PS> $keyPair = @{privateKey=$privateKey; publicKey=$publicKey} PS> Add-KeeperRecord -Uid ... -keyPair $keyPair #> [CmdletBinding(DefaultParameterSetName = 'add')] Param ( [Parameter()] [switch] $GeneratePassword, [Parameter(ParameterSetName = 'add')] [string] $RecordType, [Parameter(ParameterSetName = 'add')] [string] $Folder, [Parameter(ParameterSetName = 'edit', Mandatory = $True)] [string] $Uid, [Parameter()] [string] $Title, [Parameter()] [string] $Notes, [Parameter(ValueFromRemainingArguments = $true)] $Extra ) Begin { [KeeperSecurity.Vault.VaultOnline]$vault = getVault [KeeperSecurity.Vault.KeeperRecord]$record = $null $fields = @{} $fieldName = $null foreach ($var in $Extra) { if ($var -match '^-') { $fieldName = $var.Substring(1) if ($var -match ':$') { $fieldName = $fieldName.Substring(0, $fieldName.Length - 1) } } elseif ($null -ne $fieldName) { $fields[$fieldName] = $var $fieldName = $null } else { if ($var -match '^([^=]+)=(.*)?') { $n = $Matches[1].Trim() $v = $Matches[2].Trim() if ($n -and $v) { $fields[$n] = $v } } } } } Process { if ($Uid) { if (-not $vault.TryGetKeeperRecord($Uid, [ref]$record)) { $objs = Get-KeeperChildItem -ObjectType Record | Where-Object Name -eq $Uid if ($objs.Length -gt 1) { $vault.TryGetKeeperRecord($objs[0].Uid, [ref]$record) } } if (-not $record) { Write-Error -Message "Record `"$Uid`" not found" -ErrorAction Stop return } } else { if (!$Title) { Write-Error -Message "-Title parameter is required" -ErrorAction Stop } if (-not $RecordType -or $RecordType -eq 'legacy') { $record = New-Object KeeperSecurity.Vault.PasswordRecord } else { $record = New-Object KeeperSecurity.Vault.TypedRecord $RecordType [KeeperSecurity.Utils.RecordTypesUtils]::AdjustTypedRecord($vault, $record) } } if ($Title) { $record.Title = $Title } if ($Notes -is [string]) { if ($Notes.Length -gt 0 -and $Notes[0] -eq '+') { $Notes = $record.Notes + "`n" + $Notes.Substring(1) } elseif ($Notes -eq '-') { $Notes = '' } $record.Notes = $Notes } if ($GeneratePassword.IsPresent) { $fields['password'] = [Keepersecurity.Utils.CryptoUtils]::GenerateUid() } foreach ($fieldName in $fields.Keys) { $fieldValue = $fields[$fieldName] $fieldLabel = '' if ($fieldName -match '^([^.]+)(\..+)?$') { if ($Matches[1] -and $Matches[2]) { $fieldName = $Matches[1].Trim() $fieldLabel = $Matches[2].Trim().Substring(1) } } if ($fieldName -match '^\$') { $fieldName = $fieldName.Substring(1).Trim() } if ($fieldValue -is [securestring]) { $fieldValue = (New-Object PSCredential 'a', $fieldValue).GetNetworkCredential().Password } if ($record -is [KeeperSecurity.Vault.PasswordRecord]) { switch ($fieldName) { 'login' { $record.Login = $fieldValue } 'password' { $record.Password = $fieldValue } 'url' { $record.Link = $fieldValue } Default { if ($fieldLabel) { if ($fieldName -eq 'text') { $fieldName = $fieldLabel } else { $fieldName = "${fieldName}:${fieldLabel}" } } if ($fieldValue) { $record.SetCustomField($fieldName, $fieldValue) | Out-Null } else { $record.DeleteCustomField($fieldName) | Out-Null } } } } elseif ($record -is [KeeperSecurity.Vault.TypedRecord]) { if (-not $fieldLabel) { [KeeperSecurity.Vault.RecordField]$recordField = $null if (-not [KeeperSecurity.Vault.RecordTypesConstants]::TryGetRecordField($fieldName, [ref]$recordField)) { $fieldLabel = $fieldName $fieldName = 'text' } } $recordTypeField = New-Object KeeperSecurity.Vault.RecordTypeField $fieldName, $fieldLabel [KeeperSecurity.Vault.ITypedField]$typedField = $null if ([KeeperSecurity.Vault.VaultDataExtensions]::FindTypedField($record, $recordTypeField, [ref]$typedField)) { } else { if ($fieldValue) { $typedField = [KeeperSecurity.Vault.VaultDataExtensions]::CreateTypedField($fieldName, $fieldLabel) if ($typedField) { $record.Custom.Add($typedField) } } } if ($typedField) { if ($fieldValue) { $typedField.ObjectValue = $fieldValue } else { $typedField.DeleteValueAt(0) } } } } } End { if ($record.Uid) { $task = $vault.UpdateRecord($record) } else { $folderUid = $Script:Context.CurrentFolder if ($Folder) { $folderNode = resolveFolderNode $vault $Folder $folderUid = $folderNode.FolderUid } $task = $vault.CreateRecord($record, $folderUid) } $task.GetAwaiter().GetResult() } } New-Alias -Name kadd -Value Add-KeeperRecord Register-ArgumentCompleter -CommandName Add-KeeperRecord -ParameterName Folder -ScriptBlock $Keeper_FolderPathRecordCompleter Register-ArgumentCompleter -CommandName Add-KeeperRecord -ParameterName RecordType -ScriptBlock $Keeper_RecordTypeNameCompleter function Remove-KeeperRecord { <# .Synopsis Removes Keeper record. .Parameter Name Folder name or Folder UID #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding(DefaultParameterSetName = 'Default')] Param ( [Parameter(Position = 0, Mandatory = $true)][string] $Name ) [KeeperSecurity.Vault.VaultOnline]$vault = getVault $folderUid = $null $recordUid = $null [KeeperSecurity.Vault.KeeperRecord] $record = $null if ($vault.TryGetKeeperRecord($Name, [ref]$record)) { $recordUid = $record.Uid if (-not $vault.RootFolder.Records.Contains($recordUid)) { foreach ($f in $vault.Folders) { if ($f.Records.Contains($recordUid)) { $folderUid = $f.FolderUid break } } } } if (-not $recordUid) { $objs = Get-KeeperChildItem -ObjectType Record | Where-Object Name -eq $Name if (-not $objs) { Write-Error -Message "Record `"$Name`" does not exist" return } if ($objs.Length -gt 1) { Write-Error -Message "There are more than one records with name `"$Name`". Use Record UID do delete the correct one." return } $recordUid = $objs[0].Uid $folderUid = $Script:Context.CurrentFolder } $recordPath = New-Object KeeperSecurity.Vault.RecordPath $recordPath.RecordUid = $recordUid $recordPath.FolderUid = $folderUid $task = $vault.DeleteRecords(@($recordPath)) $task.GetAwaiter().GetResult() | Out-Null } New-Alias -Name kdel -Value Remove-KeeperRecord function Move-RecordToFolder { <# .Synopsis Moves records to Folder. .Parameter Record Record UID, Path or any object containing property Uid. .Parameter Folder Folder Name, Path, or UID #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)]$Records, [Parameter(Position = 0, Mandatory = $true)][string]$Folder, [Parameter()][switch]$Link ) Begin { [KeeperSecurity.Vault.VaultOnline]$vault = getVault $folderNode = resolveFolderNode $vault $Folder $sourceRecords = @() } Process { foreach ($r in $Records) { if ($null -ne $r.Uid) { $r = $r.Uid } [KeeperSecurity.Vault.FolderNode]$folder = $null [KeeperSecurity.Vault.KeeperRecord]$record = $null if ($vault.TryGetKeeperRecord($r, [ref]$record)) { if ($record -is [KeeperSecurity.Vault.PasswordRecord] -or $record -is [KeeperSecurity.Vault.TypedRecord]) { if ($folderNode.FolderUid -and $vault.RootFolder.Records.Contains($record.Uid)) { $folder = $vault.RootFolder } else { foreach ($fol in $vault.Folders) { if ($fol.FolderUid -eq $folderNode.FolderUid) { continue } if ($fol.Records.Contains($record.Uid)) { $folder = $fol break } } } } else { Write-Error "`$r`" record type is not supported." -ErrorAction Stop } } else { [KeeperSecurity.Vault.FolderNode]$fol = $null if (-not $vault.TryGetFolder($Script:Context.CurrentFolder, [ref]$fol)) { $fol = $vault.RootFolder } $comps = splitKeeperPath $r $folder, $rest = parseKeeperPath $comps $vault $fol if (-not $rest) { Write-Error "`"$r`" should be a record" -ErrorAction Stop } [KeeperSecurity.Vault.KeeperRecord]$rec = $null foreach ($recordUid in $folder.Records) { if ($vault.TryGetKeeperRecord($recordUid, [ref]$rec)) { if ($rec.Title -eq $rest) { if ($rec -is [KeeperSecurity.Vault.PasswordRecord] -or $rec -is [KeeperSecurity.Vault.TypedRecord]) { $record = $rec break } } } } } if (-not $record -or -not $folder) { Write-Error "Record `"$r`" cannot be found" -ErrorAction Stop } $rp = New-Object KeeperSecurity.Vault.RecordPath $rp.RecordUid = $record.Uid $rp.FolderUid = $folder.FolderUid $sourceRecords += $rp } } End { if (-not $sourceRecords) { Write-Error "There are no records to move" -ErrorAction Stop } $vault.MoveRecords($sourceRecords, $folderNode.FolderUid, $Link.IsPresent).GetAwaiter().GetResult() | Out-Null $vault.ScheduleSyncDown([System.TimeSpan]::FromSeconds(0)).GetAwaiter().GetResult() | Out-Null } } New-Alias -Name kmv -Value Move-RecordToFolder Register-ArgumentCompleter -CommandName Move-RecordToFolder -ParameterName Folder -ScriptBlock $Keeper_FolderPathRecordCompleter function Get-KeeperRecordType { <# .Synopsis Get Record/Field Type Information .Parameter ShowFields Show Field Types .Parameter Name Record Type Name #> [CmdletBinding()] Param ( [switch] $ShowFields, [Parameter(Position = 0, Mandatory = $false)][string] $Name ) [KeeperSecurity.Vault.VaultOnline]$vault = getVault if ($ShowFields.IsPresent) { [KeeperSecurity.Vault.RecordTypesConstants]::RecordFields | Where-Object { -not $Name -or $_.Name -eq $Name } | Sort-Object Name } else { $vault.RecordTypes | Where-Object { -not $Name -or $_.Name -eq $Name } | Sort-Object Name } } New-Alias -Name krti -Value Get-KeeperRecordType function resolveFolderNode { Param ([KeeperSecurity.Vault.VaultOnline]$vault, $path) [KeeperSecurity.Vault.FolderNode]$folder = $null if (-not $vault.TryGetFolder($path, [ref]$folder)) { if (-not $vault.TryGetFolder($Script:Context.CurrentFolder, [ref]$folder)) { $folder = $vault.RootFolder } $comps = splitKeeperPath $path $folder, $rest = parseKeeperPath $comps $vault $folder if ($rest) { Write-Error "Folder $path not found" -ErrorAction Stop } } $folder } # SIG # Begin signature block # MIIR1wYJKoZIhvcNAQcCoIIRyDCCEcQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUfGVRRN+mlsUrq0BIp7TBwCX8 # qPCggg4jMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B # AQwFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVk # IFJvb3QgRzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg # Q0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5 # WRuxiEL1M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJP # DqFX/IiZwZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXz # ENOLsvsI8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bq # HPNlaJGiTUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTC # fMjqGzLmysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaD # G7dqZy3SvUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urO # kfW+0/tvk2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7AD # K5GyNnm+960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4 # R+Z1MI3sMJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlN # Wdt4z4FKPkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0I # U0F8WD1Hs/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYB # Af8CAQAwHQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaA # FOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK # BggrBgEFBQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v # Y3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4 # oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv # b3RHNC5jcmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcN # AQEMBQADggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcT # Ep6QRJ9L/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WT # auPrINHVUHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9 # ntSZz0rdKOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np37 # 5SFTWsPK6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0 # HKKlS43Nb3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL # 6TEa/y4ZXDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+1 # 6oh7cGvmoLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8 # M4+uKIw8y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrF # hsP2JjMMB0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy # 1lKQ/a+FSCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIHazCC # BVOgAwIBAgIQAnNTGQOIer82vZ1cJyDJDjANBgkqhkiG9w0BAQsFADBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg # Q0ExMB4XDTIyMDIwMjAwMDAwMFoXDTI1MDIwMTIzNTk1OVowcDELMAkGA1UEBhMC # VVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMR0wGwYDVQQK # ExRLZWVwZXIgU2VjdXJpdHkgSW5jLjEdMBsGA1UEAxMUS2VlcGVyIFNlY3VyaXR5 # IEluYy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDNgTqmksdjUyKF # 5zWkDyghf0PLWJWdzG0TX2j8B4J55xwt+B17zd4Xc3n0dvmSVAyPQANeN+mP1chf # 4LTRn9h4jWb8Jsfn+JzyRhj/gYINYvBnpRpqoM0z7QC9Ebwj5T61Cogm9EKGcrG+ # Ujh+Z7pTqfSUrHD8NMXhDL/UpVn+w0Pb4qg7o7AH2o94n7u/qTlMGZCs+VCAvhNr # wPABxvFY07YGb9t5/IZlPE8vG3p1vw2SbgREgFWSEQFj6X2CIhSrbiFCW/766/Mq # EX6qm+RyF71fD4d3yShg39guaE9o+TBl1MqVCje4bK/wGoNxCho0I6Z1fBBKloyp # vlx3gPpU7tJJ+KpuIiel9R9dGQuscqKzehPtbRc9Abr9ThN/HrLg1sFFVMdn2oMR # 63QCUdz+B1NuS7Ap8Ti7XvAPJHzEuQDcdMcRbkIfllJVqrb9UXEFwOPzvRU2KrcQ # 42Jlnn4T+WenPx5Nr3o/o08WLhLTicEK1OacEowyRLBmih4Gxpdk3fUAVCEkdvmq # TSydQpl1Bk8V88dxCkB1wMZyFYLNcddBL4kUbwjso/z6f2TtfAVYs/iIRWqs7Xqt # 4F2BBqobOGMymwg6VgVjjzDIgJCZSbjpq2IoVTci5vli6vxgSoZ01fccSaKa4Izm # B7DbobIkIjLgPqpnCkqlHuJj5hQ9twIDAQABo4ICBjCCAgIwHwYDVR0jBBgwFoAU # aDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFCZd3/KEdT2t5WTIFb3TUaM4 # sTikMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0f # BIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU # cnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGg # T4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k # ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZn # gQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BT # MIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln # aWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIx # Q0ExLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAGyDM3Cbxq # Auhr8O2xwOoCSVKmFkXqicwlrugwLW44Y4WX+imvTrGfjj2S99k/4D5H8DgtW/u8 # tOxcCoehTOCIEwP5TLrieHppsqAR4jaJRcdAHOWiJ1bmwQBv/cBU9vaelL0oXxxf # TwD9oDaQNuyq6p+nIJMqbKv33b8AWGe3zq4JwblaFjRDL5lUDNhPx3g/pm7JhnbX # 7QTKydAJvpbuP5cqUH1GEeVMjc5vEELtGNy/fy7Ekm4dndX4IZcFXW5L0Lx8cReB # hIZwA+pzdzTWQYvfxgRMb/j2uY+Tkb6Wz2x9BBS1UXiP2qrs3rhQv8DZRkUSqnko # YD4uJP8gk8BXcIXIThgEF2YCq2hBiwna5Ijbwkmjn1lWwGv15SznTOTnrVApJqB1 # tB2s2ovUNV4CyKDPVr+9/CS6IQJfEZeHYcYLsIga2q5NZCrqZAasBfCwALVkALos # DIWhs33vYLfETMSuk5Hd5JC+hLjVM3ZJwslvnc/wec2r0GNAiZ3a1aweC7NYuzRz # 29Mi/eR/4ylmCltyZqYJ1JcC/g6eY2Q0xkdWc8P0yHfQ/3fe7+AKXXKNjfv858GW # lg1Ck2lvwPdLqJWqj1FwJPiGRCB+WulPe0csTyWnf+ed45TXx69tZ6BZr0Xr2jXu # ybBdJtg0NN0a62xxWrmX42CgsrzHzRm7OzGCAx4wggMaAgEBMH0waTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB # MQIQAnNTGQOIer82vZ1cJyDJDjAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEK # MAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3 # AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQU03CBQGLj6cJe65wN # umVoBjL3/8QwDQYJKoZIhvcNAQEBBQAEggIAoJv1HibDT7PNYI7vU/ws3bt5dKl6 # LJc4Y09CsIRNemb/qiHj1HMed7wp8GKJ6IJW8JCP/DQttalkdvZUN1LUW2Wl/nuv # YIGtIDNixIbobYDDPHJl1fqQhrlziwbBIdFy5IIwRk5V7Q1nve/WkNMcys/hfYkp # kR8Wqibbz488yOsJSmOV/uC+rt6smi0j7KheGWWcFgTlvPREQSCOlSsdpVkV/nyW # KRVIvaRvtfGad+16n6DReK4hwP2c+iqj2oznrromzX88zhun8dkmIpo5d89+y6+6 # 6+6iALGYjXZv0WdeWJL9txd94RJDnkWQXwIvcijUoNOrEeLUqzZRu0nKLOOHivi2 # jUpu5G1K+jUkXIGO8eu0vbZmZzho1/RZx0PcBlMgKf3PsiYbpHCaEo57yOeQGn/m # To4XvHjr+TXc42JdbwYeWz5ECjIJbMow6i+EyHZ8atDrPwhH26BsSl4vTbpwOZ7Q # B0b0IDPOiqTvUgakskJAZPn7X4Jlmm7l0YGbVUkII5jAu/+Z71dANf2/bupHasjJ # zIT1BdTp21UpZi2BbtmqL8tnRrvBMmXtd414mrl4NLbkSv+HAoZpZYIN1veO0M6t # NjOYYRI9fs4vf3Rg5rUyE4KoiO3pkrHjP/0gjVWPb5mDWbjb2zmmRceHBMbZn9pF # y/EDK07f/4H1mjM= # SIG # End signature block |