Skip to content

Instantly share code, notes, and snippets.

@davoscollective davoscollective/crypter.py
Last active May 24, 2018

Embed
What would you like to do?
Python command line tool plus library to use cryptography.Fernet for symmetric encryption/decryption of secrets file
"""
A utility to encrypt/decrypt a file using a fernet key.
Useful for storing credentials or other sensitive data.
You will need a fernet.key file, which you can also create with this utility.
-------
By leveraging the magical click library, this can be used at the command line. Because the click interface
then calls another function internally, it can also be used by importing that crypter function into your python
code.
e.g.
At the command line:
>python crypter.py -f secrets.cfg -o outputfile [-e |-d]
OR
In your python script (assuming this file is in the same directory as your script)
from crypter import crypter
"""
from __future__ import print_function
import click
from pathlib2 import Path
from cryptography.fernet import Fernet, InvalidToken
def crypter(secretsfile=None,outfile=None,keyfile=None,cryptermode=None,keygen=False,tofile=True):
"""
Encrypt or decrypt a file with a Fernet key.
Optionally generate a new Fernet key.
Optionally output to string or same file or other file.
:param secretsfile: File with text to be encrypted or decrypted
:type secretsfile: string
:param outfile: Optional filename to save the encrypted or decrypted data as.
:type outfile: string or path
:param keyfile: file containing a single line with a valid fernet key
:type keyfile: string or path
:param cryptermode: valid values are 'encrypt' or 'decrypt'
:type cryptermode: string
:param keygen: Generate a new fernet.key file in same directory
:type keygen: boolean
:param tofile: output cryption to a new file, or to a string if False
:type tofile: boolean
"""
cryptermode = 'encrypt' if cryptermode is None else cryptermode
if secretsfile is None and not keygen:
raise ValueError('\nError: -f or --secretsfile parameter is required when --keygen is not specified.\n')
if keyfile is None:
keyfile = str(Path(Path(__file__).resolve().parent,'fernet.key'))
if keygen:
KEY = Fernet.generate_key()
with Path(keyfile).open('wb+') as keyF:
keyF.write(KEY)
if secretsfile is None:
print('{keyfile} generated'.format(keyfile=keyfile))
return
if secretsfile is None:
raise ValueError('\nError: -f or --secretsfile parameter is required.\n')
try:
with Path(secretsfile).open('rb') as sourceF:
sourcebytes = sourceF.read()
except IOError:
raise IOError('\nExpecting a {0} file\n'.format(secretsfile))
outfile = secretsfile if outfile is None else outfile
try:
with Path(keyfile).open('rb') as keyF:
keytext = keyF.read()
KEY = Fernet(keytext)
except IOError:
raise IOError ('\nExpecting a {keyfile} file\n'.format(keyfile=keyfile))
if cryptermode == 'encrypt':
outputbytes = KEY.encrypt(sourcebytes)
else:
try:
outputbytes = KEY.decrypt(sourcebytes)
except InvalidToken as e:
print('Trying to decrypt plain text?')
raise e
if tofile:
with Path(outfile).open('wb+') as target:
target.write(outputbytes)
else:
return(outputbytes)
@click.command()
@click.pass_context
@click.option('--keygen',is_flag=True,help="generate a fernet key file", is_eager=True)
@click.option('-f','--secretsfile', help="filename of input secrets file to be encrypted or decrypted")
@click.option('-o','-outfile',help='filename for output file, writes to input secretsfile if not specified')
@click.option('-k','--keyfile',help='fernet key file, looks for fernet.key if not specified')
@click.option('-e','--encrypt','cryptermode',is_flag=True,flag_value='encrypt',help='encrypts the filename, ',default=True)
@click.option('-d','--decrypt','cryptermode',is_flag=True,flag_value='decrypt',help='decrypts the filename')
def main(ctx,secretsfile,outfile,keyfile,cryptermode,keygen):
"""
Takes a fernet key file and secrets file and optionally encrypts (the default) or decrypts the
secrets file using the key, overwriting the same file unless outfile is specified.
Can also generate a key file only, or generate a keyfile and use it.
\b
Examples:
to encrypt a file in place using fernet.key file:
python crypter.py -f secrets.cfg
python crypter.py -f secrets.cfg -e
\b
to decrypt the same file:
python crypter.py -f secrets.cfg -d
\b
to specify a key and output file
python crypter.py -f secrets.cfg -e -k myferneykeyfile.key -o encryptedoutput.cfg
\b
to generate a fernet key and then use it to encrypt a file
python crypter.py --keygen -k myfernet.key -f secrets.cfg -e -o encrypted_with_new_key_output.cfg
\b
Can also be used to generate a fernet key only:
e.g.
python crypter.py --keygen -k myfernet.key
invalid:
using -d and --keygen together
One or both of --keygen and -f (--secretsfile) are required
"""
try:
crypter(secretsfile=secretsfile,outfile=outfile,keyfile=keyfile,cryptermode=cryptermode,keygen=keygen)
except IOError:
click.echo(ctx.get_help())
raise
except ValueError:
click.echo(ctx.get_help())
raise
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.