PowerShell Encryption

The other day I needed a quick command-line tool I could use across a bunch of machines a home to encrypt/decrypt some files, without having to go through too much of a hassle.

I happened to have PowerShell installed on all of them, so this quick script made things easy.

Unlike my previous post on the matter, this one doesn't use DPAPI, so you are not tied to an account or computer.

Be warned - if you forget your password, there is no way to recover these!

Encryption

Basically we need a password / secret to be typed and hashed into a fixed-length key, and an initialization vector.

The initialization vector is used to avoid input blocks with identical information coming out as identical output blocks. Any unique value will do, and it need not be a secret, so we prepend it in plaintext in the output file.

The .NET algorithms already generate one on the fly on creation, so we can use that directly.

param ([string]$filename)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$password = Read-Host -Prompt "Enter the file password"

$contentsAsBytes = [System.IO.File]::ReadAllBytes($filename)

# Create a hash of the password.
$passwordBytes = [System.Text.Encoding]::UTF8.GetBytes($password)
$hashObject = [System.Security.Cryptography.SHA256]::Create()
$hashAsBytes = $hashObject.ComputeHash($passwordBytes)

# Create an AES provider, its encryptor, and encrypt the plaintext bytes.
$aes = [System.Security.Cryptography.Aes]::Create()
$iv = $aes.IV
$aes.Key = $hashAsBytes
$encryptor = $aes.CreateEncryptor()
$encryptedBytes = $encryptor.TransformFinalBlock($contentsAsBytes, 0, $contentsAsBytes.Length)

# Prepend the IV in an array and write it out.
$outputBytes = [byte[]]::new($iv.Length + $encryptedBytes.Length)
$iv.CopyTo($outputBytes, 0)
$encryptedBytes.CopyTo($outputBytes, $iv.Length)
[System.IO.File]::WriteAllBytes($filename + '.enc', $outputBytes)

Decryption

Here we simply reverse the process, reading the IV first.

param ([string]$filename)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$password = Read-Host -Prompt "Enter the file password"

$outputBytes = [System.IO.File]::ReadAllBytes($filename)

# Create a hash of the password.
$passwordBytes = [System.Text.Encoding]::UTF8.GetBytes($password)
$hashObject = [System.Security.Cryptography.SHA256]::Create()
$hashAsBytes = $hashObject.ComputeHash($passwordBytes)

# Extracting IV first - 16 bytes
$decryptIV = [byte[]]::new(16)
[System.Array]::Copy($outputBytes, $decryptIV, 16)

# Create an AES provider, its decryptor, and decrypt the encrypted bytes.
$decryptAes = [System.Security.Cryptography.Aes]::Create()
$decryptAes.IV = $decryptIV
$decryptAes.Key = $hashAsBytes
$decryptor = $decryptAes.CreateDecryptor()
$decryptedBytes = $decryptor.TransformFinalBlock($outputBytes, 16, $outputBytes.Length - 16)

# Write it out
[System.IO.File]::WriteAllBytes($filename + '.dec', $decryptedBytes)

References

Here are some handy references for the various commands and .NET APIs that we're using to do this.

Happy encrypting!

Tags:  powershell

Home