How to securely send a file or message using RSA or ECDSA for encryption, message authentication and data integrity

This is a tutorial on how to use Elliptic Curve Digital Signature Algorithm (ECDSA) to encrypt a message.

Generation of Key Pairs

In order to get public key encryption to work, first think is to generate  a private/public key pair. You may use either RSA or ECDSA key pairs. RSA encryption does not support forward secrecy and should be avoided. Forward secrecy means that all old messages will be de decrypted if your private key is found in the future or if RSA algorithm is compromised.

RSA key pair generation

Generate private key:

$ openssl genrsa -aes256 -out kyriakos_private.pem 4096

Generate public key:

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

ECDSA key pair generation

Generate private key:

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

Protect your private key with a passwd and overwrite private key file:

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

Generate public key:

$ openssl ec -in kyriakos_private.ec.pem -pubout -out kyriakos_public.ec.pem

Each partner that is involved in the message exchange hold its other the public key. So let’s say you want to send me an ecrypted file and you possess my public key that is: "kyriakos_public.ec.pem"

Example Message for encyption

First let's create an example file for encryption:

$ echo 'hello world, this is an encrypted text message ' > message.txt

Random Key Generation

Then let's create random key for symmetric encryption, that will be used to encrypt the above message. Symmetric encryption is benefecial over asymmetric for file encryption.

$ openssl rand 2048 > passkey.bin

We'll use the key to encrypt the. You can have a look of the key with

$ hexdump passkey.bin 
Sample output:
0000000 d529 d753 f2ae 508d 1873 9148 3a39 9086
0000010 f74d fb00 3122 978f 8df2 3889 d1d7 cbdb
0000020 e06a 86a0 c0ee b16b 0a21 f7d9 e8ae adb5
0000030 c9c5 6b2a 5331 fbb9 342f 5801 91f6 27c6
0000040

Encrypt Message

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

$ openssl enc -aes-256-cbc -salt -in message.txt -out message.bin -pass file:passkey.bin

A warning will appear that
*** WARNING : deprecated key derivation used. Using -iter or -pbkdf2 would be better. Since the method seems deprecated you may use pbkdf2 option that is “Use password-based key derivation function 2”. 
$ openssl enc -aes-256-cbc -salt -in message.txt -out message.bin -pbkdf2

$ openssl enc -aes-256-cbc -salt -in message.txt -out message.bin -iter 1024

and enter a passwd in both cases!!
you may also use your passkey with -pbkdf2 option.
The end result will be the same, this was replace the password you entered.

$ openssl enc -aes-256-cbc -salt -in message.txt -out message.bin -pass file:passkey.bin -pbkdf2

Check also other encoding options with -k, -kfile options. Sometimes reading passwd from terminal may result to bad decryption. You can use an inline passwd:

$ openssl enc -aes-256-cbc -salt -in message.txt -out message.bin -k mypass -pbkdf2

Last step is to encode your message with base64 encoding:

$ openssl base64 -in message.bin -out message.b64

Key Encryption (if used)

$ openssl rsautl -encrypt -inkey <other_party_publickey.pem> -pubin -in passkey.bin -out passkey.enc.bin   $ openssl base64 -in passkey.enc.bin -out passkey.enc.b64

 

Hash Digest for data integrity.

Make a hash digest of msg.txt so the receiver can check that no errors or tampering happened when it was sent. A hash function is used to obtain and a value called “digest’. A hash function is any function that can be used to map data of arbitrary size to data of fixed size. Older versions of OpenSSL used MD5 key derivation, and MD5 is broken. SHA-2 function seems more proper as the the SHA-2 family consists of six hash functions with digests (hash values) that are 224, 256, 384 or 512 bits: SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256. SHA-256 and SHA-512 are novel hash functions computed with 32-bit and 64-bit words, respectively.

Create the digest:

$ cat message.txt | openssl dgst -sha256 -binary | xxd -p > message.digest.txt

Base64 encoding:

$ openssl base64 -in message.digest.txt -out message.digest.b64

Cryptographic Signature for authentication

Generate a cryptographically secure signature out of the hash digopenssl enc -d -aes-256-cbc -in message.bin -out message.txt --pass file:passkey.bin -pbkdf2est using your private key, so the other party can solely identfy you as the sender.

Create signature:

$ openssl dgst -sha256 -sign kyriakos_private.ec.pem -out message.signature.bin message.digest.txt 

Base64 encoding:

$ openssl base64 -in message.signature.bin -out message.signature.b64

This is the end of encryption. Your local folder should have now the following files: :

message.b64 message.bin 
message.digest.b64
message.digest.txt
message.signature.b64
message.signature.bin
message.txt

Now you can send/transmit/email ONLY the following files to the recipient:

message.digest.b64 
message.b64
message.signature.b64

As well owner public key and random key created (if used):

kyriakos_public.ec.pem

passkey.enc.b64

 

Decryption of files at the recipient side

Copy the above file to another folder to simulate decryption process. Simply do

mkdir decrypt 
cp *.b64 decrypt/ cd decrypt

Decode files from Base64

openssl base64 -d -in message.b64 -out message.bin   
openssl base64 -d -in message.digest.b64 -out message.digest.bin
openssl base64 -d -in message.signature.b64 -out message.signature.bin
openssl base64 -d -in passkey.enc.b64 -out passkey.enc.bin
(if used)

All .b64 files are not necessary any more and you can delete them

rm *.b64

 

Decrypt the Key (if used)

$ openssl rsautl -decrypt -inkey <recipient_privatekey>.pem -in passkey.enc.bin -out passkey.bin  

Decrypt the message file

openssl enc -d -aes-256-cbc -in message.bin -out message.txt -pbkdf2 (or) openssl enc -d -aes-256-cbc -in message.bin -out message.txt --pass file:passkey.bin -pbkdf2 (if a key was used)

You check that you message is now identical to the original one!!

You need now to check authentication and data integrity to verify that the message was not altered during transmission by a man in the middle and that is the from the verified sender

 

Verification

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

$ cat message.txt | openssl dgst -sha256 -binary | xxd -p > message.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 message.digest.bin message.digest2.bin  

If the two digest files are completely equal, the above command will return nothing. If that happens, you're good. If not, then message has been modified during transmission!!

 

Verify the Signature

The signature is a sure-fire way to verify that the files you just got did indeed come from the owner and have been encrypted with owners private key, which only the owner holds!! So signature must be verified!!

 

$ openssl dgst -sha256 -verify kyriakos_public.ec.pem -signature message.signature.bin message.digest.bin 
if the above command returns a “Verified OK” message them you are done!!
Message was received and decrypted correctly, no bit manipulation and the owner was the one supposed to be!!

If you get “Error Verifying Data. 139939101205824:error”, then signature could not be verified.

More to read about:
https://wiki.openssl.org/index.php/Enc (full set of options for openssl)
https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
https://en.wikipedia.org/wiki/Cryptographic_hash_function
https://www.freecodecamp.org/news/openssl-command-cheatsheet-b441be1e8c4a/

see all curves supporting ECDSA: openssl ecparam -list_curves

see all ciphers for TLSv1.3 encryption: openssl ciphers -tls1_3

see all cipher algorithms: openssl list -cipher-algorithms