SecuredCredential.psm1

<#
 .Synopsis
   Private routine used to decrypt an encrypted wallet

 .Description
   Private routine used to Encrypt the Wallet

 .Parameter walletfile
   The full path of the wallet. This will overwrite the file with encrypted contents of data

  .Parameter keyfile
   The full path for the keyfile

 .Parameter data
   The data to encrypt and place in the wallet file. This uses AES.psm1

  .Example
     Private-EncryptWallet $walletFile $keyfile $data

#>

function Private-EncryptWallet
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="Wallet Filename")] [string]$walletfile,
[parameter(Mandatory=$true,HelpMessage="Key Filename")] [string]$keyFile,
[parameter(Mandatory=$true,HelpMessage="Wallet Data")] $walletData
)

  #probably should test for keyfile is correct, and throw exception.
  #but good enough for now.
  $b64Key = Get-Content -Path $keyFile
  $key = [System.Convert]::FromBase64String($b64Key)
  $crypto = Initialize-AESCryptography $key
  $walletJsonString = ConvertTo-Json $walletData
  if ($walletJsonString[0] -ne '[') {
      $walletJsonString = "[" + $walletJsonString + "]"
  }
  #Write-Host "WalletJsonString before encrypt: $walletJsonString"
  $encryptedString = ConvertTo-AESEncryptedString $crypto $walletJsonString
  $encryptedString | Set-Content -path $walletFile
}

<#
 .Synopsis
   Private Routine to decrypt an encrypted password wallet

 .Description
   Private routine used to Decrypt the Wallet

 .Parameter walletfile
   The full path of the wallet.

  .Parameter keyfile
   The full path for the keyfile

  .Example
     $wallet = Private-DecryptWallet $walletFile $keyfile

#>

function Private-DecryptWallet
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="Wallet Filename")] [string]$walletfile,
[parameter(Mandatory=$true,HelpMessage="Key Filename")] [string]$keyFile
)

  $encryptedWalletData = (Get-Content -Path $walletFile)
  #probably should test for keyfile is correct, and throw exception.
  #but good enough for now.
  $b64Key = Get-Content -Path $keyFile
  $key = [System.Convert]::FromBase64String($b64Key)

  $crypto = Initialize-AESCryptography $key
  $decryptedWalletData = ConvertFrom-AESEncryptedString $crypto $encryptedWalletData
  #Write-Host "decryptedWalletDAta: $decryptedWalletData"
  $walletData = @()
  $walletData += ConvertFrom-Json $decryptedWalletData
  #Write-Host "WalletDAta:$walletData"
  #Write-Host "WalletData.getType(): $($walletData.getType())"
  #Write-Host "WalletData.count:$($walletData.count)"
  Write-Output $walletData
}


<#
 .Synopsis
  New-SecuredCredential creates a secured credential through prompting as needed.

 .Description
   Creates a new securedCredential. If no wallet or keyfile has been established, a
   new wallet and key are generated. The wallet and keyfile are then saved to the filenames.

 .Parameter walletfile
   The full path of the wallet.

  .Parameter keyfile
   The full path for the keyfile

  .Parameter name
   The name of the entry in the Wallet

   .Example
     New-SecuredCredential $walletFile $keyFile
     #prompts for everything else.

#>

function New-SecuredCredential
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="Wallet Filename")] [string]$walletfile,
[parameter(Mandatory=$true,HelpMessage="Key Filename")] [string]$keyFile,
[parameter(Mandatory=$true,HelpMessage="Name")] [string]$name,
[parameter(Mandatory=$false,HelpMessage="Name")] [string]$promptForMore="N"
)

$newCredential = @{
name = $name
}

if ($promptForMore -eq "Y") {
$promptForMore = "N"
  do {
    $fieldName = Read-Host -Prompt "Please enter the name of the parameter to store"
    $isThisSecure = Read-Host -Prompt "Do you want to echo **'s as you type?Y|N"
    if ($isThisSecure -cmatch "Y") {
      $valueAsSecure = Read-Host -Prompt "Please enter the value for $fieldName" -AsSecureString
      $value = (New-Object PSCredential($name,$valueAsSecure)).GetNetworkCredential().Password
    }
    else {
      $value = Read-Host -Prompt "Please enter the value for $fieldName"
    } #else if isThisSecure

    $promptForMore = Read-Host -Prompt "Do you need to add another parameter? Y|N"
    $newCredential.Add($fieldName,$value)
  } #do
    until($promptForMore -cmatch "N")
} #if promptForMore

