Skip to content

Instantly share code, notes, and snippets.

@kebman
Last active November 25, 2022 12:35
Show Gist options
  • Save kebman/f02fe0b1dbebc9ee1a56a7885c30f014 to your computer and use it in GitHub Desktop.
Save kebman/f02fe0b1dbebc9ee1a56a7885c30f014 to your computer and use it in GitHub Desktop.
How To Encrypt a File – And Securely Send It – using OpenSSL

Here is a tutorial – or simulation, rather – on symmetric file encryption, and public key encryption to securely send your keys and files to a friend using OpenSSL with the Terminal. The tutorial is made for UX systems (e.g. OS X and Linux), and you may have to install OpenSSL if you don't have it (OS X should have it pre-installed).

Note: The use of the dollar sign ($) in front of the terminal lines is just a convention. Ignore it, and focus on what comes after it.

Simulation

Navigate to an empty folder and make two folders, alice and bob:

$ mkdir alice
$ mkdir bob  

Generate Key Pairs

In order to get public key encryption to work, the first think you should do is to make sure you have generated a private/public key pair. Here's an example of how ´you do that:

RSA key pair

Note: This is for testing purposes only. You shouldn't use RSA in a real application. Ref.: https://blog.trailofbits.com/2019/07/08/fuck-rsa/

Generate private key:

$ openssl genrsa -aes256 -out private.pem 4096

Pass phrase tips: Use a sentence with several random words, but also use numbers and weird characters

Generate public key:

$ openssl rsa -in private.pem -outform PEM -pubout -out public.pem

Key Generation Tutorial

Generate a key pair in each folder. Give Alice alice as password, and give Bob bobby as password. In a production environment you should use much stronger passwords, but we're just testing for now.

Here's how to generate each key pair.

First we'll do Alice:

$ openssl genrsa -aes256 -out alice/private.pem 4096
$ openssl rsa -in alice/private.pem -outform PEM -pubout -out alice/public.pem  

Then we'll do Bob:

$ openssl genrsa -aes256 -out bob/private.pem 4096
$ openssl rsa -in bob/private.pem -outform PEM -pubout -out bob/public.pem  

Under is an example of ECDSA key pairs. It's just there for reference, so just skip ahead to the Message section if you're not curious.

ECDSA key pair, only for reference

I just provided this for reference, since it's useful to know. But you can skip it for this tutorial.

ECDSA is a technology that uses mathematical curves for their cryptography, just like in Bitcoin! The benefit of an ECDSA key pair is that they deliver the same security as RSA, only with a much shorter key length. They can also be used for shared key derivation, meaning you don't really need to send a random key with the file. Instead you can just encrypt the file with the shared key.

Generate private key:

$ openssl ecparam -name prime256v1 -genkey -noout -out private.ec.pem

Protect your private key:

$ openssl pkcs8 -topk8 -in private.ec.pem -out private.enc.pem
$ mv private.enc.pem private.ec.pem

Note: the mv overwrites private.ec.pem with the encrypted and passworded private key.

Generate public key:

$ openssl ec -in bob/private.pem -pubout -out public.ec.pem

As advised, you can skip the ECDSA section for this tutorial. It's just there for reference.

Message

First let's create an example file:

$ echo 'hello world' > bob/msg.txt

Random Key

Then let's create random key for symmetric encryption (we'll get to the asymmetric part soon):

$ openssl rand 64 > bob/randomkey.bin

We'll use the key to encrypt the

Encrypt Message

Use the randomkey.bin file we generated to encrypt our message file, msg.txt.

$ openssl enc -aes-256-cbc -salt -in bob/msg.txt -out bob/msg.bin -pass file:./bob/randomkey.bin

Note: Older versions of OpenSSL used MD5 key derivation, and MD5 is broken. The problem should be fixed by now, but it's prudent to be aware of it. For more info, see: https://cryptosense.com/blog/weak-key-derivation-in-openssl/. However it should be changed to SHA256 by now, per: https://github.com/openssl/openssl/blob/master/doc/man1/enc.pod

Prepare for Sending

Prepare the encrypted msg.bin for sending over the Internet:

$ openssl base64 -in bob/msg.bin -out bob/msg.b64

This simply encodes the file into base64. Sometimes when you send a file over the internet, the data is handled in different ways that can lead to loss of information when it's tossed between different systems. Base64 encode ensures that there will be no such data loss since it uses characters that are known on all systems. There are flags to auto-code into base64, like -a, but I like being explicit about what happens.

Encrypt the Key

Lastly we need send the key we used to encrypt the file to our friend, somehow. This is where public key encryption comes in. To make sure nobody else can see the key, we'll use our Alice's public key to encrypt the file key, like so:

$ openssl rsautl -encrypt -inkey alice/public.pem -pubin -in bob/randomkey.bin -out bob/randomkey.enc.bin  
$ openssl base64 -in bob/randomkey.enc.bin -out bob/randomkey.enc.b64

As you can see, we used Alice's public key to encrypt our file-key. Now that our file is securely encrypted, and we have asymmetrically encrypted the file-key, we prepare to send the files – but hold on to your horses! You may want to look at a few additional steps, to ensure the integrity of your package.

Hash Digest

Make a hash digest of msg.txt so the receiver can check that no errors or tampering happened when it was sent.

