Skip to content
 

File encryption on the command line

This list is just a reference which hopefully saves some googling.

Let's make it clear that we're talking about symmetric encryption here, that is, a password (or better, a passphrase) is supplied when the file is encrypted, and the same password can be used to decrypt it. No public/private key stuff or other preparation should be necessary. We want a quick and simple way of encrypting stuff (for example, before moving them to the cloud or offsite backup not under our control). As said, file ecryption, not whole filesystems or devices.

Another important thing is that symmetric encryption is vulnerable to brute force attacks, so a strong password should always be used and the required level of security should always be evaulated. It may be that symmetric encryption is not the right choice for a specific situation.

It is worth noting that the password or passphrase that are supplied to the commands are not used directly for encryption/decription, but rather are used to derive the actual encryption/decryption keys. However this is done transparently by the tools (usually through some sort of hashing) and for all practical purposes, these passwords or passphrases are the keys, and should be treated as such.

In particular, one thing that should be avoided is putting them directly on the command line. Although some tools allow that, the same tools generally also offer options to avoid it, and they should definitely be used.

Openssl

Probably the simplest and most commonly installed tool is openssl.

# Encrypt
$ openssl enc -aes-192-cbc -in plain.txt -out encrypted.enc
# Decrypt
$ openssl enc -d -aes-192-cbc -in encrypted.enc -out plain.txt

