Skip to content

Instantly share code, notes, and snippets.

@heaths
Created October 9, 2023 07:42
Show Gist options
  • Save heaths/9700da7afe75c8b2c68f5755c6bcff3a to your computer and use it in GitHub Desktop.
Save heaths/9700da7afe75c8b2c68f5755c6bcff3a to your computer and use it in GitHub Desktop.
DPAPI PowerShell Module
#Requires -Version 6.0
using namespace System.Security.Cryptography
using namespace System.Text
function Protect-Data {
[CmdletBinding(DefaultParameterSetName = 'Path')]
[OutputType([byte[]])]
param (
[Parameter(ParameterSetName = 'Value', Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[byte[]] $Value,
[Parameter(ParameterSetName = 'Path', Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
[string] $Path,
[Parameter(ParameterSetName = 'LiteralPath', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('PSPath')]
[string] $LiteralPath,
[Parameter()]
[switch] $Force,
[Parameter()]
[DataProtectionScope] $Scope = [DataProtectionScope]::CurrentUser,
[Parameter()]
[ValidateNotNullOrEmpty()]
[byte[]] $AdditionalEntropy
)
begin {
[byte[]] $buffer = @()
}
process {
if ($PSCmdlet.ParameterSetName -eq 'Path') {
$LiteralPath = Resolve-Path -Path $Path
}
if ($PSCmdlet.ParameterSetName -eq 'Value') {
$buffer += $Value
} else {
$buffer = Get-Content -LiteralPath $LiteralPath -Force:$Force -AsByteStream
[ProtectedData]::Protect($buffer, $AdditionalEntropy, $Scope)
}
}
end {
if ($PSCmdlet.ParameterSetName -eq 'Value') {
[ProtectedData]::Protect($buffer, $AdditionalEntropy, $Scope)
}
}
}
function Protect-Path {
[CmdletBinding(DefaultParameterSetName = 'Path', ConfirmImpact = 'Medium', SupportsShouldProcess = $true)]
param (
[Parameter(ParameterSetName = 'Path', Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
[string] $Path,
[Parameter(ParameterSetName = 'LiteralPath', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('PSPath')]
[string] $LiteralPath,
[Parameter(Mandatory = $true, Position = 1)]
[string] $Destination,
[Parameter()]
[switch] $Force,
[Parameter()]
[DataProtectionScope] $Scope = [DataProtectionScope]::CurrentUser,
[Parameter()]
[ValidateNotNullOrEmpty()]
[byte[]] $AdditionalEntropy
)
end {
[void] $PSBoundParameters.Remove('Confirm')
[void] $PSBoundParameters.Remove('Destination')
[void] $PSBoundParameters.Remove('WhatIf')
Protect-Data @PSBoundParameters | Set-Content -Path $Destination -AsByteStream -NoNewLine -Force:$Force
}
}
function Unprotect-Data {
[CmdletBinding(DefaultParameterSetName = 'Path')]
[OutputType([byte[]])]
param (
[Parameter(ParameterSetName = 'Value', Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[byte[]] $Value,
[Parameter(ParameterSetName = 'Path', Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
[string] $Path,
[Parameter(ParameterSetName = 'LiteralPath', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('PSPath')]
[string] $LiteralPath,
[Parameter()]
[switch] $Force,
[Parameter()]
[DataProtectionScope] $Scope = [DataProtectionScope]::CurrentUser,
[Parameter()]
[ValidateNotNullOrEmpty()]
[byte[]] $AdditionalEntropy,
[Parameter()]
[switch] $AsString
)
begin {
[byte[]] $buffer = @()
$unprotect = {
$plaintext = [ProtectedData]::Unprotect($buffer, $AdditionalEntropy, $Scope)
if ($AsString) {
return [Encoding]::UTF8.GetString($plaintext)
}
$plaintext
}
}
process {
if ($PSCmdlet.ParameterSetName -eq 'Path') {
$LiteralPath = Resolve-Path -Path $Path
}
if ($PSCmdlet.ParameterSetName -eq 'Value') {
$buffer += $Value
} else {
$buffer = Get-Content -LiteralPath $LiteralPath -Force:$Force -AsByteStream
&$unprotect
}
}
end {
if ($PSCmdlet.ParameterSetName -eq 'Value') {
&$unprotect
}
}
}
function Unprotect-Path {
[CmdletBinding(DefaultParameterSetName = 'Path', ConfirmImpact = 'Medium', SupportsShouldProcess = $true)]
param (
[Parameter(ParameterSetName = 'Path', Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
[string] $Path,
[Parameter(ParameterSetName = 'LiteralPath', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('PSPath')]
[string] $LiteralPath,
[Parameter(Mandatory = $true, Position = 1)]
[string] $Destination,
[Parameter()]
[switch] $Force,
[Parameter()]
[DataProtectionScope] $Scope = [DataProtectionScope]::CurrentUser,
[Parameter()]
[ValidateNotNullOrEmpty()]
[byte[]] $AdditionalEntropy
)
end {
[void] $PSBoundParameters.Remove('Confirm')
[void] $PSBoundParameters.Remove('Destination')
[void] $PSBoundParameters.Remove('WhatIf')
Unprotect-Data @PSBoundParameters | Set-Content -Path $Destination -AsByteStream -NoNewLine -Force:$Force
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment