OpenSSL, a tool which accompanies us all the time but often being ignored by everyone. OpenSSL is currently the most powerful tool in the cryptographic industry.

With that said, OpenSSL itself is not easy to use and rather complicated and confusing.

This guide focus on signing & encrypting data in a simple way, utilizing a pair of Elliptic Curve keys.

My OpenSSL version:

OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

Getting Started

Generate keys

First we need to choose the algorithm. Unlike RSA, which only differs in key size, ECC has a lot of curves, like NIST curve, Brainpool curve.

Yeah I know, Curve 25519 folks. Unfortunately Curve 25519 is not a standard curve and therefore not supported by ecparam in OpenSSL. Alternatively you can use openssl genpkey -algorithm ed25519/x25519 to generate Curve 25519 keys, but due to the specialty of Curve 25519, it CANNOT be used for encryption, only signing. Also, you are ought to generate X.509 certificates in order to sign stuff, which is beyond what we are going to talk about.

Run openssl ecparam -list_curves to see all the curves available.

$ openssl ecparam -list_curves                                                                                      1  secp112r1 : SECG/WTLS curve over a 112 bit prime field
  secp112r2 : SECG curve over a 112 bit prime field
  secp128r1 : SECG curve over a 128 bit prime field
  secp128r2 : SECG curve over a 128 bit prime field
  secp160k1 : SECG curve over a 160 bit prime field
  secp160r1 : SECG curve over a 160 bit prime field
  secp160r2 : SECG/WTLS curve over a 160 bit prime field
  secp192k1 : SECG curve over a 192 bit prime field
  secp224k1 : SECG curve over a 224 bit prime field
  secp224r1 : NIST/SECG curve over a 224 bit prime field
  secp256k1 : SECG curve over a 256 bit prime field
  secp384r1 : NIST/SECG curve over a 384 bit prime field
  secp521r1 : NIST/SECG curve over a 521 bit prime field
  prime192v1: NIST/X9.62/SECG curve over a 192 bit prime field
  prime192v2: X9.62 curve over a 192 bit prime field
  prime192v3: X9.62 curve over a 192 bit prime field
  prime239v1: X9.62 curve over a 239 bit prime field
  prime239v2: X9.62 curve over a 239 bit prime field
  prime239v3: X9.62 curve over a 239 bit prime field
  prime256v1: X9.62/SECG curve over a 256 bit prime field
  sect113r1 : SECG curve over a 113 bit binary field
  sect113r2 : SECG curve over a 113 bit binary field
  sect131r1 : SECG/WTLS curve over a 131 bit binary field
  sect131r2 : SECG curve over a 131 bit binary field
  sect163k1 : NIST/SECG/WTLS curve over a 163 bit binary field
  sect163r1 : SECG curve over a 163 bit binary field
  sect163r2 : NIST/SECG curve over a 163 bit binary field
  sect193r1 : SECG curve over a 193 bit binary field
  sect193r2 : SECG curve over a 193 bit binary field
  sect233k1 : NIST/SECG/WTLS curve over a 233 bit binary field
  sect233r1 : NIST/SECG/WTLS curve over a 233 bit binary field
  sect239k1 : SECG curve over a 239 bit binary field
  sect283k1 : NIST/SECG curve over a 283 bit binary field
  sect283r1 : NIST/SECG curve over a 283 bit binary field
  sect409k1 : NIST/SECG curve over a 409 bit binary field
  sect409r1 : NIST/SECG curve over a 409 bit binary field
  sect571k1 : NIST/SECG curve over a 571 bit binary field
  sect571r1 : NIST/SECG curve over a 571 bit binary field
  c2pnb163v1: X9.62 curve over a 163 bit binary field
  c2pnb163v2: X9.62 curve over a 163 bit binary field
  c2pnb163v3: X9.62 curve over a 163 bit binary field
  c2pnb176v1: X9.62 curve over a 176 bit binary field
  c2tnb191v1: X9.62 curve over a 191 bit binary field
  c2tnb191v2: X9.62 curve over a 191 bit binary field
  c2tnb191v3: X9.62 curve over a 191 bit binary field
  c2pnb208w1: X9.62 curve over a 208 bit binary field
  c2tnb239v1: X9.62 curve over a 239 bit binary field
  c2tnb239v2: X9.62 curve over a 239 bit binary field
  c2tnb239v3: X9.62 curve over a 239 bit binary field
  c2pnb272w1: X9.62 curve over a 272 bit binary field
  c2pnb304w1: X9.62 curve over a 304 bit binary field
  c2tnb359v1: X9.62 curve over a 359 bit binary field
  c2pnb368w1: X9.62 curve over a 368 bit binary field
  c2tnb431r1: X9.62 curve over a 431 bit binary field
  wap-wsg-idm-ecid-wtls1: WTLS curve over a 113 bit binary field
  wap-wsg-idm-ecid-wtls3: NIST/SECG/WTLS curve over a 163 bit binary field
  wap-wsg-idm-ecid-wtls4: SECG curve over a 113 bit binary field
  wap-wsg-idm-ecid-wtls5: X9.62 curve over a 163 bit binary field
  wap-wsg-idm-ecid-wtls6: SECG/WTLS curve over a 112 bit prime field
  wap-wsg-idm-ecid-wtls7: SECG/WTLS curve over a 160 bit prime field
  wap-wsg-idm-ecid-wtls8: WTLS curve over a 112 bit prime field
  wap-wsg-idm-ecid-wtls9: WTLS curve over a 160 bit prime field
  wap-wsg-idm-ecid-wtls10: NIST/SECG/WTLS curve over a 233 bit binary field
  wap-wsg-idm-ecid-wtls11: NIST/SECG/WTLS curve over a 233 bit binary field
  wap-wsg-idm-ecid-wtls12: WTLS curve over a 224 bit prime field
  Oakley-EC2N-3:
        IPSec/IKE/Oakley curve #3 over a 155 bit binary field.
        Not suitable for ECDSA.
        Questionable extension field!
  Oakley-EC2N-4:
        IPSec/IKE/Oakley curve #4 over a 185 bit binary field.
        Not suitable for ECDSA.
        Questionable extension field!
  brainpoolP160r1: RFC 5639 curve over a 160 bit prime field
  brainpoolP160t1: RFC 5639 curve over a 160 bit prime field
  brainpoolP192r1: RFC 5639 curve over a 192 bit prime field
  brainpoolP192t1: RFC 5639 curve over a 192 bit prime field
  brainpoolP224r1: RFC 5639 curve over a 224 bit prime field
  brainpoolP224t1: RFC 5639 curve over a 224 bit prime field
  brainpoolP256r1: RFC 5639 curve over a 256 bit prime field
  brainpoolP256t1: RFC 5639 curve over a 256 bit prime field
  brainpoolP320r1: RFC 5639 curve over a 320 bit prime field
  brainpoolP320t1: RFC 5639 curve over a 320 bit prime field
  brainpoolP384r1: RFC 5639 curve over a 384 bit prime field
  brainpoolP384t1: RFC 5639 curve over a 384 bit prime field
  brainpoolP512r1: RFC 5639 curve over a 512 bit prime field
  brainpoolP512t1: RFC 5639 curve over a 512 bit prime field
  SM2       : SM2 curve over a 256 bit prime field

