Encrypting Text (Part 2)

by Oct 17, 2019

This is the second part of our text encryption/decryption series. In the first part you learned how you can safely encrypt text on a machine. Now let’s take a look at the decrypting part.

To successfully decrypt text, you must specify the same encoding that was used during encryption. Based on your encryption parameters, you must specify the same password, and based on the -Scope settings, decryption will work only for you and/or only on the same machine where you encrypted the text.

Here is the function Unprotect-Text. We also copied the Protect-Text function from our last tip so you have both handy:

function Protect-Text
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String]
        $SecretText,
        
        [string]
        $Password='',
        
        [string]
        [ValidateSet('CurrentUser','LocalMachine')]
        $scope = 'CurrentUser',

        [string]
        [ValidateSet('UTF7','UTF8','UTF32','Unicode','ASCII','Default')]
        $Encoding = 'Default',

        [Switch]
        $ReturnByteArray
        
    )
    begin
    {
        Add-Type -AssemblyName System.Security
        if ([string]::IsNullOrEmpty($Password))
        {
            $optionalEntropy = $null
        }
        else
        {
            $optionalEntropy = [System.Text.Encoding]::$Encoding.GetBytes($Password)
        }
    }
    process
    {
        try
        {
            $userData = [System.Text.Encoding]::$Encoding.GetBytes($SecretText)
            $bytes = [System.Security.Cryptography.ProtectedData]::Protect($userData, $optionalEntropy, $scope)
            if ($ReturnByteArray)
            {
                $bytes
            }
            else
            {
                [Convert]::ToBase64String($bytes)
            }
        }
        catch
        {
            throw "Protect-Text: Unable to protect text. $_"
        }
    }
}



function Unprotect-Text
{
    [CmdletBinding(DefaultParameterSetName='Byte')]
    param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="Text", Position=0)]
        [string]
        $EncryptedString,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="Byte", Position=0)]
        [Byte[]]
        $EncryptedBytes,
        
        [string]
        $Password='',
        
        [string]
        [ValidateSet('CurrentUser','LocalMachine')]
        $scope = 'CurrentUser',

        [string]
        [ValidateSet('UTF7','UTF8','UTF32','Unicode','ASCII','Default')]
        $Encoding = 'Default'
        
    )
     begin
    {
        Add-Type -AssemblyName System.Security

        if ([string]::IsNullOrEmpty($Password))
        {
            $optionalEntropy = $null
        }
        else
        {
            $optionalEntropy = [System.Text.Encoding]::$Encoding.GetBytes($Password)
        }
    }
    process
    {
        try
        {
            if ($PSCmdlet.ParameterSetName -eq 'Text')
            {
                $inBytes = [Convert]::FromBase64String($EncryptedString)
            }
            else
            {
                $inBytes = $EncryptedBytes
            }
            $bytes = [System.Security.Cryptography.ProtectedData]::Unprotect($inBytes, $optionalEntropy, $scope)
            [System.Text.Encoding]::$Encoding.GetString($bytes)
        }
        catch
        {
            throw "Unprotect-Text: Unable to unprotect your text. Check optional password, and make sure you are using the same encoding that was used during protection."
        }
    }
}

And here is an example on how to use it:

$text = "This is my secret"

$a = Protect-Text -SecretText $text -scope CurrentUser -Password zumsel 

Unprotect-Text -EncryptedString $a -scope CurrentUser -Password zumsel 

Protect-Text creates a Base64-encoded string that can be decrypted using Unprotect-Text. Omit the -Password parameter if you don’t want to set an additional password. If you don’t specify a password, you get the default protection only, based on -Scope.

To save space, you can use a byte array instead of a Base64-encoded string:

$b = Protect-Text -SecretText $text -scope CurrentUser -ReturnByteArray
Unprotect-Text -EncryptedBytes $b -scope CurrentUser

Twitter This Tip! ReTweet this Tip!