4 min read
Understanding PowerShell Arrays
PowerShell arrays are a powerful and versatile way of managing collections of data, enabling you to efficiently...
Unlocking the Power of PowerShell: Tips for Success
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.
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)
}
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.
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.
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:
This is where the circle closes, and this series comes to an end:
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:
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.
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.
Jan 7, 2025 by Aleksandar Nikolić and Dr. Tobias Weltner
PowerShell arrays are a powerful and versatile way of managing collections of data, enabling you to efficiently...
Dec 27, 2024 by Aleksandar Nikolić and Dr. Tobias Weltner
Tired of hidden errors in your PowerShell scripts? Discover how validation and transformation attributes can bring...
Dec 20, 2024 by Aleksandar Nikolić and Dr. Tobias Weltner
Want to make your PowerShell scripts faster and more flexible? Learn how to optimize the pipeline with script blocks...
Tobias Weltner and Aleksandar Nikolić joinly wrote the blog post series 'Tobias&Aleksandar's PowerShell tips'. So we introduce both of them here:
----------------------------
Aleksandar Nikolić is a Microsoft Azure MVP and co-founder of PowerShellMagazine.com, the ultimate online source for PowerShell enthusiasts. With over 18 years of experience in system administration, he is a respected trainer and speaker who travels the globe to share his knowledge and skills on Azure, Entra, and PowerShell. He has spoken at IT events such as Microsoft Ignite, ESPC, NIC, CloudBrew, NTK, and PowerShell Conference Europe.
----------------------------
Tobias is a long-time Microsoft MVP and has been involved with the development of PowerShell since its early days. He invented the PowerShell IDE "ISESteroids", has written numerous books on PowerShell for Microsoft Press and O'Reilly, founded the PowerShell Conference EU (psconf.eu), and is currently contributing to the advancement of PowerShell as member in the "Microsoft Cmdlet Working Group". Tobias shares his expertise as a consultant in projects and as a trainer in in-house trainings for numerous companies and agencies across Europe.