├── secret.txt ├── secure.py └── README.md /secret.txt: -------------------------------------------------------------------------------- 1 | user: this-could-be-a-username 2 | pass: this_could_be_a_password 3 | -------------------------------------------------------------------------------- /secure.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple encryption/decryption of files using one-time-passwords and public key cryptography, using command-line tools. 3 | 4 | Reference: https://gist.github.com/colinstein/de1755d2d7fbe27a0f1e 5 | """ 6 | 7 | import argparse 8 | from subprocess import call 9 | import tarfile 10 | 11 | 12 | def encrypt(file, key): 13 | encrypted_file = file + '.enc' 14 | call(['openssl', 'rand', '192', '-out', 'key']) 15 | call(['openssl', 'aes-256-cbc', '-in', file, '-out', encrypted_file, '-pass', 'file:key']) 16 | call(['openssl', 'rsautl', '-encrypt', '-pubin', '-inkey', key, '-in', 'key', '-out', 'key.enc']) 17 | call(['tar', '-zcvf', 'secret.tgz', encrypted_file, 'key.enc']) 18 | call(['rm', 'key']) 19 | call(['rm', 'key.enc']) 20 | call(['rm', encrypted_file]) 21 | 22 | 23 | def decrypt(file, key): 24 | tar = tarfile.open(file) 25 | encrypted_file = [f.name for f in tar if f.name != 'key.enc'][0] 26 | call(['tar', '-xzvf', file]) 27 | call(['openssl', 'rsautl', '-decrypt', '-ssl', '-inkey', key, '-in', 'key.enc', '-out', 'key']) 28 | call(['openssl', 'aes-256-cbc', '-d', '-in', encrypted_file, '-out', encrypted_file[:-4], '-pass', 'file:key']) 29 | call(['rm', 'key']) 30 | call(['rm', 'key.enc']) 31 | call(['rm', encrypted_file]) 32 | 33 | 34 | if __name__ == '__main__': 35 | # Parse arguments 36 | parser = argparse.ArgumentParser( 37 | description='Simple encryption/decryption of files using one-time-passwords and public key cryptography.' 38 | ) 39 | group = parser.add_mutually_exclusive_group() 40 | group.add_argument('-e', '--encrypt', help='Use to encrypt a file', action='store_true') 41 | group.add_argument('-d', '--decrypt', help='Use to decrypt a file', action='store_true') 42 | parser.add_argument('-f', '--file', help='Path to file to be encrypted/decrypted', required=True) 43 | parser.add_argument('-k', '--key', help='Path to key (public for encrypt, private for decrypt)', required=True) 44 | args = vars(parser.parse_args()) 45 | 46 | # Check at least one of encrypt or decrypt is specified 47 | if not args['encrypt'] and not args['decrypt']: 48 | raise parser.error('One of -d/--decrypt or -e/--encrypt must be set.') 49 | 50 | if args['encrypt']: 51 | encrypt(file=args['file'], key=args['key']) 52 | elif args['decrypt']: 53 | decrypt(file=args['file'], key=args['key']) 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # One-Time Padlock 2 | 3 | Simple encryption/decryption of files using one-time-passwords and public key cryptography, using command-line tools. 4 | 5 | Useful for encrypting passwords & files for transfer across insecure networks. This is a simple Python wrapper around several command-line tools that: 6 | 1. Generates a secure one-time key. 7 | 2. Encrypts the file to be secured using the one-time key. 8 | 3. Encrypts the one-time key using the recipient's public key. 9 | 4. Packages both the encrypted file, and the encrypted one-time key, into a single .tgz archive. 10 | 5. Cleans up by removing key, encrypted key and encrypted file. 11 | 12 | For decryption the reverse process is followed: 13 | 1. Extract the compressed archive. 14 | 2. Decrypts the encrypted one-time key using your private key. 15 | 3. Decrypts the data file using the decrypted one-time key. 16 | 4. Cleans up by removing key, encrypted key and encrypted file. 17 | 18 | Full credit to [colinstein](https://gist.github.com/colinstein) as this is nothing more than a Python convenience wrapper around his instructions here: https://gist.github.com/colinstein/de1755d2d7fbe27a0f1e 19 | 20 | 21 | ## Install 22 | 23 | ```sh 24 | wget https://raw.githubusercontent.com/john-sandall/onetimepadlock/master/secure.py 25 | ``` 26 | 27 | ## Usage 28 | 29 | From the commandline: 30 | 31 | ```sh 32 | # Encrypt 33 | python secure.py --encrypt -f secret.txt -k /path/to/public/key.pub.pkcs8 34 | 35 | # Encrypt using the public key of a GitHub user 36 | curl -sf "https://github.com/GITHUB_USERNAME.keys" | tail -n1 > recipient_key.pub 37 | ssh-keygen -e -f recipient_key.pub -m PKCS8 > recipient_key.pub.pkcs8 38 | python secure.py --encrypt -f secret.txt -k recipient_key.pub.pkcs8 39 | rm recipient_key.pub recipient_key.pub.pkcs8 40 | 41 | # Decrypt 42 | python secure.py --decrypt -f secret.tgz -k /path/to/private/key 43 | ``` 44 | 45 | ## Manual steps (including generation of .pkcs8 key) 46 | 47 | ```sh 48 | # Generate a PKCS8 version of your public key (e.g. id_rsa.pub -> id_rsa.pub.pkcs8) 49 | ssh-keygen -e -f ~/.ssh/id_rsa.pub -m PKCS8 > ~/.ssh/id_rsa.pub.pkcs8 50 | 51 | # Encrypt (generate one-time-use password in file `key`, use key to encrypt secret.txt, then use public key to encrypt key file) 52 | openssl rand 192 -out key 53 | openssl aes-256-cbc -in secret.txt -out secret.txt.enc -pass file:key 54 | openssl rsautl -encrypt -pubin -inkey ~/.ssh/id_rsa.pub.pkcs8 -in key -out key.enc 55 | tar -zcvf secret.tgz *.enc 56 | # Can now send secret.tgz ONLY to recipient via insecure network. 57 | 58 | # Decrypt 59 | tar -xzvf secret.tgz 60 | openssl rsautl -decrypt -ssl -inkey ~/.ssh/id_rsa -in key.enc -out key 61 | openssl aes-256-cbc -d -in secret.txt.enc -out secret.txt -pass file:key 62 | 63 | # Tip: to encrypt an entire directory 64 | tar -zcvf secret_dir.tgz /path/to/directory/ 65 | # Then continue as above, replacing secret.txt with secret_dir.tgz 66 | # When decrypting, unpack the archive with following command 67 | tar -xzvf secret_dir.tgz 68 | ``` 69 | --------------------------------------------------------------------------------