When backing up databases/table to files I needed to have them encrypted for security and compliance. But there were a few concerns:
- I didn't want the file to be in plaintext, ever.
- I didn't want that same user to be able to decrypt the file later in case the account was compromised.
So, Pub/Priv is great for #2. Unfortunately, Pub/Priv is not made for large files, therefor I decided follow this process:
- Generate symetric key
- Encrypt files in-line w/ symetric key using AES-256-CBC
- Encrypt symetric key w/ public-key encryption (from a 4096-bit private key)
Info on types of encryption used:
Here are some setup things:
# Generate private key (I would place this in your /root/.ssh/ folder or some place safer)
openssl genrsa -out private_key.pem 4096
# Generate public key (this can go anywhere, just make sure the user that runs the encryption can read it. I use ~/.ssh/ of the user)
openssl rsa -in private_key.pem -out public_key.pem -outform PEM -pubout
# Public key used to encrypt symmetric key (make sure this points to where yours is)
CERTFILE=~/.ssh/public_key.pem
# Randomly generate the parts of the symmetric key and initialization vector
ENC_IV=$(openssl rand 32 -hex) # 32 bytes = (32*8) = 256-bit
ENC_KEY=$(openssl rand 32 -hex) # 32 bytes = (32*8) = 256-bit
## Command used to encrypt in-line
# "openssl" is self-explanatory
# "enc" is the sub-app inside openssl that does encryption
# "aes-256-cbc" is the type of cypher used
# "-e" means encrypt, use "-d" when decrypting
# "-iv ${ENC_IV}" is where I'm passing in the initialization vector we generated
# "-K ${ENC_KEY}" is where I'm passing in the symmetric key we generated
# more info: https://www.openssl.org/docs/manmaster/apps/enc.html
STREAM_ENCRYPT_COMMAND="openssl enc -aes-256-cbc -e -iv ${ENC_IV} -K ${ENC_KEY}"
# File name/location stuff
DEST_FOLDER=/data/backups/
OUTPUT_FULL_PATH=${DEST_FOLDER}/test_file
# Save the symmetric key and IV to a pub key encrypted file separated with a ":"
echo "${ENC_IV}:${ENC_KEY}" | openssl rsautl -encrypt -inkey ${CERTFILE} -pubin > ${OUTPUT_FULL_PATH}.key
Do your thing and encrypt it in-stream
<mysqldump, tar, curl, or whatever you do> | ${STREAM_ENCRYPT_COMMAND} > ${OUTPUT_FULL_PATH}.bin
You can also do more things in-stream like gzip it before encrypting it:
<mysqldump, tar, curl, or whatever you do> | gzip | ${STREAM_ENCRYPT_COMMAND} > ${OUTPUT_FULL_PATH}.gz.bin
This creates two files The key file: /data/backups/test_file.key The encrypted file: /data/backups/test_file.bin -or- /data/backups/test_file.gz.bin
To decrypt, you reverse the process:
- Decrypt the key file using the private key
- split the contents into symmetric key and IV
- Use the symmetric key and IV to decrypt the bin file
- reverse the pipe order so if it was zipped then encrypted, it's decrypted and then unzipped
- swap the "-e" for "-d" to decrypt
CERTFILE=/root/.ssh/public_key.pem
SECRETS=$(cat /data/backups/test_file.key | openssl rsautl -decrypt -inkey ${CERTFILE})
ENC_IV=$(echo ${SECRETS} | cut -f1 -d':')
ENC_KEY=$(echo ${SECRETS} | cut -f2 -d':')
STREAM_DECRYPT_COMMAND="openssl enc -aes-256-cbc -d -iv ${ENC_IV} -K ${ENC_KEY}"
# Gzipped file:
cat /data/backups/test_file.gz.bin | ${STREAM_DECRYPT_COMMAND} | gunzip > /data/backups/test_file
# Just encrypted:
cat /data/backups/test_file.gz.bin | ${STREAM_DECRYPT_COMMAND} > /data/backups/test_file
I would suggest having your script that does the encryption to only be writeable by root or something like that so no one can just add logging statements to get the symmetric key.
In both mysql commands in the scripts I have it prompt for the password to the database for security reasons, but you could put it in the ~/.my.conf file or something.