The above is the basic syntax. The cipher name can of course be different; the man page for the enc openssl subcommand lists the supported algorithms (the official docs also say: "The output of the enc command run with unsupported options (for example openssl enc -help) includes a list of ciphers, supported by your version of OpenSSL, including ones provided by configured engines." Still, it seems that adding a regular -help or -h option wouldn't be too hard). Other useful options:

  • -d to decrypt
  • -pass to specify a password source. In turn, the argument can have various formats: pass:password to specify the password directly in the command, env:var to read it from the environment variable $var, file:pathname to read it from the file at pathname, fd:number to read it from a given file descriptor, and stdin to read it from standard input (equivalent to fd:0, but NOT equivalent to reading it from the user's terminal, which is the default behavior if -pass is not specified)
  • -a to base64-encode the encrypted file (or assume it's base64-encoded if decrypting)

Openssl can also read the data to encrypt from standard input (if no file is specified with -in) and/or write to standard output (if -out is not given). Example with password from file:

# Encrypt
$ tar -czvf - file1 file2 ... | openssl enc -aes-192-cbc -pass file:/path/to/keyfile -out archive.tar.gz.enc
# Decrypt
$ openssl enc -d -aes-192-cbc -pass file:/path/to/keyfile -in archive.tar.gz.enc | tar -xzvf -

GPG

There are two main versions of GPG, the 1.x series and the 2.x series (respectively 1.4.x and 2.0.x at the time of writing).

gpg comes with a companion program, gpg-agent, that can be used to store and retrieve passphrases use to unlock private keys, in much the same way that ssh-agent caches password-protected SSH private keys (actually, in addition to its own, gpg-agent can optionally do the job of ssh-agent and replace it). Using gpg-agent is optional with gpg 1.x, but mandatory with gpg 2.x. In practice, when doing symmetric encryption, the agent is not used, so we won't talk about it here (although we will briefly mention it later when talking about aespipe, since that tool can use it).

GPG 1.4.x
# Encrypt file
$ gpg --symmetric --cipher-algo AES192 --output encrypted.enc plain.txt
# Decrypt file
$ gpg --decrypt --output plain.txt encrypted.enc

# Encrypt stdin to file
$ tar -czvf - file1 file2 ... | gpg --symmetric --cipher-algo AES192 --output archive.tar.gz.enc
# Decrypt file to stdout
$ gpg --decrypt archive.tar.gz.enc | tar -xzvf -

Useful options:

  • -a (when encrypting) create ascii-armored file (ie, a special text file)
  • --cipher-algo ALG (when encrypting) use ALG as cipher algorithm (run gpg --version to get a list of supported ciphers)
  • --batch avoid asking questions to the user (eg whether to overwrite a file). If the output file exists, the operation fails unless --yes is also specified
  • --yes assume an answer of "yes" to most questions (eg when overwriting an output file, which would otherwise ask for confirmation)
  • --no-use-agent to avoid the "gpg: gpg-agent is not available in this session" message that, depending on configuration, might be printed if gpg-agent is not running (it's only to avoid the message; as said, the agent is not used anyway with symmetric encryption)
  • --passphrase string use string as the passphrase
  • --passphrase-file file read passphrase from file
  • --passphrase-fd n read passphrase from file descriptor n (use 0 for stdin)
  • --quiet suppress some output messages
  • --no-mdc-warning (when decrypting) suppress the "gpg: WARNING: message was not integrity protected" message. Probably, a better thing to do is use --force-mdc when encrypting, so GPG won't complain when decrypting.

In any case, GPG will create and populate a ~/.gnupg/ directory if it's not present (I haven't found a way to avoid it - corrections welcome).

Similar to openssl, GPG reads from standard input if no filename is specified at the end of the command line. However, writing to standard output isn't obvious.

When encrypting, if no --output option is given, GPG will create a file with the same name as the input file, with the added .gpg extension (eg file.txt becomes file.txt.gpg), unless input comes from stdin, in which case output goes to stdout. If the input comes from a regular file and writing to standard ouput is desired, --output - can be used. --output can of course also be used if we want an output file name other than the default with .gpg appended.
On the other hand, when decrypting using --decrypt output goes to stdout unless --output is used to override it. If --decrypt is not specified, GPG still decrypts, but the default operation is to decrypt to a file named like the one on the command line but with the .pgp suffix removed (eg file.txt.pgp becomes file.txt); if the file specified does not end in .pgp, then --output must be specified (--output - writes to stdout), otherwise PGP exits with a "unknown suffix" error.

GPG 2.0.x
# Encrypt file
$ gpg --symmetric --batch --yes --passphrase-file key.txt --cipher-algo AES256 --output encrypted.enc plain.txt
# Decrypt file
$ gpg --decrypt --batch --yes --passphrase-file key.txt --output plain.txt encrypted.enc

# Encrypt stdin to file
$ tar -czvf - file1 file2 ... | gpg --symmetric --batch --yes --passphrase-file key.txt --cipher-algo AES256 --output archive.tar.gz.enc
# Decrypt file to stdout
$ gpg --decrypt --batch --yes --passphrase-file key.txt archive.tar.gz.enc | tar -xzvf -

In this case, the --batch option is mandatory (and thus probably --yes too) if we don't want pgp to prompt for the passphrase and instead use the one supplied on the command line with one of the --passphrase* options. The --no-use-agent option is ignored in gpg 2.0.x, as using the agent is mandatory and thus it should always be running (even though it's not actually used when doing symmetric encryption).

aespipe

As the name suggests, aespipe only does AES in its three variants (128, 192, 256). Aespipe tries hard to prevent the user from specifying the passphrase on the command line (and rightly so), so the passphrase(s) must normally be in a file (plaintext or encrypted with GPG). It is of course possible to come up with kludges to work around these restrictions, but they are there for a reason.

Aespipe can operate in single-key mode, where only one key/passphrase is necessary, and in multi-key mode, for which at least 64 keys/passphrases are needed. With 64 keys it operates in multi-key-v2 mode, with 65 keys it switches to multi-key-v3 mode, which is the safest and recommended mode, and the one that will be used for the examples.

So we need a file with 65 lines of random grabage; one way to generate it is as follows:

$ tr -dc '[:print:]' < /dev/random | fold -b | head -n 65 > keys.txt

If the above command blocks, it means that the entropy pool of the system isn't providing enough data. Either generate some entropy by doing some work or using an entropy-gathering daemon, or use /dev/urandom instead (at the price of losing some randomness).

Aespipe can also use a pgp-encrypted key file; more on this later. For now let's use the cleartext one.

# Encrypt a file using aes256
$ aespipe -e AES256 -P keys.txt < plain.txt > encrypted.enc
# Decrypt
$ aespipe -d -P keys.txt < encrypted.enc > plain.txt

As can be seen from the examples, given the way aespipe works (that is, as a pipe), it is not necessary to show its usage to encrypt to/from stdin/stdout, since it's its default and only mode of operation.

Useful options:

  • -C count run count rounds of hashing when generating the encryption key from the passphrase. This stretching helps to slow down brute force attacks. Recommended if using single-key mode, not needed in multi-key mode(s)
  • -e ENCALG (when encrypting) use ENCALG as cipher algorithm (AES128, AES192, AES256)
  • -h HASHALG use HASHALG to generate the actual key from the passphrase (default depends on encryption algorithm, see the man page)

One very important thing to note is that aespipe has a minimum block granularity when encrypting and decrypting; in simple terms, this means that the result of the decryption must always be a multiple of this minumum (16 bytes in single-key mode, 512 bytes in multi-key modes). NULs are added to pad if needed. Here is a blatant demonstration of this fact:

$ echo hello > file.txt.orig
$ ls -l file.txt.orig
-rw-r--r-- 1 waldner users 6 Jul 11 16:52 file.txt.orig
$ aespipe -P keys.txt < file.txt.orig > file.txt.enc
$ aespipe -d -P keys.txt < file.txt.enc > file.txt.dec
$ ls -l file.txt.*
-rw-r--r-- 1 waldner users 512 Jul 11 16:58 file.txt.dec
-rw-r--r-- 1 waldner users 512 Jul 11 16:57 file.txt.enc
-rw-r--r-- 1 waldner users   6 Jul 11 16:52 file.txt.orig
$ od -c file.txt.dec 
0000000   h   e   l   l   o  \n  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0001000

Some file formats can tolerate garbage at the end (eg tar), other can't, so this is something to keep into account when using aespipe. In the cases where the original size is known, it may be possible to postprocess the decrypted file to remove the padding but this may not always be practical:

$ origsize=$(wc -c < file.txt.orig)
$ truncate -s "$origsize" file.txt.dec
# alternatively
$ dd if=file.txt.dec bs="$origsize" count=1 > file.txt.likeorig

In the cases where the exact byte size is needed and no postprocessing is possible or wanted, another tool should be used (eg gpg or openssl).

Ok, so let's now see how to use an encrypted keyfile with aespipe. The file should be encrypted with GPG, which in turn can do symmetric encryption (as previously seen in this same article) or public-key encryption (using a public/private key pair, which should be already generated and available - not covered here).
Let's encrypt our keys.txt file both with symmetric and public-key encryption (separately)

# using symmetric encryption
$ gpg --symmetric --output keys.enc.sym keys.txt
# enter passphrase, or use some --passphrase* option to specify one

# using public key encryption
$ gpg --encrypt --recipient 199705C4 --output keys.enc.pubk keys.txt
# no passphrase is required, as only the public key is used to encrypt
# here "199705C4" is the id of the (public) key

Now, we want to encrypt or decrypt some file using the keys contained in our password-protected keyfile(s). This is done using the -K option (instead of -P) to aespipe. Let's start with the symmetrically enctrypted keyfile (keys.enc.sym):

# encrypt
$ aespipe -e aes256 -K keys.enc.sym < plain.txt > encrypted.enc
# aespipe prompts for the gpg passphrase to decrypt the keyfile

# decrypt
$ aespipe -d -e aes256 -K keys.enc.sym < encrypted.enc > plain.txt
# same thing, passphrase for keyfile is prompted

Now with the public-key encrypted keyfile:

# encrypt
$ aespipe -e aes256 -K keys.enc.pubk < plain.txt > encrypted.enc
# to decrypt keys.enc.pubk, the private key is needed, 
# aespipe prompts for the passphrase to unlock the private key

# decrypt
$ aespipe -d -e aes256 -K keys.enc.pubk < encrypted.enc > plain.txt
# same thing, passphrase to unlock the private key is prompted

So far, nothing special. However, for this last case (keyfile encrypted with public key cryptography), aespipe can actually use gpg-agent (if it's running) to obtain the passphrase needed to unlock the private key. This is done with the -A option, which tells aespipe the path to the socket where gpg-agent is listening. Assuming gpg-agent has already seen the passphrase to unlock the private key, it can transmit it to aespipe.

# The gpg-agent socket information is in the GPG_AGENT_INFO environment variable
# in the session where the agent is running, or one to which the variable has been exported. For example:
$ echo "$GPG_AGENT_INFO"
/tmp/gpg-gXM3Pm/S.gpg-agent:4897:1
# encrypt using a public-key encrypted keyfile, but tell aespipe to ask gpg-agent for the passphrase
$ aespipe -e aes256 -A "$GPG_AGENT_INFO" -K keys.enc.pubk < plain.txt > encrypted.enc
# similar for decryption

Other utilities

Let's have a look at some other utilities that are simpler but lack the flexibility provided by the previous ones.

mcrypt

This seems to be almost unusable, as doing practically anything beyond simple, optionless encryption produces a message like

Signal 11 caught. Exiting.

so it doesn't seem to be a good candidate for serious use. Some research shows many user in the same situation. More information is welcome.

aescrypt

This is a little-known program, however aescrypt is open source and very simple to use. It is multiplatform and has even a GUI for graphical operation. Here, however, we'll use the command-line version.

# encrypt a file
$ aescrypt -e -p passphrase file.txt
# creates file.txt.aes

# decrypt a file
$ aescrypt -d -p passphrase file.txt.aes
# creates file.txt

# encrypt standard input
$ tar -czvf - file1 file2 ... | aescrypt -e -p passphrase - -o archive.tar.gz.aes

# decrypt to stdout
$ aescrypt -d -p passphrase -o - archive.tar.gz.aes | tar -xzvf -

If no -p option is specified, aescrypt interactively prompts for the passphrase.
If no -o option is specified, a file with the same name and the .aes suffix is created when encrypting, and one with the .aes suffix removed when decrypting.

Since putting passwords directly on the command line is bad, it is possible to put the passphrase in a file and tell aescrypt to read it from the file. However, the file is not a simple text file; it has to be in a format that aescrypt recognizes. To create it, the documentation suggests using the aescrypt_keygen utility as follows:

$ aescrypt_keygen -p somesupercomplexpassphrase keyfile.key

The aescrypt_keygen program is only available in the source code package and not in the binary one (at least in the Linux version). However, since this file, according to the documentation, is nothing more than the UTF-16 encoding of the passphrase string, it's easy to produce the same result without the dedicated utility:

# generate keyfile
$ echo somesupercomplexpassphrase | iconv -f ascii -t utf-16 > keyfile.key

Once we have a keyfile, we can encrypt/decrypt using it:

$ aescrypt -e -k keyfile.key file.txt
# etc.
ccrypt

The ccrypt utility is another easy-to-use encryption program that implements the AES(256) algorithm. Be sure to read the man page and the FAQ.

Warning: when not reading from standard input, ccrypt overwrites the source file with the result of the encryption or decryption. This means that, if the encryption process is interrupted, a file could be left in an only partially encrypted state. On the other hand, when encrypting standard input this (obviously) does not happen. Sample usage:

# encrypt a file; overwrites the unencrypted version, creates file.txt.cpt
$ ccrypt -e file.txt

# decrypt a file; overwrites the encrypted version, creates file.txt
$ ccrypt -d file.txt.cpt

In this mode, multiple file arguments can be specified, and they will all be encrypted/decrypted. It is possible to recursively encrypt files contained in subdirectories if the -r/--recursive option is specified.

If no files are specified, ccrypt operates like a pipe:

# Encrypt standard input (example)
$ tar -czvf - file1 file2 ... | ccrypt -e > archive.tar.gz.enc
# Decrypt to stdout (example)
$ ccrypt -d < archive.tar.gz.enc | tar -xzvf -

To use the command non-interactively, it is possible to specify the passphrase in different ways:

  • -K|--key passphrase: directly in the command (not recommended)
  • -E|--envvar var: the passphrase is the content of environment variable $var

A useful option might be the -x|--keychange, which allows changing the passphrase of an already encrypted file; the old and new passphrases are prompted - or specified on the command line with -K/-H (--key/--key2) or -E/-F (--envvar/--envvar2) respectively, the file is decrypted with the old passphrase and reencrypted with the new one.

7-zip

The compression/archiving utility 7-zip can apparently do AES256 encryption, deriving the encryption key from the passphrase specified by the user with the -p option:

# encrypt/archive, prompt for passphrase
$ 7z a -p archive.enc.7z file1 file2 ...

# encrypt/archive, passphrase on the command line
$ 7z a -ppassphrase archive.enc.7z file1 file2 ...

# encrypt/archive standard input (prompt for passphrase)
$ tar -cvf - file1 file2 ... | 7z a -si -p archive.enc.tar.7z

# decrypt/extract, prompt for passphrase
$ 7z x -p archive.enc.7z [ file1 file2 ... ]

# decrypt/extract, passphrase on the command line
$ 7z x -ppassphrase archive.enc.7z [ file1 file2 ... ]

# decrypt/extract to stdout (prompt for passphrase)
$ 7z x -so -p archive.enc.tar.7z | tar -xzvf -

It looks like there's no way to run in batch (ie, non-interactive) mode without explicitly specifying the passphrase on the command line.

5 Comments

  1. Dan says:

    The following line seems to be missing the -d flag (which tells aespipe to decrypt instead of encrypt)

    aespipe -e aes256 -K keys.enc.sym plain.txt

    should be

    aespipe -d -e aes256 -K keys.enc.sym plain.txt

  2. owais says:

    Awsome post it really helped me out for non interactive mode.
    gpg --batch --yes --no-use-agent --passphrase "passwd" --symmetric --cipher-algo AES192 --output encrypted.enc my-rawfile.txt

  3. 7z can also decrypt and encrypt with AES256 with "regular" zip archives. I just found this useful exchanging files with a Windows user who's corporate standard was AES256/zip. Decrypt was obvious, but encrypt wasn't quite so, when I came to give data back:-

    $ 7z a -mem=AES256 -p archive.zip foo bar

    Without the -mem flag it seemed to apply some simplistic method which meant regular 'unzip' could open (when given the right password). Whereas unzip whinged uniformly, just like when I received the original archive

    $ unzip archive.zip
    #Archive: archive.zip
    # skipping: foo need PK compat. v5.1 (can do v4.5)
    # skipping: bar need PK compat. v5.1 (can do v4.5)

    I think the GNOME File Roller utility might hide much of the details for GNOME users, but ark under KDE misses an AES256 for regular zip archives option.