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 decryptcp *.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