If you have someone’s public SSH key, you can use OpenSSL to safely encrypt a file and send it to them over an insecure connection (i.e. the internet). They can then use their private key to decrypt the file you sent.
If you encrypt/decrypt files or messages on more than a one-off occasion, you should really use GnuPGP as that is a much better suited tool for this kind of operations. But if you already have someone’s public SSH key, it can be convenient to use it, and it is safe.
There is a limit to the maximum length of a message – i.e. size of a file – that can be encrypted using asymmetric RSA public key encryption keys (which is what SSH keys are). For this reason, we’ll actually generate a 256 bit key to use for symmetric AES encryption and then encrypt/decrypt that symmetric AES key with the asymmetric RSA keys. This is how encrypted connections usually work, by the way.
Encrypt a file using a public SSH key
Generate the symmetric key (32 bytes gives us the 256 bit key):
$ openssl rand -out secret.key 32
You should only use this key this one time, by the way. If you send something to the recipient at another time, don’t reuse it.
Encrypt the file you’re sending, using the generated symmetric key:
$ openssl aes-256-cbc -in secretfile.txt -out secretfile.txt.enc -pass file:secret.key
In this example
secretfile.txt is the unencrypted secret file, and
secretfile.txt.enc is the encrypted file. The encrypted file can be named whatever you like.
Encrypt the symmetric key, using the recipient’s public SSH key:
$ openssl rsautl -encrypt -oaep -pubin -inkey <(ssh-keygen -e -f recipients-key.pub -m PKCS8) -in secret.key -out secret.key.enc
recipients-key.pub with the recipient’s public SSH key.
Delete the unencrypted symmetric key, so you don’t leave it around:
$ rm secret.key
Now you can send the encrypted secret file (secretfile.txt.enc) and the encrypted symmetric key (secret.key.enc) to the recipient. It is even safe to upload the files to a public file sharing service and tell the recipient to download them from there.
Decrypt a file encrypted with a public SSH key
First decrypt the symmetric.key:
$ openssl rsautl -decrypt -oaep -inkey ~/.ssh/id_rsa -in secret.key.enc -out secret.key
The recipient should replace ~/.ssh/id_rsa with the path to their secret key if needed. But this is the path to where it usually is located.
Now the secret file can be decrypted, using the symmetric key:
$ openssl aes-256-cbc -d -in secretfile.txt.enc -out secretfile.txt -pass file:secret.key
Again, here the encrypted file is
secretfile.txt.enc and the unencrypted file will be named
* Why are you generating 192 bytes when only 32 are needed for the AES-256 symmetric key?
* Use OAEP (as PKCS#1 v1.5 is deterministic) when encrypting your symmetric key, otherwise two identical keys will have the same ciphertext. (chosen plaintext attack)
* I … I … have no other explanation that I must have had temporary brain damage. I mixed up bits and bytes! :-o Well, at least generating 1536 bits for the “password” didn’t do any harm :-)
* You’re absolutely right. PKCS#1 v1.5 should only be used for signing, not for encryption. I’ve updated the commands now.
Thank you so much for your comment, I really appreciate it!
I tried the suggested encryption command (openssl aes-256-cbc) but got the warning result:
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
$ openssl rand 32 -out secret.key
rand: Use -help for summary.
command not working.
Reading around the web, plus looking at the docs, it seems to me that -pass is not for inputting the key, but rather inputting a password, from which both the key and the IV for CBC are derived. This isn’t good, insofar there seems to be a consensus that OpenSSL’s key derivation isn’t all that good.
We are using the 256 bit symmetric “key” as the password. The key to the file containing the password is the asymmetric SSH key.
Right. I’m merely noting that the password is not the symmetric key. Rather, OpenSSL uses the password to generate both the actual symmetric key and the IV. (In that sense, the password does not have to be 256 bits, except insofar as it’s probably a good idea for it to have as much entropy as the actual key that will be derived from it.)
This distinction isn’t entirely unimportant from a practical standpoint, as apparently many people in the security community don’t like OpenSSL’s method for deriving the key from the password.
Exactly! That was my first thought when I saw it mentioned as the key used for symmetric encryption. You are absolutely right Stephen. The pass argument is not the symmetric encryption key. It is a password from which key and IV are derived.
I do want to add—don’t take my comment the wrong way. This page was extremely useful to me. There was stuff on StackOverflow, but much of it wasn’t quite as concrete as the solution you posted here.
Here we are encrypting and decrypting a file. What if we need to encrypt and decrypt a password saved in that file instead. Can we do it using the same commands?
openssl rand 32 -out secret.key
I sometimes got these errors:
140625532782232:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:531:
I did not get those errors if i base64 encode the random string using:
openssl rand 32 | base64 -w 0 > secret.key
(replace -w with -b on BSD/OSX)
Thank you for this post!
I made a bash script to put this all together and easily encrypt/decrypt files with ssh key: https://github.com/S2-/sshencdec
This is likely a terribly naive question.
What is the benefit to generating a one-off symmetric password and encrypting that with the target’s public key, vs encrypting the desired payload directly with the target’s public key?
I tried to explain that in the beginning:
The problem is that anything we want to encrypt probably is too large to encrypt using asymmetric RSA public key encryption keys.
Thank you for the reply. That makes sense!
Hi, thanks for the tip!
I got the following error message with 1.1.0h:
openssl rand 32 -out secret.key
Extra arguments given.
rand: Use -help for summary.
The command works when options are before the size:
openssl rand -out secret.key 32
Yeah, I’ve noticed that OpenSSL started being picky about that lately. Updated the text now.
Thank you for leaving the comment, Olivier.
thank’s for your post !
Realy simple and easy.
It can be used to start discover other features in openssl.
Comments are closed.