It often happens that I need to persist a password or another sensitive information strings in a file or database. When it happens I can never recall what was exactly the command I used to do so in the past. That’s why I decided to encapsulate the two operation of encrypting and decrypting a string in a cmdlet so that the next time I can just check my blog post.

A small preface about the operation of encryption. It is based on the ConvertTo-SecureString cmdlet which on its own uses Advanced Encryption Standard (AES) encryption algorithm. Supported key lengths by the AES encryption algorithm in this case are 128, 192, or 256 bits and they do depend on the specified key length.

function Protect-String()
{
    [CmdletBinding()]
    param
    (
        [string][parameter(Mandatory = $true)]$String,
        [string][parameter(Mandatory = $true)]$Key
    )
    BEGIN { }
    PROCESS
    {      
        if (([system.Text.Encoding]::Unicode).GetByteCount($Key) * 8 -notin 128,192,256)
        {
            throw "Given encription key has an invalid lenght. The specified key must have a length of 128, 192, or 256 bits."
        }

        $secureKey = ConvertTo-SecureString -String $Key -AsPlainText -Force
        
        return ConvertTo-SecureString $String -AsPlainText -Force | ConvertFrom-SecureString -SecureKey $secureKey
    }
    END { }
}

As you can see, there are two required parameters, string that you are trying to encrypt and the key to use to encrypt it. As mentioned above, specified key must have a length of 128, 192, or 256 bits. This translate in a string with length respectively equal to 8, 12 or 16 chars. The calculation is simple, strings inside PowerShell are represented as 16-bit Unicode, instances of .NET’s System.String class, thus 16 bits per character. Knowing this, the maths is easy.
For record, if we haven’t specified any key, the Windows Data Protection API (DPAPI) would be used to encrypt the standard string representation and we wouldn’t be capable of decrypting our string on a different computer.
After we invoke our cmdlet, we will get back the encrypted string. We can then persist that information safely in, at example, our configuration file or a database field.

Once we need to read our value back we can use the following cmdlet:

function Unprotect-String()
{
    [CmdletBinding()]
    param
    (
        [string][parameter(Mandatory = $true)]$String,
        [string][parameter(Mandatory = $true)]$Key
    )
    BEGIN { }
    PROCESS
    {
        if (([system.Text.Encoding]::Unicode).GetByteCount($Key) * 8 -notin 128,192,256)
        {
            throw "Given encription key has an invalid lenght. The specified key must have a length of 128, 192, or 256 bits."
        }

        $secureKey = ConvertTo-SecureString -String $Key -AsPlainText -Force
        $secureString = ConvertTo-SecureString $String -SecureKey $secureKey

        $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "dummy", $secureString

        return $credential.GetNetworkCredential().Password
    }
    END { }
}

Passing in the encrypted string and the key that should be used to decrypt the information, this cmdlet will return the decrypted string.

Following an example:

$password = "My strong super password"
$key = '1234567890123456'

$encryptedString = Protect-String $password $key

Unprotect-String $encryptedString $key

You can expect to see outputted console the “My strong super password”.

That’s all folks. Keep your sensitive information safe!

3 thoughts on “Persisting sensitive information with PowerShell”
  1. Hello,
    It doesnt work, returns : ##[error]A parameter cannot be found that matches parameter name ‘ToSession’

    Any ideas?

    Thanks in advance

    1. Hi Kala,

      I just re-run all of the samples I do provide and they work as described. Where is the ToSession parameter coming from? Seems like you are using a Copy-Item cmdlet or similar ones that do accept the session object and you are running an older version of PowerShell (as ToSession parameter is available only on PSv5).

      Mario

    2. It seems you comment ended up in the wrong post. This seems relevant to my WinRm Copy File extension. Also, given the error I do suppose that the build server or the machine you are copying onto, are not running PowerShell v5.

      Mario

Leave a Reply

Your email address will not be published. Required fields are marked *