Skip to the main content.

Unlocking the Power of PowerShell: Tips for Success

Master Advanced Encryption Standard – AES

As you have seen in the previous parts, SecureStrings (and the underlying DPAPI) can strongly AES-encrypt and decrypt secrets, but this functionality is limited to Windows OS. If you are using a different operating system, such as Linux or macOS, you only have access to unsafe encoding by default.

In this final part, we will consolidate all the techniques discussed, allowing you to encrypt secrets directly using the raw encryption libraries that are also employed by DPAPI. This approach provides you with maximum control over the encryption process.

On Windows OS, Microsoft supports all the techniques described below. However, on other platforms, .NET does not yet fully support everything. This limitation explains why SecureStrings are not currently encrypted on non-Windows platforms. Whether the APIs used below are implemented and available on your specific platform is beyond the scope of this article.

 

AES encryption – custom commands

To help you explore and understand symmetric AES encryption, here are a few helper functions. Just run them in PowerShell to use them:


function New-AesManagedObject 
{
  param
  (
    # secret key; it can be a base64-encoded string or a byte array
    [Object]
    $Key = $null,

    # initialization vector (IV), a chunk of random data;     # it can be a base64-encoded string or a byte array
    [Object]
    $RandomData = $null,

    [ValidateSet(128,192,256)]
    [int]
    $KeySize = 256
  )
  $aesManaged = [System.Security.Cryptography.AesManaged]::new()
  $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
  $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
  $aesManaged.BlockSize = 128
  $aesManaged.KeySize = $KeySize
  
  # if random data was submitted..
  if ($RandomData) 
  {
    # ...if it was a string, convert it back to bytes
    if ($RandomData -is [String]) 
    {
      Write-Verbose "Random Initialization Vector (IV) submitted: $RandomData"
      $RandomData = [System.Convert]::FromBase64String($RandomData)
    }
    
    Write-Verbose "Random Initialization Vector (IV) submitted: $RandomData"
    $aesManaged.IV = $RandomData
  }
  
  # if a key was submitted...
  if ($key) 
  {
    # if it was a string, convert it back to bytes
    if ($key -is [string]) 
    {
      Write-Verbose "Key submitted: $Key"
      $Key = [System.Convert]::FromBase64String($Key)
    }

    Write-Verbose "Key submitted: $Key"
    $aesManaged.Key = $Key
  }

  $aesManaged
}

function New-AesKey 
{
  <#
      .SYNOPSIS
      Create a new AES key and return it as a base64-encoded string

      .EXAMPLE
      New-AesKey
      Returns a new AES key in base64 string format
  #>
  param
  (
    [ValidateSet(128,192,256)]
    [int]
    $KeySize = 256
  )

  $aesManaged = New-AesManagedObject @PSBoundParameters
  $aesManaged.GenerateKey()
  [Convert]::ToBase64String($aesManaged.Key)
}

function Protect-String 
{    
  param
  (
    [Parameter(Mandatory)]
    [string]
    $PlainText,

    [Parameter(Mandatory)]
    [string]
    $Key,

    [int]
    $KeySize = 256
  )
  $bytes = [System.Text.Encoding]::UTF8.GetBytes($PlainText)
  $null = $PSBoundParameters.Remove('PlainText')
  $aesManaged = New-AesManagedObject @PSBoundParameters
  $encryptor = $aesManaged.CreateEncryptor()
  $encryptedData = $encryptor.TransformFinalBlock($bytes, 0, $bytes.Length)
  [byte[]]$fullData = $aesManaged.IV + $encryptedData
  $aesManaged.Dispose()
  [Convert]::ToBase64String($fullData)
}

function Unprotect-String 
{
  param
  (
    [Parameter(Mandatory)]
    [string]
    $EncryptedString,

    [Parameter(Mandatory)]
    [string]
    $Key,

    [int]
    $KeySize = 256
  )
  $bytes = [System.Convert]::FromBase64String($EncryptedString)
  $randomData = $bytes[0..15]
  $aesManaged = New-AesManagedObject -Key $key -RandomData $randomData -KeySize $KeySize 
  $decryptor = $aesManaged.CreateDecryptor()
  $unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16)
  $aesManaged.Dispose()
  [System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)

 

Encryption keys

AES encryption is a symmetric encryption method, meaning you use the same key for both encryption and decryption. The level of security is determined by the length of your secret key, which must be one of the supported lengths: 128 bits, 256 bits, or more.



