Monday, June 13, 2011

Encrypting a file in Windows

Recently I’ve been thinking about backing up some of my files into the cloud. I would want to encrypt my files before sending them into the cloud just in case my account gets compromised. I started looking for a free/open source command line solution for file encryption. Some of the best open source solution I had found only offered source code download and I didn’t feel like installing a compiler on my windows machine just to try them. This gave me the motivation to find way to use something already installed into windows to do the encryption. I found that powershell was already installed on that machine and that it had access to .Net. That’s great.

On windows, too often, the quickest and easiest way to do something is to download a third .party software to do it, even if you already have the code somewhere in the operating system. Windows does not come with a compiler, it does not come with compiled applications to use all its features, it is not really empowering its users by it self. At least now it has powershell.

So, I made a small powershell script that creates an AES encrypted copy of a file. In the end I will probably not use it because I remembered that 7zip which is already installed on my machine supports encryption, is able to sync a directory in compressed archives and has a command line interface.

Anyways here is the script:
param(
    [string]$keyfile=$(throw "Parameter keyfile required."),
    [string]$infile=$(throw "Parameter infile required."),
    [string]$outfile=$(throw "Parameter outfile required."),
    [switch]$decrypt
    )
# Encrypt Usage:
# powershell -ExecutionPolicy Unrestricted ./aesfile.ps1 -keyfile key.txt -infile data.txt -outfile data.txt.aes
# Decrypt Usage:
# powershell -ExecutionPolicy Unrestricted ./aesfile.ps1 -keyfile key.txt -infile data.txt.aes -outfile data.dec.txt -decrypt

# Read the key and the iv
# each is on one line and has its byte written in decimal ascii separated by ":"
[byte[]]$key = (Get-Content $keyfile)[0]| foreach {($_ -split ":")}
[byte[]]$iv = (Get-Content $keyfile)[1]| foreach {($_ -split ":")}
[Console]::Error.WriteLine("key=$key")
[Console]::Error.WriteLine("IV=$iv")
[Console]::Error.WriteLine("infile=$infile")
[Console]::Error.WriteLine("outdile=$outfile")
[Console]::Error.WriteLine("decrypt=$decrypt")

# Setup FileStreams
[System.IO.Stream]$instream = new-object System.IO.FileStream $infile, ([System.IO.FileMode]::Open)
[System.IO.Stream]$outstream = new-object System.IO.FileStream $outfile, ([System.IO.FileMode]::Create)

# Setup Encryption
$AesCSP = New-Object System.Security.Cryptography.AesCryptoServiceProvider
$AesCSP.Key = $key
$AesCSP.IV = $IV
if(!$decrypt) {$cryptor=$AesCSP.CreateEncryptor()}
else{$cryptor=$AesCSP.CreateDecryptor()}
$cstream = new-Object Security.Cryptography.CryptoStream $instream,$cryptor,"Read"

# Number of bytes to read with each chunks from the stream.
$BLOCK_BYTE_SIZE = $AesCSP.BlockSize/8
$NB_BLOCKS_READ = 100
$CHUNK_BYTE_SIZE = $BLOCK_BYTE_SIZE * $NB_BLOCKS_READ

#$cstream.CopyTo($outstream, $CHUNK_BYTE_SIZE) Only in .Net 4.0 :(
[byte[]]$buffer = new-object byte[] $CHUNK_BYTE_SIZE

while($byte_read=$cstream.Read($buffer, 0, $buffer.Length))
{
    $outstream.Write($buffer, 0, $byte_read)
}

$cstream.Close()
$instream.Close()
$outstream.Close()


Here is a sample key file:
247:74:89:63:153:30:170:243:11:247:227:187:2:177:244:17:154:9:150:12:155:253:168:19:224:241:254:148:213:50:147:184
129:15:216:20:169:11:167:32:75:57:109:137:15:14:92:212


The first line is the key(32 bytes), the second line is an initialization vector(16 bytes).
Bytes are written in decimal and separated by ‘:’.

Notes:
    I lost a lot of time trying to support reading binary input on stdin and never found a solution. For something that's called “powershell” I expected better support for stdin. The existing commands/functions only support reading text from the keyboard and the hidden $input.'<>4__this' can only read lines of text. I wanted to use my script with | and > instead of having to open the files myself.