Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Python command line tool plus library to use KMS for symmetric encryption/decryption of secrets file
"""
A utility to encrypt/decrypt a file using an AWS KMS key.
Useful for storing credentials or other sensitive data.
You will need a KMS key created in AWS, and have permission to use it. The script defaults to
a key with alias "alias/botoUsage".
This script intentionally doesn't support providing key/secret key credentials directly to boto3.
boto3 uses a cascading approach to searching for credentials for connecting to AWS, and this script
needs to run in one of those contexts i.e. running under an IAM role or with AWS Key / Secret key
stored as Environment Variables or credentials file.
On your development machine I recommend the credentials/config file approach because it lets you
store multiple accounts. See https://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html
In production on AWS, IAM roles & policies are the way to go.
-------
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 kms_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 kms_crypter import crypter
"""
import boto3
import base64
import pathlib2 as pathlib
import click
import six
#This is Sydney Australia, change as appropriate
REGION_NAME = 'ap-southeast-2'
def crypter(secretsfile=None,outfile=None,keyid=None,cryptermode=None,tofile=True):
'''
Encrypt or decrypt a file with an AWS KMS key and optionally output to string
or same file or other file.
'''
cryptermode = 'encrypt' if cryptermode is None else cryptermode
keyid='alias/botoUsage' if keyid is None else keyid
if secretsfile is None:
raise ValueError('\nError: -f or --secretsfile parameter is required.\n')
try:
with pathlib.Path(secretsfile).open('rb') as sourceF:
sourcebytes = sourceF.read()
except IOError:
raise IOError('\nExpecting a {0} file\n'.format(secretsfile))
# Overwrite secrets file if no outfile provided
outfile = secretsfile if outfile is None else outfile
session = boto3.session.Session(region_name=REGION_NAME)
kms = session.client('kms')
if cryptermode == 'encrypt':
encrypted_dict = kms.encrypt(KeyId=keyid, Plaintext=sourcebytes)
binary_encrypted = encrypted_dict[u'CiphertextBlob']
outputbytes = base64.b64encode(binary_encrypted)
else:
binary_data = base64.b64decode(sourcebytes)
decrypted_dict = kms.decrypt(CiphertextBlob=binary_data)
outputbytes = decrypted_dict[u'Plaintext']
if tofile:
with pathlib.Path(outfile).open('wb+') as target:
target.write(outputbytes)
else:
return(outputbytes)
@click.command()
@click.pass_context
@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','--keyid',help='KMS key, arn or alias')
@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,keyid,cryptermode):
'''
Takes a KMS key and secrets file and optionally encrypts (the default) or decrypts the
secrets file using the key, overwriting the same file unless outfile is specified.
\b
Examples:
to encrypt a file in place:
python crypter.py -f secrets.cfg
python crypter.py -f secrets.cfg -e
\b
to encrypt a file in place providing a key (arn, key id, key alias):
python crypter.py -f secrets.cfg
python crypter.py -f secrets.cfg -k alias/someKey -e
\b
to encrypt to a different file:
python crypter.py -f secrets.cfg -o encrypted.cfg -d
\b
to decrypt the same file:
python crypter.py -f secrets.cfg -d
'''
try:
crypter(secretsfile=secretsfile,outfile=outfile,keyid=keyid,cryptermode=cryptermode)
except IOError:
click.echo(ctx.get_help())
raise
except ValueError:
click.echo(ctx.get_help())
raise
if __name__ == '__main__':
main()
@davoscollective

This comment has been minimized.

Copy link
Owner Author

commented May 24, 2018

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.