How can I encrypt a file with an ec public key?

8

3

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?

starbeamrainbowlabs

Posted 2015-11-21T16:36:40.607

Reputation: 849

Do you only want to encrypt or do you also want to sign? – SEJPM – 2015-11-21T17:17:21.547

@SEJPM Just encrypt, though if I have to sign too that's not a problem. – starbeamrainbowlabs – 2015-11-21T17:21:14.743

2I'm voting to close this question as off-topic because it is about usage of cryptography-based software rather than about cryptography itself. – None – 2015-11-21T17:51:04.973

1I'm proposing to migrate this question, as it doesn't ask about cryptography per se but may still be useful to other people. I'm proposing SuperUser as migration target, as their scope is (among other things) to answer questions on how to use modern operating systems and their tools. – SEJPM – 2015-11-21T18:06:15.287

Answers

5

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.

SEJPM

Posted 2015-11-21T16:36:40.607

Reputation: 193

Thanks! How do I decrypt a message that I've encrypted with this script? – starbeamrainbowlabs – 2015-11-22T10:56:29.387

@starbeamrainbowlabs, same as encryption basically. You derive the shared secret from the supplied (ephemeral) public key and the recipient's private key. You hash it as above. Then you verify the HMAC using openssl dgst, finally you decrypt using openssl enc (there's a command line switch for decryption) – SEJPM – 2015-11-22T11:02:52.707

You need to quote $RawDerivedSharedSecret otherwise it gets split and globbed, which has a low but nonnegligible probability of changing the 'derived' secret so that decrypt using the same parties' keys won't work. Even quoted, depending on your echo (which in turn depends on your shell) some low-proability values may produce differing results on different platforms; printf '%s' "$var" is the safe way. Plus setting it from =$( -derive ) will trim trailing bytes of 00 or 0A, which is consistent but not as presumably desired. ... – dave_thompson_085 – 2015-11-22T21:20:52.050

... To avoid both simply, put the derive and the hash-to-hex in one pipeline processed=$(openssl pkeyutl -derive ... | openssl sha256). After deleting (stdin)= from processed it contains only 0-9a-f and can be safely echo'd without quoting. – dave_thompson_085 – 2015-11-22T21:32:52.943

@dave_thompson_085, I hope I've fixed the pipelining as you suggested. Furthermore I think openssl requires the HMAC key and the cipher key to be quoted. But anyways thanks for your advice. – SEJPM – 2015-11-22T21:48:32.583

On Unix quoting/escaping controls how the shell constructs the argument(s) it passes to the program; the program doesn't and can't control what the shell does. enc -K (and -iv) require hex digits (and convert them to bytes), and hex digits never need quoting (unless you've done something horrible to IFS). dgst -hmac (which I didn't pay attention to before) actually accepts characters, so when you give it $ProcessedDerivedEtc you aren't actually HMACing with SHA256(ECDH) say octets AB C0 12 ... but with hexof(SHA256(ECDH)) octets 61 62 63 30 31 32 .... – dave_thompson_085 – 2015-11-23T05:23:52.330

... With openssl at both ends this doesn't matter since they match, but if you needed SHA256(ECDH) e.g. for interop with something else it would be hard; for one thing, you can't have a 00 byte anywhere in an argument (not just trailing) and that happens about 1 in 8 times. One time I needed hmac with arbitrary bytes I had to do it 'by hand' using perl to construct secret XOR 36s concat input and pipe into openssl dgst -- plain dgst not -hmac -- then construct secret XOR 5Cs concat that and pipe to a second dgst. Not fun. – dave_thompson_085 – 2015-11-23T05:32:59.833

Thanks! I'll take a look as soon as I have some more time. – starbeamrainbowlabs – 2015-11-30T09:03:30.883

update: actually openssl commandline can do hmac with a hex key, just not with -hmac $chars instead use -mac hmac -macopt hexkey:$hex see https://superuser.com/questions/1311622/explanation-of-hmac-flag-in-open-ssl . Plus I apparently missed before that you have 1 & 4 swapped in the first two descriptions but correct in the third: it's ecparam -genkey (not ec) to generate a key and ec -pubout (not ecparam) to extract the pubkey. And finally -param_enc explicit is a waste of space for no benefit at all.

– dave_thompson_085 – 2018-10-03T07:38:45.677

And more substantively, aes-{128,192,256}-ctr IS available in commandline enc starting with OpenSSL 1.0.1 -- which was actually released 3 years before 2015. Though for some reason list-cipher-commands (in 1.1.0 list -cipher-commands) doesn't show it, only the -algorithms form does(!) – dave_thompson_085 – 2018-10-03T07:46:31.760

-2

Alexey Vesnin

Posted 2015-11-21T16:36:40.607

Reputation: 565

The code in that post appears to be in C / C++. I want to use a command line tool. – starbeamrainbowlabs – 2015-11-21T17:01:03.147

what about compiling the code into a command-line tool? Write all the exact ciphers in source and compile your solution =) – Alexey Vesnin – 2015-11-23T01:35:04.190