#Create Keys is KeyFile does not yet exist.
#For the wallet, we expect ONLY 1 KEY and it must be different
#than any other key.
if (-Not (Test-Path $keyFile) -and (-Not (Test-Path $walletFile))) {
    #Initialize the Cryptography engine with a Generated Key and IV
    $crypto = Initialize-AESCryptography
    #Now use this key and IV to Encrypt and Decrypt the String

    #Make sure to store the key somewhere safe - otherwise it's useless.
    #We are NOT using Export-CLIXML/Import-CLIXML
    $crypto.Key
    $b64Key = [System.Convert]::ToBase64String($crypto.Key)
    $b64Key
    $b64Key | Set-Content -Path $keyFile
    $crypto.Dispose()
}

$wallet = @()
if (Test-Path $walletFile)
{
    $wallet += Private-DecryptWallet $walletfile $keyFile
}

$wallet += $newCredential
#Now Encrypt it
$x = Private-EncryptWallet $walletFile $keyFile $wallet

} #New-SecuredCredential


<#
 .Synopsis
   Gets a list of all credentials in the wallet. Provides only "name"

 .Description
  Gets a list of all credentials in the wallet. Provides only "name"

 .Parameter walletfile
   The full path of the wallet.

  .Parameter keyfile
   The full path for the keyfile

  .Example
     $entries = Get-SecuredCredentialList $walletFile $keyfile

#>

Function Get-SecuredCredentialList
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="WalletFile")] [string]$walletFile,
[parameter(Mandatory=$true,HelpMessage="KeyFile")] [string]$keyFile
)
#As long as we have a keyfile and walletFile, go for it
$wallet = @()
if ((Test-Path $keyFile) -and (Test-Path $walletFile)) {
    $wallet += Private-DecryptWallet $walletFile $keyFile
    Write-Output $wallet.name
}

} #Get-SecuredCredentialList


<#
 .Synopsis
   Returns a full entry from the wallet, unencrypted.

 .Description
  Reads and decrypts the wallet in memory and returns the specific entry with given name.

 .Parameter walletfile
   The full path of the wallet.

  .Parameter keyfile
   The full path for the keyfile

  .Parameter name
   The unique entry in the wallet to return.

  .Example
     $credential = Get-SecuredCredential $walletFile $keyfile $name

#>


Function Remove-SecuredCredential
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="WalletFile")] [string]$walletFile,
[parameter(Mandatory=$true,HelpMessage="KeyFile")] [string]$keyFile,
[parameter(Mandatory=$true,HelpMessage="Name")] [string]$name
)
#As long as we have a keyfile and walletFile, go for it
$wallet = @()
$entry
if ((Test-Path $keyFile) -and (Test-Path $walletFile)) {
    $wallet += Private-DecryptWallet $walletFile $keyFile
    $walletList = [System.Collections.ArrayList] $wallet
    $entry = $walletList | Where-Object {$_.name -eq $name}
    $walletList.Remove($entry)
    $wallet = @()
    $wallet += $walletList
    Private-EncryptWallet $walletFile $keyFile $wallet
}

} #Remove-SecuredCredential

Function Get-SecuredCredential
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="WalletFile")] [string]$walletFile,
[parameter(Mandatory=$true,HelpMessage="KeyFile")] [string]$keyFile,
[parameter(Mandatory=$true,HelpMessage="Name")] [string]$name
)
#As long as we have a keyfile and walletFile, go for it
$wallet = @()
$entry
if ((Test-Path $keyFile) -and (Test-Path $walletFile)) {
    $wallet += Private-DecryptWallet $walletFile $keyFile
    $entry = $wallet | Where-Object {$_.name -eq $name}
    Write-output $entry
}

} #Get-SecuredCredentialList