$ cat bob/msg.txt | openssl dgst -sha256 -binary | xxd -p > bob/msg.digest.txt  
$ openssl base64 -in bob/msg.digest.txt -out bob/msg.digest.b64

Make a Cryptographic Signature

Use your private key to generate a cryptographically secure signature out of the hash digest, so the receiver can be absolutely sure that it came from you (given that you didn't lose your keys...).

$ openssl dgst -sha256 -sign bob/private.pem -out bob/msg.signature.bin bob/msg.digest.txt  
$ openssl base64 -in bob/msg.signature.bin -out bob/msg.signature.b64

Note: In order for this to work, you need to have generated a key pair of your own. More on that further down.

Send Away!

Now either pack all .b64 files using bz2 or zip, or send them separately. :)

Depending a little on what you want to do next, you can either delete all the temporary files (at least after you got confirmation that your friend received them), or you can store the encrypted file and the random key locally.

In order to do the latter, you should first securely delete all enc.bin and enc.b64 files. Also delete the temporary msg files, including the original. Leave only msg.bin. Then encrypt randomkey.bin, like so:

$ openssl aes-256-cbc -a -salt -in bob/randomkey.bin -out bob/randomkey.enc.bin  
$ mv bob/randomkey.enc.bin bob/randomkey.bin

Note: The last step overwrites randomkey.bin with the password protected password key. Remember to also delete randomkey.enc.b64. ;)

Simulated sending the files:

$ cp bob/msg.b64 alice/  
$ cp bob/msg.digest.b64 alice/  
$ cp bob/msg.signature.b64 alice/  
$ cp bob/randomkey.enc.b64 alice/

Yes, I know it's just copying it to Alice, but in real life you'd send them by mail, or give her a Dropbox link (or other cloud service).

How to Receive and Decrypt Files

Unpack and Unbase

Unpack the files if they're packed :p

If they are base64 encoded, you want to decode that, like so:

$ openssl base64 -d -in <infile> -out <outfile>

Simulate un-base64-ing:

$ openssl base64 -d -in alice/msg.b64 -out alice/msg.bin  
$ openssl base64 -d -in alice/msg.digest.b64 -out alice/msg.digest.bin  
$ openssl base64 -d -in alice/msg.signature.b64 -out alice/msg.signature.bin  
$ openssl base64 -d -in alice/randomkey.enc.b64 -out alice/randomkey.enc.bin  
$ rm alice/*.b64

Note: The last command deletes all *.b64 files, as you don't need them anymore after un-base64-ing.

Decrypt the Key

Syntax: openssl rsautl -decrypt -inkey id_rsa.pem -in key.bin.enc -out key.bin

$ openssl rsautl -decrypt -inkey alice/private.pem -in alice/randomkey.enc.bin -out alice/randomkey.bin  
$ rm alice/randomkey.enc.bin

Decrypt the File

Finally decrypt the file <3

Syntax: openssl enc -d -aes-256-cbc -in SECRET_FILE.enc -out SECRET_FILE -pass file:./key.bin

$ openssl enc -d -aes-256-cbc -in alice/msg.bin -out alice/msg.txt -pass file:./alice/randomkey.bin  

Great! Nearly done! You've unpacked and decrypted all the files, but... You need to check a couple of more things to be really sure about the quality of the files you've just received.

Verification

First let's check the hash digest. Make your own hash digest of the received file, like so:

$ cat alice/msg.txt | openssl dgst -sha256 -binary | xxd -p > alice/msg.digest2.bin

Then compare it to the hash digest you got from Bob. If they are equal, then you can be sure there were no errors while sending the files, or tampering for that matter.

$ diff alice/msg.digest.bin alice/msg.digest2.bin  

If the two digest files are completely equal, the above command will return nothing. If that happens, you're good. You only need to worry if the command returns something. But to be really sure, you need to also check the signature.

Verify the Signature

The signature is a sure-fire way to verify that the files you just got did indeed come from the owner of the Bob's public key. :p Yes, I know that sounds a little strange, but how do you know it was actually Bob who packaged the files? To know that, you pretty much have to call him and ask, given that you're not talking to an impostor... Paranoia intensifies!

Anyway, here's how you check the signature:

Syntax: openssl dgst -sha256 -verify -signature /tmp/sign.sha256

$ openssl dgst -sha256 -verify bob/public.pem  -signature alice/msg.signature.bin alice/msg.digest.bin

If it returns Verified OK, then you're golden. If not, then there may be something wicked afoot...

That's All Folks!

And that's it! Now you know how to safely send files using OpenSSL. To be truly safe, however, you should probably consider using GnuPG instead.

@b-a-b-i-s
Copy link

In line 154 it is file:./bob/randomkey.bin
You have forgotten the '/'

@kebman
Copy link
Author

kebman commented Dec 15, 2021

Thank you, Sir! Not sure how it snuck in there, but I think it worked last time I tested this, which is years ago xD

@arafat-shopify
Copy link

Thanks a lot

@kebman
Copy link
Author

kebman commented Nov 25, 2022

Today I'd probably opt for the X25519 protocol for the key exchange, and then use some strong key derivation tool to generate sub-keys from the shared secret. Then I'd use one of the sub-keys to encrypt the files with some stream cipher. If you want to send many files, or the data is big, then you will perhaps also need some archiving tool together with a compression tool as well, and compress the data before you encrypt it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment