Public/Convert-PlaintextToSecure.ps1


Function Convert-PlaintextToSecure {

<#
.SYNOPSIS
    Converts a plaintext string to an encrypted string
 
.DESCRIPTION
    This function converts a plaintext string into an encrypted one that can be saved e.g. in text files.
    It encrypts in two layers:
 
    1. AES-256 via SecureString with a random keyfile (the classic format of this module)
    2. A machine-bound outer layer, so a stolen ciphertext plus keyfile cannot be decrypted
       on another machine. On Windows this uses DPAPI (with the keyfile as additional entropy),
       on Linux/macOS keys derived from the keyfile and the machine id.
 
.PARAMETER String
    The string you want to encrypt
 
.PARAMETER Scope
    Machine (default) - decryptable on this machine only, by any account that can read the keyfile
    User - decryptable on this machine only AND only by the current user account
    Portable - legacy single-layer format (keyfile only, no machine binding);
                         use this only if you need to move ciphertexts between machines together with the keyfile
 
.EXAMPLE
    Convert-PlaintextToSecure -String "Hello World"
 
.EXAMPLE
    "Hello World" | Convert-PlaintextToSecure -Scope User
 
.INPUTS
    Decrypted String
 
.OUTPUTS
    Encrypted String
 
.NOTES
    Author: florian.von.bracht@apteco.de
 
#>


    [cmdletbinding()]
    # The whole point of this function is to encrypt a caller-supplied plaintext string; there is no credential to source a SecureString from instead.
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')]
    param(
         [Parameter(Mandatory=$true,ValueFromPipeline)][String]$String
        ,[Parameter(Mandatory=$false)][ValidateSet('Machine','User','Portable')][String]$Scope = 'Machine'
        #,[Parameter(Mandatory=$false)][String]$KeyfilePath = ".\aes.key"
    )

    Begin {

        # Use the default keyfile, if not loaded yet
        If ( $null -eq $Script:keyfile ) {
            $Script:keyfile = $Script:defaultKeyfile
        }

        # Create the file, if not existing yet
        If ( (Test-Path -Path $Script:keyfile) -eq $false ) {
            New-KeyfileRaw -Path $Script:keyfile -ByteLength 32 -Force
        }

    }

    Process {

        $return = ""

        # read key bytes (handles both binary and legacy text format)
        $salt = Read-Keyfile -Path $Script:keyfile

        # inner layer: AES via SecureString with the keyfile
        $stringSecure = ConvertTo-SecureString -String $String -AsPlainText -Force
        $return = ConvertFrom-SecureString $stringSecure -Key $salt
        $stringSecure.Dispose()

        # outer layer: bind the ciphertext to this machine (and optionally this user)
        If ( $Scope -ne 'Portable' ) {
            $return = Protect-MachineBoundString -String $return -KeyBytes $salt -Scope $Scope
        }

        # return
        return $return

    }

    End {

    }

}