<#
 .Synopsis
   Updates any field of an entry.

 .Description
  Selectively prompts to update any entry in the wallet.
  This is a wrapper for ADD which adds or updates.

 .Parameter walletfile
   The full path of the wallet.

  .Parameter keyfile
   The full path for the keyfile

  .Parameter name
   The name of the entry in the wallet to update.

  .Example
     Update-SecuredCredential $walletFile $keyfile $name

#>

Function Update-SecuredCredentialField
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="WalletFile")] [string]$walletFile,
[parameter(Mandatory=$true,HelpMessage="KeyFile")] [string]$keyFile,
[parameter(Mandatory=$true,HelpMessage="Name")] [string]$name
)
#Add and Update are identical. This is a wrapper calling Add, which Adds or Updates
Add-SecuredCredentialField $walletFile $keyFile $name
} #Update-SecuredCredentialField




<#
 .Synopsis
   Adds or Updates any field of an entry.

 .Description
  Selectively prompts to update any entry in the wallet.
  Adds or Updates if entry and field exists.

 .Parameter walletfile
   The full path of the wallet.

  .Parameter keyfile
   The full path for the keyfile

  .Parameter name
   The name of the entry in the wallet to update.

  .Example
     Update-SecuredCredential $walletFile $keyfile $name

#>

Function Add-SecuredCredentialField
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="WalletFile")] [string]$walletFile,
[parameter(Mandatory=$true,HelpMessage="KeyFile")] [string]$keyFile,
[parameter(Mandatory=$true,HelpMessage="Name")] [string]$name,
[parameter(Mandatory=$false,HelpMessage="FieldName")] [string]$field,
[parameter(Mandatory=$false,HelpMessage="FieldValue")] [string]$value
)
$wallet = @()
#As long as we have a keyfile and walletFile, go for it
if ((Test-Path $keyFile) -and (Test-Path $walletFile)) {
    $wallet += Private-DecryptWallet $walletFile $keyFile
    $node = $wallet | where-object { $_.name -eq $name }
    if ($node -ne $null) {
       if ($field -eq $null) {
          $field = Read-Host -Prompt "What is the fieldName you wish to add?"
          $isThisSecure = Read-Host -Prompt "Do you want to echo **'s as you type?Y|N"
          if ($isThisSecure -cmatch "Y") {
            $valueAsSecure = Read-Host -Prompt "Please enter the value for $field" -AsSecureString
            $value = (New-Object PSCredential($name,$valueAsSecure)).GetNetworkCredential().Password
          }
          else {
            $value = Read-Host -Prompt "Please enter the value for $field"
          }
        }

        #verify it doesn't already exist. Either way add it or update it.
        $x = $node | get-member -MemberType NoteProperty | where-Object { $_.name -eq $field}
        if ($x -eq $null) {
            $node | Add-Member –MemberType NoteProperty –Name $field –Value $value
        }
        else {
            $node.$field = $value
        }
        #Now Encrypt it
        Private-EncryptWallet $walletFile $keyFile $wallet
    }
    else {
        Write-Output "Entry with $name not found"
    }
}

} #Add-SecuredCredentialField

Function Remove-SecuredCredentialField
{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true,HelpMessage="WalletFile")] [string]$walletFile,
[parameter(Mandatory=$true,HelpMessage="KeyFile")] [string]$keyFile,
[parameter(Mandatory=$true,HelpMessage="Name")] [string]$name,
[parameter(Mandatory=$true,HelpMessage="FieldName")] [string]$field
)
$wallet = @()
#As long as we have a keyfile and walletFile, go for it
if ((Test-Path $keyFile) -and (Test-Path $walletFile)) {
    $wallet += Private-DecryptWallet $walletFile $keyFile
    $node = $wallet | where-object { $_.name -eq $name }
    if (-not $node -eq $null) {
        $node = $node | select-Object -ExcludeProperty $field
        #Now Encrypt it
        Private-EncryptWallet $walletFile $keyFile $wallet
    }
    else {
        Write-Output "Entry with $name not found"
    }
}

} #Remove-SecuredCredentialField