This is an old revision of the document!
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