There are dedicated tools for signing code like minisign or signify.

This is post is about understanding the idea.

Providing a cryptographic hash like an **md5** or **sha256** checksum for a file you distribute only gives the receiver **integrity** verification, proof that a file has not been corrupted or tampered with.

Including a digital signature adds **authentication** and **non-repuditation**.

Public key cryptography is used to generate digital signatures.

In software development this is also called code signing.

The general algorithm:

**To Sign**

- Generate a hash of the data file
- Encrypt the hash with a private key producing a signature file
- Distribute the data and signature files

**To Verify**

- Generate a hash of the data file
- Use the public key to unencrypt the signature file
- Check that the two values match

Obviously the crypto hash algorithm has to be the same in both signing and verification.

So why not just sign the original file?

Public key cryptography is slow and the size of the file you can encrypt with an algorithm like RSA is limited. By hashing the data first and only **signing** the hash, we only need to encrypt a small file.

The OpenSSL command line utility provides all the tools we need to digitally sign files.

```
$ openssl version
OpenSSL 1.1.1f 31 Mar 2020
```

I am using **OpenSSL 1.1.1f** on an Ubuntu 20.04 machine for these examples.

### Create an elliptic-curve public/private key pair with genpkey

Generate a private key using one of the standard NIST curves **P-384**

```
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out ec-private.pem
```

And now a public key based on the private key

```
$ openssl pkey -in ec-private.pem -pubout -out ec-public.pem
```

The key sizes are relatively small

```
$ ls -l ec-*
-rw------- 1 scott scott 306 Aug 29 09:50 ec-private.pem
-rw-rw-r-- 1 scott scott 215 Aug 29 09:50 ec-public.pem
```

The private key **ec-private.pem** should be kept secret.

The public key **ec-public.pem** is meant to be shared.

### Signing

Openssl can do the signing, both hashing and encrypting, in one command.

Create a signature file like this

```
$ openssl dgst -sign ec-private.pem -out data.sig data
```

The signature file is small

```
$ ls -l data*
-rw-r--r-- 1 scott scott 415867 Aug 29 10:38 data
-rw-rw-r-- 1 scott scott 103 Aug 29 10:40 data.sig
```

It should be distributed with the **data** file.

### Hash algorithm

The default hashing (digest) algorithm is **sha256**.

You can change this by adding another argument to the signing command.

For instance to use **sha3-512**

```
$ openssl dgst -sha3-512 -sign ec-private.pem -out data.sig data
```

The available hash algorithms are

```
$ openssl list --digest-commands
blake2b512 blake2s256 gost md4
md5 rmd160 sha1 sha224
sha256 sha3-224 sha3-256 sha3-384
sha3-512 sha384 sha512 sha512-224
sha512-256 shake128 shake256 sm3
```

### Verifying

Verification requires the public key and knowledge of the hashing algorithm that was used.

If the default **sha256** was used

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

A failure looks like this

```
$ openssl dgst -verify ec-public.pem -signature data.sig modified-data
Verification Failure
```

If a different hash algorithm was used, this needs to be specified or the check will fail.

```
$ openssl dgst -sha3-512 -sign ec-private.pem -out data.sig data
$ openssl dgst -verify ec-public.pem -signature data.sig data
Verification Failure
$ openssl dgst -sha3-512 -verify ec-public.pem -signature data.sig data
Verified OK
```

The shell variable **$?** is set to zero (success) or one (failure) as you would expect.

### Encrypting the Private Key

For additional protection of the private key, you can encrypt it with a password.

The extra **-aes256** argument will encrypt the private key using the AES algorithm.

```
$ openssl genpkey -aes256 -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out ec-private.pem
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
```

Now whenever you use the private key, you will need the password

```
$ openssl pkey -in ec-private.pem -pubout -out ec-public.pem
Enter pass phrase for ec-private.pem:
$ openssl dgst -sign ec-private.pem -out data.sig data
Enter pass phrase for ec-private.pem:
```

The private key is slightly larger

```
$ ls -l ec-*
-rw------- 1 scott scott 464 Aug 29 10:59 ec-private.pem
-rw-rw-r-- 1 scott scott 215 Aug 29 11:00 ec-public.pem
```

Prompting for pass phrase is the default, but you can provide the password using other methods with the **-passin** argument

Directly in the command

```
$ openssl dgst -sign ec-private.pem -passin pass:the-password -out data.sig data
```

Using an environment variable

```
$ SECRET=the-password
$ openssl dgst -sign ec-private.pem -passin env:SECRET -out data.sig data
```

Using a **pathname** where the argument can be a file

```
$ echo the-password > secret
$ openssl dgst -sign private.pem -passin file:secret -out data.sig data
```

There are other options to provide the password. See the **Pass Phrase** section of the openssl(1) man page.

A password on the private key does not affect how the public key is used.

### Using RSA keys

RSA public key cryptography uses larger keys and is more CPU intensive, but can be used in a similar fashion.

Generating a 4096-bit RSA private key

```
$ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out rsa-private.pem
..........................................++++
...................................................++++
```

Generating a public key from the private key is the same

```
$ openssl pkey -in rsa-private.pem -pubout -out rsa-public.pem
```

The keys are larger

```
$ ls -l rsa*
-rw------- 1 scott scott 3272 Aug 29 11:10 rsa-private.pem
-rw-rw-r-- 1 scott scott 800 Aug 29 11:11 rsa-public.pem
```

Using the RSA keys for signing and verification are the same

```
$ openssl dgst -sign rsa-private.pem -out data.sig data
$ openssl dgst -verify rsa-public.pem -signature data.sig data
```

The signature is also larger with RSA keys

```
$ ls -l data*
-rw-r--r-- 1 scott scott 415867 Aug 29 10:38 data
-rw-rw-r-- 1 scott scott 512 Aug 29 11:25 data.sig
```

The commands for using different hash algorithms and encrypting the private key are the same.