How to encrypt a file with an ec public key

encryption

I have generated an EC public/private key pair using openssl with the following commands:

openssl ecparam -name brainpoolP512t1 -param_enc explicit -genkey -out mykey.pem
openssl ec -in mykey.pem -pubout -out mykey.pub

How can I encrypt / decrypt a file using the new keys that I've just generated using the (linux) terminal?

Best Answer

The high level strategy for this is as follows:

  1. Generate a temporary EC private key using openssl ec
  2. Use the recipient's public key to derive a shared secret using openssl pkeyutl
  3. Encrypt the plaintext using openssl enc using the derived secret key
  4. Generate the EC public key from the private key using openssl ecparam
  5. Generate the HMAC of the cipher text into a third file using openssl dgst
  6. Delete the EC private key and the shared secret

The manual flow for this should roughly look at follows:

openssl ec -genkey -param_enc explicit -out temppriv.pem -name brainpool512r1
openssl pkeyutl -derive -inkey temppriv.pem -peerkey RecipientsPublicKey.pem -out SharedSecret.bin
openssl dgst -sha256 -out HashedSharedSecret SharedSecret.bin
openssl enc -aes-256-ofb -iv "00000000000000000000000000000000" -K "<Enter Hex From HashedSharedSecret here>" -in YourPlaintextFile -out ciphertext.enc
openssl ecparam -in tempprivkey.pem -pubout -out temppubkey.pem
openssl dgst -sha256 -hmac "<Enter Hex From HashedSharedSecret here>" -out MAC.bin ciphertext.enc
#strip the everything non-hex using your editor from MAC.bin
rm SharedSecret.bin
rm tempprivkey.pem

The script doing the encryption should roughly look like the following:

    #!/bin/sh
    EphemeralPrivateKey=$(openssl ecparam -genkey -param_enc explicit -name brainpool512r1) #generate the ephmeral private key
PrivateKeyBuffer=$(mktemp) #allocate a file to bufer the private key for the derive operation
    PeerPublicKey="$1"
    PlainTextFile="$2"
    EphemeralPublicKeyFile="$3"
    CipherTextFile="$4"
    MACFile="$5"
echo -n "$EphemeralPrivateKey" > $PrivateKeyBuffer  #buffer the private key
    ProcessedDerivedSharedSecret=$(openssl pkeyutl -derive -inkey $PrivateKeyBuffer -peerkey $PeerPublicKey|openssl dgst -sha256) #derive the symmetric key using SHA-256 from the established secret
        rm $PrivateKeyBuffer #remove the temporary file
        ProcessedDerivedSharedSecret=${ProcessedDerivedSharedSecret#*= } #strip the (stdin)=
        openssl enc -aes-256-ofb -iv "0000000000000000000000000000000" -K "$ProcessedDerivedSharedSecret" -in "$PlainTextFile" -out "$CipherTextFile" #encrypt using 0 IV and SHA-256 as key
        MACValue=$(openssl dgst -sha256 -hmac "$ProcessedDerivedSharedSecret" "$CipherTextFile") #MAC it
        MACValue=${MACValue#*= } #strip the (stdin)=
        echo -n "$MACValue" > $MACFile #write the MAC
        echo -n "$EphemeralPrivateKey" | openssl ec -param_enc explicit -pubout -out "$EphemeralPublicKeyFile" #write the ephemeral public key

The above code should work, but may not be optimal. The final message is composed from temppubkey.pem, ciphertext.enc and MAC.bin, you may combine this in whatever way you prefer. Note that my choice for AES-256-OFB is no accident but intentional as CTR, CCM and GCM mode aren't available via the command line. Note further that I preferred AES-256 over the standard choice of AES-128 here because we can simply plug the output of SHA-256 in there. Note even further that using an all-zero IV is secure here as OFB "only" requires unique IVs per key and each key is fully random.


As for the security considerations: This method will generate a temporary private key for each file, ensuring all encryptions are unique and leakage of one shared secret won't leak all shared secrets for the same pair of communication partners. You could use digital signatures to ensure the message actually came from the same source.