Add ability to encrypt CA private key at rest (#386)

Fixes #8.

`nebula-cert ca` now supports encrypting the CA's private key with a
passphrase. Pass `-encrypt` in order to be prompted for a passphrase.
Encryption is performed using AES-256-GCM and Argon2id for KDF. KDF
parameters default to RFC recommendations, but can be overridden via CLI
flags `-argon-memory`, `-argon-parallelism`, and `-argon-iterations`.
This commit is contained in:
John Maguire
2023-04-03 13:59:38 -04:00
committed by GitHub
parent ee8e1348e9
commit a56a97e5c3
16 changed files with 1037 additions and 69 deletions

View File

@@ -1,6 +1,7 @@
package main
import (
"crypto/ed25519"
"crypto/rand"
"flag"
"fmt"
@@ -49,7 +50,7 @@ func newSignFlags() *signFlags {
}
func signCert(args []string, out io.Writer, errOut io.Writer) error {
func signCert(args []string, out io.Writer, errOut io.Writer, pr PasswordReader) error {
sf := newSignFlags()
err := sf.set.Parse(args)
if err != nil {
@@ -77,8 +78,36 @@ func signCert(args []string, out io.Writer, errOut io.Writer) error {
return fmt.Errorf("error while reading ca-key: %s", err)
}
caKey, _, err := cert.UnmarshalEd25519PrivateKey(rawCAKey)
if err != nil {
var caKey ed25519.PrivateKey
// naively attempt to decode the private key as though it is not encrypted
caKey, _, err = cert.UnmarshalEd25519PrivateKey(rawCAKey)
if err == cert.ErrPrivateKeyEncrypted {
// ask for a passphrase until we get one
var passphrase []byte
for i := 0; i < 5; i++ {
out.Write([]byte("Enter passphrase: "))
passphrase, err = pr.ReadPassword()
if err == ErrNoTerminal {
return fmt.Errorf("ca-key is encrypted and must be decrypted interactively")
} else if err != nil {
return fmt.Errorf("error reading password: %s", err)
}
if len(passphrase) > 0 {
break
}
}
if len(passphrase) == 0 {
return fmt.Errorf("cannot open encrypted ca-key without passphrase")
}
caKey, _, err = cert.DecryptAndUnmarshalEd25519PrivateKey(passphrase, rawCAKey)
if err != nil {
return fmt.Errorf("error while parsing encrypted ca-key: %s", err)
}
} else if err != nil {
return fmt.Errorf("error while parsing ca-key: %s", err)
}