$key128 = New-AesKey -KeySize 128
$key256 = New-AesKey -KeySize 256

$key128
$key256 

 

This is what the keys look like:

kX/Oi8KLqtIpPQJhge9LSQ==
8BiDjKQEmhXHUcuUM7SwgeE0fRjbuZdI+Ix+jT4a7HE=

Each time you run New-AesKey, you receive a fresh and unique key. Note that the keys may have different lengths, depending on the number of bits you requested.

 

Encrypting secrets

With your AES keys, you can now encrypt sensitive information:



$AESencrypted128 = Protect-String -PlainText 'Hello World 123' -Key $key128
$AESencrypted256 = Protect-String -PlainText 'Hello World 123' -Key $key256
$AESencrypted128
$AESencrypted256 

 

The encrypted information looks similar to this:

gpmy1EE0e/Bv55VIR2N6yJMGzXEjGvvS07hxiTueEJM=
cRnS66rW/rfHORJ3mZq2R+LnYvGqQZ+0b33bm9WO6TQ=

Note that the encrypted information is of the same length, regardless of the number of bits in your secret key. The length of the encrypted information depends solely on the length of your text.

 

Decrypting secrets

To decrypt your secrets, you need the same secret key that you used for encryption. Since we are performing basic symmetric encryption, you will encounter the typical challenges associated with safely exchanging the secret key:



[PSCustomObject]@{
  "OK1" = Unprotect-String -EncryptedString $AESencrypted128 -Key $key128
  "Fail (Key Mismatch1)" = Unprotect-String -EncryptedString $AESencrypted128 -Key $key256
  "Fail (Key Mismatch2)" = Unprotect-String -EncryptedString $AESencrypted256 -Key $key128
  "OK2" = Unprotect-String -EncryptedString $AESencrypted256 -Key $key256

Here is the result, illustrating that you can decrypt the information only by using the correct secret key. If you use the wrong key, you won’t receive an exception; instead, you’ll get garbled text. This behavior is beneficial from a security standpoint, as it prevents an attacker from using exceptions to test passwords:

Hello world mismatch wrong secret key

 

This is where the circle closes, and this series comes to an end:

  • SecureStrings use the same AES encryption under the hood when saved or serialized, but this functionality is limited to Windows only (which is very secure).
  • DPAPI (Data Protection API) manages the secret key exchange by using your PC and, optionally, your identity as the secret key for encryption and decryption (on Windows only).
  • The encrypted SecureString is then hex-encoded and can be saved to a file. This occurs when you use Export-Clixml to serialize a SecureString or a PSCredential (which contains a SecureString).
  • On non-Windows systems, where DPAPI is unavailable, SecureStrings skip the encryption part and are just encoded – this is highly insecure.

You can use any of these techniques directly and tailor your own solutions. In this article series, we have provided code examples for all scenarios:

  • Simple built-in solution:
    Using SecureStrings to persist common secrets (like passwords).
  • Reusing built-in simple encryption:
    Accessing DPAPI directly to encrypt and decrypt simple secrets without the need for a secret key exchange.
  • Storing binaries as strings:
    Encoding binary information (such as encrypted secrets, as well as other binary data like images or DLLs) for persistence in strings and text files.
  • Symmetric AES encryption:
    Quickly and efficiently encrypting large quantities of data in a secure manner.

The only thing missing now is the secret key exchange needed to transfer the symmetric AES key from sender to receiver. We will not cover this part because it is already built into PowerShell and is quite straightforward. Simply look at Protect-CmsMessage and Unprotect-CmsMessage: these cmdlets facilitate asymmetric key exchange, allowing for a certificate-based transfer of the secret AES key to the party that needs to decrypt your data.

 

SecureStrings – This was our deep dive


 

Good2know

Your ultimate PowerShell Cheat Sheet

Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.

The poster is available for download and in paper form.

PowerShell Poster 2023

Get your poster here!

 

 

Related links 

 

Related posts

4 min read

Understanding PowerShell Arrays

PowerShell arrays are a powerful and versatile way of managing collections of data, enabling you to efficiently...

5 min read

Enhance PowerShell scripts to validate and transform data

Tired of hidden errors in your PowerShell scripts? Discover how validation and transformation attributes can bring...

4 min read

Embrace the PowerShell pipeline

Want to make your PowerShell scripts faster and more flexible? Learn how to optimize the pipeline with script blocks...

About the author: