public/Unprotect-Blob.ps1
function Unprotect-Blob() { Param( [Parameter(Position = 0, ValueFromPipeline = $true)] [Byte[]] $EncryptedBlob, [Parameter(ParameterSetName = "PrivateKey")] [Byte[]] $PrivateKey, [Parameter(ParameterSetName = "Password")] [SecureString] $Password, [Parameter(ParameterSetName = "Keys")] [Byte[]] $Key, [Parameter(ParameterSetName = "Keys")] [Byte[]] $AuthenticationKey, [Byte[]] $InsecureInfo ) $Headers = $InsecureInfo if(!$Headers) { $Headers = New-Object 'Byte[]' -ArgumentList 0 } $options = Get-ProtectOptions $saltLength = 0 $AuthenticationSaltLength = 0 if($Password -or $PrivateKey) { if($Password) { $PrivateKey = ConvertTo-UnprotectedBytes $Password } $saltLength = $AuthenticationSaltLength = ($options.SaltSize / 8) $salt = New-Object 'Byte[]' -ArgumentList $saltLength $AuthenticationSalt = New-Object 'Byte[]' -ArgumentList $saltLength [Array]::Copy($EncryptedBlob, $Headers.Length, $salt, 0, $salt.Length) [Array]::Copy($EncryptedBlob, $Headers.Length + $salt.Length, $AuthenticationSalt, 0, $AuthenticationSalt.Length) $generator = New-Object System.Security.Cryptography.Rfc2898DeriveBytes ` -ArgumentList $PrivateKey, ($salt), ($options.Iterations) $Key = $generator.GetBytes($options.KeySize / 8) $generator.Dispose() $generator = New-Object System.Security.Cryptography.Rfc2898DeriveBytes ` -ArgumentList $PrivateKey, ($AuthenticationSalt), ($options.Iterations) $AuthenticationKey = $generator.GetBytes($options.KeySize / 8) $generator.Dispose() [Array]::Clear($pwd, 0, $pwd.Length) } if(!$Key -or $Key.Length -eq 0) { Throw [ArgumentException] "Key must not be null or empty" } if(!$AuthenticationKey -or $AuthenticationKey.Length -eq 0) { Throw [ArgumentException] "AuthenticationKey must not be null or empty" } $hmac = New-Object System.Security.Cryptography.HMACSHA256 $hmac.Key = $AuthenticationKey $hash = New-Object 'Byte[]' -ArgumentList ($hmac.HashSize / 8) $chunk = $EncryptedBlob.Length - $hash.Length $message = New-Object 'Byte[]' -ArgumentList ($chunk) [Array]::Copy($EncryptedBlob, 0, $message, 0, $chunk) $computedHash = $hmac.ComputeHash($message) $ivSize = $options.BlockSize / 8 if($EncryptedBlob.Length -lt ($hash.Length + $Headers.Length + $ivSize)) { return $null; } [Array]::Copy($EncryptedBlob, $EncryptedBlob.Length - $hash.Length, $hash, 0, $hash.Length) $compareResult = 0; for($i = 0; $i -lt $hash.Length; $i++) { $compareResult = $compareResult -bor ($hash[$i] -bxor $computedHash[$i]) } if($compareResult -ne 0) { return $null; } $aes = [System.Security.Cryptography.Aes]::Create() $aes.KeySize = $options.KeySize $aes.BlockSize = $options.BlockSize $aes.Mode = "CBC" $aes.Padding = "PKCS7" $iv = New-Object 'Byte[]' -ArgumentList $ivSize $headerLength = ($Headers.Length + $salt.Length + $AuthenticationSalt.Length) [Array]::Copy($EncryptedBlob, $headerLength, $iv, 0, $iv.Length) $headerLength += $iv.Length $decryptor = $aes.CreateDecryptor($key, $iv) $ms = New-Object System.IO.MemoryStream $cryptoStream = New-Object System.Security.Cryptography.CryptoStream($ms, $decryptor, "Write") $binaryWriter = New-Object System.IO.BinaryWriter($cryptoStream) try { $binaryWriter.Write($EncryptedBlob, $headerLength, $EncryptedBlob.Length - $headerLength - $hash.Length) $cryptoStream.FlushFinalBlock() $ms.Flush() return $ms.ToArray() } finally { $binaryWriter.Dispose() $cryptoStream.Dispose() $ms.Dispose() $decryptor.Dispose(); } } |