Basically all the curves above are available to use, specifically begun with prime, secp, brainpool

SM2 is the algorithm developed by the Chinese government and, currently, little information about it is available online. Unless you work for the CHinese government or you are dealing with entites that related to banking or incorporating in China, you are generally not suggested to use SM2 curves.

We will be using SECP curves today.

To generate your keys,

openssl ecparam -name secp384r1 -genkey -noout -out private.pem

So here’s the thing, ecparam by default only generates the parameters that are needed to generate the respective curve, which is useful if you need to generate a lot of curves. Obviously this is not the case here, so we give it -genkey to ask it to generate the key, and we specify -noout to ask OpenSSL to not output the parameters. The -name is not the name of your private key, but the name of the curve that you are using, here we are going to use 384-bit SECP curve.

Encrypting your key

The key we have just generated is UNENCRYPTED, which is not safe. I suggest everyone to encrypt their key(s).

openssl ec -in private.pem -aes-256-cbc -out private_enc.pem

Put in your passphrase and you are good to go.

To obtain your public key,

openssl ec -in private.pem -pubout -out public.pem

Signing

Now we can sign some data.

To generate some random number,

dd if=/dev/random of=data bs=1k count=1

--- OR ---

echo $RANDOM | tee data

And to sign it,

openssl dgst -sha384 -sign private.pem data > data.sig

To verify it,

openssl dgst -sha384 -verify public.pem -signature data.sig data

If everything is OK, you will see

$ openssl dgst -sha384 -verify public.pem -signature data.sig data
Verified OK

Alternatively, you can use -sha256 or -sha512.

Encrypting

Due to the specialty of ECC, you CANNOT encrypt big data blocks directly. Instead, you should generate a long and complex symmetric key and encrypt your big data blocks using that key. After that, you encrypt the key.

We first need to generate the symmetric key, but we don’t need it to be ASCII-armored. A binary should be ok.

openssl rand -out symmetric_key.bin 256

The 256 stands for the bit length. Longer is usually better but your encryption speed will be significantly slower.

After that, we can symmetrically encrypt the data.

openssl enc -aes-256-cbc -pbkdf2 -in data -out data_enc.bin -pass file:./symmetric_key.bin

Okay, let’s deal with the key.

Still, due to the specialty of ECC, we cannot encrypt the data directly. Instead, we need to utilize something called ECDH to derive a shared key from our primary key, which is symmetric. Then we encrypt data using that key.

# Derive a shared key from our primary key.
openssl pkeyutl -derive -inkey private.pem -peerkey public.pem -out shared_key.bin

# Convert the shared key into an AES key.
openssl dgst -sha256 -binary shared_key.bin > derived_key.bin

# Encrypt the key that was used to encrypt files using the derived key.
openssl enc -aes-256-cbc -pbkdf2 -in symmetric_key.bin -out encrypted_symmetric_key.bin -pass file:./derived_key.bin

Similarly, we can decrypt the data in the following steps.

# Derive a shared key from our primary key.
openssl pkeyutl -derive -inkey private.pem -peerkey public.pem -out shared_key.bin

# Convert the shared key into an AES key.
openssl dgst -sha256 -binary shared_key.bin > derived_key.bin

# Decrypt the key that was used to decrypt files using the derived key.
openssl enc -d -aes-256-cbc -pbkdf2 -in encrypted_symmetric_key.bin -out decrypted_symmetric_key.bin -pass file:./derived_key.bin

# Decrypt the data
openssl enc -d -aes-256-cbc -pbkdf2 -in data_enc.bin -out data_decrypted -pass file:./decrypted_symmetric_key.bin

Your decrypted file should appear in data_decrypted

Outro

This is just a simple guide to signing, encrypting, and decrypting using OpenSSL, with ECC keys. ECC itself is very different, so it’s not as easy as RSA, but I’m sure that you have understood the basic operations by viewing this article.