Public/Encryption.ps1
|
using namespace System using namespace System.IO using namespace System.Security.Cryptography using namespace System.Collections.Immutable #### # Unprotect-EncryptedFile function Unprotect-EncryptedFile { #### Decrypt a file encrypted by `Protect-FileWithEncryption`. AES-256-CBC with PBKDF2 key derivation. #### #### **Parameters** #### - `[string]`: __EncryptedFilePath__ #### - *Path to the `.enc` file.* #### - `[securestring]`: __FilePassword__ #### - *Password used during encryption.* #### - `[string]`: __OutputFilePath__ #### - *Destination path for the decrypted output.* #### #### **Returns** #### - `[PSCustomObject]` #### - `[string]`: __Status__ #### - *Always `'Success'`.* #### - `[string]`: __EncryptedFile__ #### - *Resolved path of the input.* #### - `[string]`: __DecryptedFile__ #### - *Path of the decrypted output.* #### - `[int]`: __EncryptedFileSizeKB__ #### - *Size of the input in KB.* #### - `[int]`: __DecryptedFileSizeKB__ #### - *Size of the output in KB.* #### - `[string]`: __Salt__ #### - *Base64-encoded salt read from the file header.* #### - `[string]`: __IV__ #### - *Base64-encoded AES IV read from the file header.* [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$EncryptedFilePath, [Parameter(Mandatory = $true)] [securestring]$FilePassword, [Parameter(Mandatory = $true)] [string]$OutputFilePath ) $EncryptedFilePath = Resolve-Path -Path $EncryptedFilePath -ErrorAction Stop try { $encryptedStream = [File]::Open($EncryptedFilePath, 'Open', 'Read') try { $salt = [byte[]]::new(16) $encryptedStream.Read($salt, 0, $salt.Length) | Out-Null $iv = [byte[]]::new(16) $encryptedStream.Read($iv, 0, $iv.Length) | Out-Null $pbkdf2 = [Rfc2898DeriveBytes]::new($FilePassword, $salt, 100000) $key = $pbkdf2.GetBytes(32) $aes = [Aes]::Create() $aes.Key = $key $aes.IV = $iv $decryptor = $aes.CreateDecryptor() $cryptoStream = [CryptoStream]::new($encryptedStream, $decryptor, [CryptoStreamMode]::Read) $outputStream = [File]::Open($OutputFilePath, 'Create', 'Write') try { $buffer = [byte[]]::new(4096) while (($bytesRead = $cryptoStream.Read($buffer, 0, $buffer.Length)) -gt 0) { $outputStream.Write($buffer, 0, $bytesRead) } $dataObject = [PSCustomObject]@{ Status = 'Success' EncryptedFile = $EncryptedFilePath DecryptedFile = $OutputFilePath EncryptedFileSizeKB = (([FileInfo]::new($EncryptedFilePath).Length / 1KB) -as [int]) DecryptedFileSizeKB = (([FileInfo]::new($OutputFilePath).Length / 1KB) -as [int]) Salt = ([Convert]::ToBase64String($salt)) IV = ([Convert]::ToBase64String($iv)) } $dataObject } finally { $outputStream.Close() $cryptoStream.Close() } } finally { $encryptedStream.Close() } } catch { throw } } #### # Protect-FileWithEncryption function Protect-FileWithEncryption { #### Encrypt a file with AES-256-CBC. Derives a 256-bit key from `SecureKey` via PBKDF2 (100 000 iterations). Writes a 32-byte header of salt + IV followed by ciphertext. Output is the source path with `.enc` appended. #### #### **Parameters** #### - `[string]`: __Path__ #### - *Path to the file to encrypt. Accepts pipeline input.* #### - `[securestring]`: __SecureKey__ #### - *Encryption passphrase.* #### #### **Returns** #### - `[ImmutableDictionary[string, object]]` #### - *`Path` (encrypted file path) and `SizeBytes`.* [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [Alias('FilePath')] [string]$Path, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [securestring]$SecureKey ) process { $FilePath = Resolve-Path -Path $Path -ErrorAction Stop $salt = [byte[]]::new(16) [RandomNumberGenerator]::Fill($salt) $aes = [Aes]::Create() $aes.Key = ([Rfc2898DeriveBytes]::new($SecureKey, $salt, 100000)).GetBytes(32) $aes.GenerateIV() $encryptedFilePath = [string]$FilePath + '.enc' $fileStream = [File]::Open($FilePath, 'Open', 'Read') $encryptedStream = [File]::Open($encryptedFilePath, 'Create', 'Write') $cryptoStream = $null try { $encryptedStream.Write($salt, 0, $salt.Length) $encryptedStream.Write($aes.IV, 0, $aes.IV.Length) $cryptoStream = [CryptoStream]::new($encryptedStream, $aes.CreateEncryptor(), [CryptoStreamMode]::Write) $buffer = [byte[]]::new(4096) while (($bytesRead = $fileStream.Read($buffer, 0, $buffer.Length)) -gt 0) { $cryptoStream.Write($buffer, 0, $bytesRead) } } finally { if ($cryptoStream) { $cryptoStream.Close() } $fileStream.Close() $encryptedStream.Close() } [ImmutableDictionary[string, object]]::Empty.Add('Path', $encryptedFilePath).Add('SizeBytes', [FileInfo]::new($encryptedFilePath).Length) } } |