File Decrypting Script
The following PowerShell script can be used to decrypt files encrypted by WinSCP, when you do not have WinSCP available.
For C# version of the AesCtrTransform
function, see Can I use AES in CTR mode in .NET?
Advertisement
param ( [Parameter(Mandatory = $True)] $path, [Parameter(Mandatory = $True)] $key ) function AesCtrTransform ($key, $salt, $inputStream, $outputStream) { $aes = New-Object System.Security.Cryptography.AesManaged -Property @{ Mode = [System.Security.Cryptography.CipherMode]::ECB Padding = [System.Security.Cryptography.PaddingMode]::None } $blockSize = $aes.BlockSize / 8 $counter = $salt.Clone() $xorMask = New-Object System.Collections.Queue<byte> $zeroIv = New-Object byte[] $blockSize $counterEncryptor = $aes.CreateEncryptor($key, $zeroIv) while (($b = $inputStream.ReadByte()) -ne -1) { if ($xorMask.Count -eq 0) { $counterModeBlock = New-Object byte[] $blockSize $counterEncryptor.TransformBlock( $counter, 0, $counter.Length, $counterModeBlock, 0) | Out-Null for ($i2 = $counter.Length - 1; $i2 -ge 0; $i2--) { if ($counter[$i2] -eq 0xFF) { $counter[$i2] = 0 } else { ++$counter[$i2] break } } foreach ($b2 in $counterModeBlock) { $xorMask.Enqueue($b2) } } $mask = $xorMask.Dequeue() $outputStream.WriteByte([byte](([byte]$b) -bxor $mask)) } } try { $aes = New-Object System.Security.Cryptography.AesManaged $key = [System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary]::Parse($key).Value $keySize = $aes.KeySize / 8 if ($key.Length -ne $keySize) { throw "Invalid key size (need $keySize bytes)" } $name = Split-Path -Leaf $path $aesCtrExt = ".aesctr.enc" if (-not ($name -like "*$aesCtrExt")) { throw "$name has not a known encrypted file extension [$aesCtrExt]" } $base64 = $name.SubString(0, $name.Length - $aesCtrExt.Length).Replace("_", "/") $padding = 4 - ($base64.Length % 4) if (($padding -gt 0) -and ($padding -lt 4)) { $base64 += "=" * $padding } $buffer = [System.Convert]::FromBase64String($base64) $blockSize = $aes.BlockSize / 8 $nameSalt = $buffer[0..($blockSize - 1)] $nameInputStream = New-Object System.IO.MemoryStream( $buffer, $nameSalt.Count, ($buffer.Length - $nameSalt.Length)) $nameOutputStream = New-Object System.IO.MemoryStream AesCtrTransform $key $nameSalt $nameInputStream $nameOutputStream $name = [System.Text.Encoding]::UTF8.GetString($nameOutputStream.ToArray()) Write-Host "Decrypted filename: $name" $parentPath = (Split-Path -Parent $path) if ($parentPath) { $outPath = Join-Path $parentPath $name } else { $outPath = $name } if (Test-Path $outPath) { throw "$outPath already exists" } if ((Get-Item $path).Length -eq 0) { Write-Host "Creating empty file..." New-Item $outPath -Type file | Out-Null } else { $fileInputStream = [System.IO.File]::OpenRead($path) $aesMagic = [System.Text.Encoding]::UTF8.GetBytes("aesctr..........") $magic = New-Object byte[] $aesMagic.Length $fileSalt = New-Object byte[] $blockSize if (($fileInputStream.Read($magic, 0, $magic.Length) -lt $magic.Length) -or (-not [System.Linq.Enumerable]::SequenceEqual($magic, $aesMagic)) -or ($fileInputStream.Read($fileSalt, 0, $fileSalt.Length) -lt $fileSalt.Length)) { throw "$path does not have valid header" } $fileOutputStream = [System.IO.File]::Create($outPath) Write-Host "Decrypting contents..." AesCtrTransform $key $fileSalt $fileInputStream $fileOutputStream $fileOutputStream.Dispose() } [System.IO.File]::SetLastWriteTime($outPath, [System.IO.File]::GetLastWriteTime($path)) Write-Host "Done." exit 0 } catch { Write-Host "Error: $($_.Exception.Message)" exit 1 }
Advertisement