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 # adding trailing equal signs if the value of $padding  is greater 0 but lower 4
    }
 
    $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