Functions/Get-EncryptedString.ps1
## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx #region Function Get-EncryptedString Function Get-EncryptedString { <# .SYNOPSIS Decrypts the specified value by using the specifed decryption key, and outputs the information to the pipeline for further usage. .DESCRIPTION Provides a secure method of using sensitive data within scripts as long as best practices are followed. Keeping decryption keys separate from encrypted data is ideal. .PARAMETER EncryptedData Specifies that the previously encrypted value that will be decrypted. .PARAMETER Path Specifies that the value that will be encrypted. .PARAMETER DecryptionKey Specifies a valid Base64 encoded byte array that will be used to decrypt the encrypted data. .EXAMPLE New-EncryptedString -Value 'YourStringValue' | Get-EncryptedString .EXAMPLE $NewEncryptedStringInfo = New-EncryptedString -Value 'YourStringValue' -Verbose $GetEncryptedStringInfo = Get-EncryptedString -EncryptedData ($NewEncryptedStringInfo.EncryptedData) -DecryptionKey ($NewEncryptedStringInfo.DecryptionKey) -Verbose Write-Output -InputObject ($GetEncryptedStringInfo) | Format-List -Property * .EXAMPLE #Both The encrypted data and the decryption key value are provided as function parameters. [String]$GetEncryptedString_EncryptedData = "76492d1116743f0423413b16050a5345MgB8AHoAbQBaAHIATQAyAHYAUAB4AEsAKwBMAEUAQwBiAC8AQgBNAFkAYgBYAHcAPQA9AHwAOQAzADYANQBkADcAMwAwADEAMgA0ADMAMgA5AGIAYwBhAGMAZABhADgANgA5ADUAMAA1AGEAMQBlAGYAZQAyAGQAOQBjADEAZAA0ADcAYwBhAGYAYgA3ADAAYwBhAGYANwBjAGYAZgAyAGQAMABmAGYANAAyAGYAZQBjADQAMwA=" [String]$GetEncryptedString_DecryptionKey = "mM8h7qOcroCBWYb2yEVD/IFBX2Bswd5sUMW3JFob0BE=" [Switch]$GetEncryptedString_Verbose = $True [Hashtable]$GetEncryptedStringParameters = @{} $GetEncryptedStringParameters.Add('EncryptedData', ($GetEncryptedString_EncryptedData)) $GetEncryptedStringParameters.Add('DecryptionKey', ($GetEncryptedString_DecryptionKey)) $GetEncryptedStringParameters.Add('Verbose', ($GetEncryptedString_Verbose)) $GetEncryptedStringInfo = Get-EncryptedString @GetEncryptedStringParameters Write-Output -InputObject ($GetEncryptedStringInfo) .EXAMPLE #The encrypted data is stored within the previously exported XML file using the 'New-EncryptedString' function and the decryption key value is provided as a function parameter. [System.IO.Fileinfo]$GetEncryptedString_Path = "$($Env:Temp.TrimEnd('\'))\New-EncryptedString\New-EncryptionInfo.xml" [String]$GetEncryptedString_DecryptionKey = '5pjYQzAhBier/52FPL9X1+KJouYbewVs/3UjeeHudjg=' [Switch]$GetEncryptedString_Verbose = $True [Hashtable]$GetEncryptedStringParameters = @{} $GetEncryptedStringParameters.Add('Path', ($GetEncryptedString_Path)) $GetEncryptedStringParameters.Add('DecryptionKey', ($GetEncryptedString_DecryptionKey)) $GetEncryptedStringParameters.Add('Verbose', ($GetEncryptedString_Verbose)) $GetEncryptedStringInfo = Get-EncryptedString @GetEncryptedStringParameters Write-Output -InputObject ($GetEncryptedStringInfo) .EXAMPLE #The decryption key is stored in a unsecure fashion within the previously exported XML file using the 'New-EncryptedString' function. [System.IO.Fileinfo]$GetEncryptedString_Path = "$($Env:Temp.TrimEnd('\'))\New-EncryptedString\New-EncryptionInfo.xml" [Switch]$GetEncryptedString_Verbose = $True [Hashtable]$GetEncryptedStringParameters = @{} $GetEncryptedStringParameters.Add('Path', ($GetEncryptedString_Path)) $GetEncryptedStringParameters.Add('Verbose', ($GetEncryptedString_Verbose)) $GetEncryptedStringInfo = Get-EncryptedString @GetEncryptedStringParameters Write-Output -InputObject ($GetEncryptedStringInfo) .NOTES Do you have processes or scripts that require you to provide a password? Against the desires of your security officer, do you have to save those passwords in plain text, in your scripts? .LINK https://www.sqlshack.com/how-to-secure-your-passwords-with-powershell/ #> [CmdletBinding(ConfirmImpact = 'Low', DefaultParameterSetName = 'String', SupportsShouldProcess = $True)] Param ( [Parameter(Mandatory=$True, ParameterSetName = 'String', ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [String]$EncryptedData, [Parameter(Mandatory=$True, ParameterSetName = 'File')] [ValidateNotNullOrEmpty()] [ValidatePattern('^.*\.xml$')] [ValidateScript({Test-Path -Path $_})] [System.IO.FileInfo]$Path, [Parameter(Mandatory=$True, ParameterSetName = 'String', ValueFromPipelineByPropertyName = $True)] [Parameter(Mandatory=$False, ParameterSetName = 'File', ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [ValidatePattern('^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$')] [String]$DecryptionKey, [Parameter(Mandatory=$False)] [Switch]$ContinueOnError ) Begin { [ScriptBlock]$ErrorHandlingDefinition = { If ([String]::IsNullOrEmpty($_.Exception.Message)) {$ExceptionMessage = "$($_.Exception.Errors.Message -Join "`r`n`r`n")"} Else {$ExceptionMessage = "$($_.Exception.Message)"} [String]$ErrorMessage = "[Error Message: $($ExceptionMessage)]`r`n`r`n[ScriptName: $($_.InvocationInfo.ScriptName)]`r`n[Line Number: $($_.InvocationInfo.ScriptLineNumber)]`r`n[Line Position: $($_.InvocationInfo.OffsetInLine)]`r`n[Code: $($_.InvocationInfo.Line.Trim())]" If ($ContinueOnError.IsPresent -eq $True) { Write-Warning -Message ($ErrorMessage) } ElseIf ($ContinueOnError.IsPresent -eq $False) { Throw ($ErrorMessage) } } Try { $DateTimeLogFormat = 'dddd, MMMM dd, yyyy @ hh:mm:ss.FFF tt' ###Monday, January 01, 2019 @ 10:15:34.000 AM### [ScriptBlock]$GetCurrentDateTimeLogFormat = {(Get-Date).ToString($DateTimeLogFormat)} $DateTimeFileFormat = 'yyyyMMdd' ###20190403### [ScriptBlock]$GetDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)} [ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)} $TextInfo = (Get-Culture).TextInfo #Determine the date and time we executed the function $FunctionStartTime = (Get-Date) [String]$CmdletName = $MyInvocation.MyCommand.Name $LogMessage = "Function `'$($CmdletName)`' is beginning. Please Wait..." Write-Verbose -Message $LogMessage #Define Default Action Preferences $ErrorActionPreference = 'Stop' $LogMessage = "The following parameters and values were provided to the `'$($CmdletName)`' function." Write-Verbose -Message $LogMessage $FunctionProperties = Get-Command -Name $CmdletName $FunctionParameters = $FunctionProperties.Parameters.Keys ForEach ($Parameter In $FunctionParameters) { If (!([String]::IsNullOrEmpty($Parameter))) { $ParameterProperties = Get-Variable -Name $Parameter -ErrorAction SilentlyContinue $ParameterValueCount = $ParameterProperties.Value | Measure-Object | Select-Object -ExpandProperty Count If ($ParameterValueCount -gt 1) { $ParameterValueStringFormat = ($ParameterProperties.Value | ForEach-Object {"`"$($_)`""}) -Join "`r`n" $LogMessage = "$($ParameterProperties.Name):`r`n`r`n$($ParameterValueStringFormat)" } Else { $ParameterValueStringFormat = ($ParameterProperties.Value | ForEach-Object {"`"$($_)`""}) -Join ', ' $LogMessage = "$($ParameterProperties.Name): $($ParameterValueStringFormat)" } Switch ($ParameterProperties.Name) { {([String]::IsNullOrEmpty($_) -eq $False) -and ($_ -inotmatch "^Password$|^PW$|^Passphrase$")} { Write-Verbose -Message $LogMessage } } } } $LogMessage = "Execution of `'$($CmdletName)`' began on $($FunctionStartTime.ToString($DateTimeLogFormat))" Write-Verbose -Message $LogMessage } Catch { $ErrorHandlingDefinition.Invoke() } } Process { Try { [Hashtable]$OutputProperties = [Ordered]@{} Switch ($PSCmdlet.ParameterSetName) { {($_ -imatch 'String')} { [Byte[]]$DecryptionKey = [System.Convert]::FromBase64String($DecryptionKey) [System.Security.SecureString]$DecryptedDataAsSecureString = ConvertTo-SecureString -String ($EncryptedData) -Key ($DecryptionKey) $DecryptedDataAsBSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($DecryptedDataAsSecureString) [String]$DecryptedData = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($DecryptedDataAsBSTR) } {($_ -imatch 'File')} { $EncryptionInfo = Import-CLIXML -Path ($Path.FullName) Switch ($Null -ine $EncryptionInfo) { {($_ -eq $True)} { $EncryptionInfoProperties = $EncryptionInfo | Get-Member | Where-Object {($_.MemberType -imatch '^Property$|^NoteProperty$')} [Boolean]$IsEncryptedDataIncluded = $EncryptionInfoProperties | Where-Object {($_.Name -imatch '^EncryptedData$')} Switch ($False) { {($IsEncryptedDataIncluded)} { $ErrorMessage = "The XML file `"$($Path.FullName)`" does not contain an `"EncryptedData`" property. No further action will be taken." Throw [System.Management.Automation.TerminateException] "$($ErrorMessage)" } } [Boolean]$IsDecryptionKeyIncluded = $EncryptionInfoProperties | Where-Object {($_.Name -imatch '^DecryptionKey$')} Switch ($IsDecryptionKeyIncluded) { {($_ -eq $True)} { [Byte[]]$DecryptionKey = [System.Convert]::FromBase64String($EncryptionInfo.DecryptionKey) } {($_ -eq $False)} { Switch ($PSBoundParameters.ContainsKey('DecryptionKey')) { {($_ -eq $True)} { [Byte[]]$DecryptionKey = [System.Convert]::FromBase64String($DecryptionKey) } {($_ -eq $False)} { [String]$ErrorMessage = @" The decryption key was not detected within `"$($Path.FullName)`". Please specify the decryption key as a function parameter and ensure that the value is a Base64 encoded byte array. Example: `$Null = Add-Type -AssemblyName 'System.Web' [Byte[]]`$EncryptionBytes = [System.Text.Encoding]::Unicode.GetBytes([System.Web.Security.Membership]::GeneratePassword(32, 16)) `$Null = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes(`$EncryptionBytes) `$EncryptionKey = [System.Convert]::ToBase64String(`$EncryptionBytes) Write-Output -InputObject (`$EncryptionKey) "@ Throw [System.Management.Automation.ParameterBindingException] "$($ErrorMessage)" } } } } [String]$EncryptedData = ($EncryptionInfo.EncryptedData) [System.Security.SecureString]$DecryptedDataAsSecureString = ConvertTo-SecureString -String ($EncryptedData) -Key ($DecryptionKey) $DecryptedDataAsBSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($DecryptedDataAsSecureString) [String]$DecryptedData = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($DecryptedDataAsBSTR) $OutputProperties.Add('Path', ($Path)) } {($_ -eq $False)} { $ErrorMessage = "The XML file `"$($Path.FullName)`" does not contain valid data. No further action will be taken." Throw [System.Management.Automation.TerminateException] "$($ErrorMessage)" } } } } $OutputProperties.Add('DecryptedDataAsSecureString', ($DecryptedDataAsSecureString)) $OutputProperties.Add('DecryptedData', ($DecryptedData)) $OutputProperties.Add('ParameterSetName', ($PSCmdlet.ParameterSetName)) } Catch { $ErrorHandlingDefinition.Invoke() } } End { Try { #Write .NET object returned by this function to the powershell pipeline $OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputProperties) Write-Output -InputObject ($OutputObject) #Determine the date and time the function completed execution $FunctionEndTime = (Get-Date) $LogMessage = "Execution of `'$($CmdletName)`' ended on $($FunctionEndTime.ToString($DateTimeLogFormat))" Write-Verbose -Message $LogMessage #Log the total script execution time $FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime) $LogMessage = "Function execution took $($FunctionExecutionTimespan.Hours.ToString()) hour(s), $($FunctionExecutionTimespan.Minutes.ToString()) minute(s), $($FunctionExecutionTimespan.Seconds.ToString()) second(s), and $($FunctionExecutionTimespan.Milliseconds.ToString()) millisecond(s)" Write-Verbose -Message $LogMessage $LogMessage = "Function `'$($CmdletName)`' is completed." Write-Verbose -Message $LogMessage } Catch { $ErrorHandlingDefinition.Invoke() } } } #endregion |