Private/DecryptFile.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
<#
    .SYNOPSIS
    Decrypts a file using AES.
 
    .DESCRIPTION
    Decrypts a file using an AES key.
 
    .PARAMETER FileToDecrypt
    File(s) to be decrypted
 
    .PARAMETER Key
    AES key to be used for decryption.
 
    .EXAMPLE
 
    DecryptFile 'C:\file.ext.encrypted' $key
 
    This example decrypts C:\file.ext.encrypted with the key stored in the variable $key.
 
    .NOTES
    Author: Tyler Siegrist
    Date: 8/23/2016
    https://gallery.technet.microsoft.com/scriptcenter/EncryptDecrypt-files-use-65e7ae5d
#>

Function DecryptFile {
    Param(
       [Parameter(Mandatory=$true, Position=1)]
       [System.IO.FileInfo[]]$FileToDecrypt,
       [Parameter(Mandatory=$true, Position=2)]
       [String]$Key,
       [Parameter(Mandatory=$false, Position=3)]
       [String]$Suffix
    )
 
    #Load dependencies
    try {
        if ($PSVersionTable.PSEdition -eq "Core") {
            $SystemCoreAssemblyPath = $($(Get-Command pwsh).Source | Split-Path -Parent) + '\System.Core.dll'
            if (!$(Test-Path $SystemCoreAssemblyPath)) {
                Write-Error "The path '$SystemCoreAssemblyPath' was not found! Halting!"
                $global:FunctionResult = "1"
                return
            }
            $null = Add-Type -Path $SystemCoreAssemblyPath
        }
        else {
            [System.Reflection.Assembly]::LoadWithPartialName('System.Security.Cryptography')
        }
    }
    catch {
        Write-Error 'Could not load required assembly.'
        Return
    }

    #Configure AES
    try {
        $EncryptionKey = [System.Convert]::FromBase64String($Key)
        $KeySize = $EncryptionKey.Length*8
        $AESProvider = New-Object 'System.Security.Cryptography.AesManaged'
        $AESProvider.Mode = [System.Security.Cryptography.CipherMode]::CBC
        $AESProvider.BlockSize = 128
        $AESProvider.KeySize = $KeySize
        $AESProvider.Key = $EncryptionKey
    }
    catch {
        Write-Error 'Unable to configure AES, verify you are using a valid key.'
        Return
    }

    #Used to store successfully decrypted file names.
    $DecryptedFiles = @()
    $FailedToDecryptFiles = @()

    foreach ($File in $FileToDecrypt) {
        # Try to decrypt as long as the .decrypted file doesn't already exist
        $DestinationFile = "$($File.FullName).decrypted"

        if (!$(Test-Path $DestinationFile)) {
            #Open file to decrypt
            try {
                $FileStreamReader = New-Object System.IO.FileStream($File.FullName, [System.IO.FileMode]::Open)
            }
            catch {
                Write-Error "Unable to open $($File.FullName) for reading."
                Continue
            }
        
            #Create destination file
            try {
                $FileStreamWriter = New-Object System.IO.FileStream($DestinationFile, [System.IO.FileMode]::Create)
            }
            catch {
                $FileStreamReader.Close()
                $FileStreamWriter.Close()
                Remove-Item $DestinationFile -Force
                Write-Error "Unable to open $DestinationFile for writing."
                Continue
            }

            #Get IV
            try {
                [Byte[]]$LenIV = New-Object Byte[] 4
                $FileStreamReader.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null
                $FileStreamReader.Read($LenIV,  0, 3) | Out-Null
                [Int]$LIV = [System.BitConverter]::ToInt32($LenIV,  0)
                [Byte[]]$IV = New-Object Byte[] $LIV
                $FileStreamReader.Seek(4, [System.IO.SeekOrigin]::Begin) | Out-Null
                $FileStreamReader.Read($IV, 0, $LIV) | Out-Null
                $AESProvider.IV = $IV
            }
            catch {
                $FileStreamReader.Close()
                $FileStreamWriter.Close()
                Remove-Item $DestinationFile -Force
                $FailedToDecryptFiles += $File
                Write-Error "Unable to read IV from $($File.FullName), verify this file was made using the included EncryptFile function."
                Continue
            }

            Write-Verbose "Decrypting $($File.FullName) with an IV of $([System.Convert]::ToBase64String($AESProvider.IV))"

            #Decrypt
            try {
                $Transform = $AESProvider.CreateDecryptor()
                [Int]$Count = 0
                [Int]$BlockSizeBytes = $AESProvider.BlockSize / 8
                [Byte[]]$Data = New-Object Byte[] $BlockSizeBytes
                $CryptoStream = New-Object System.Security.Cryptography.CryptoStream($FileStreamWriter, $Transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
                Do
                {
                    $Count = $FileStreamReader.Read($Data, 0, $BlockSizeBytes)
                    $CryptoStream.Write($Data, 0, $Count)
                }
                While ($Count -gt 0)

                $CryptoStream.FlushFinalBlock()
                $CryptoStream.Close()
                $FileStreamWriter.Close()
                $FileStreamReader.Close()

                #Delete encrypted file
                Remove-Item $File.FullName
                Write-Verbose "Successfully decrypted $($File.FullName)"
                $DecryptedFiles += $DestinationFile
            }
            catch {
                Write-Error "Failed to decrypt $($File.FullName)."
                $CryptoStream.Close()
                $FileStreamWriter.Close()
                $FileStreamReader.Close()
                Remove-Item $DestinationFile
                $FailedToDecryptFiles += $File
            }
        }        
    }

    $Result = New-Object -TypeName PSObject
    $Result | Add-Member -MemberType NoteProperty -Name Computer -Value $env:COMPUTERNAME
    $Result | Add-Member -MemberType NoteProperty -Name AESKey -Value $Key
    $Result | Add-Member -MemberType NoteProperty -Name FilesDecryptedwAESKey -Value $DecryptedFiles
    $Result | Add-Member -MemberType NoteProperty -Name FilesFailedToDecrypt -Value $FailedToDecryptFiles
    return $Result
}