From c881cfdfa298971233ecc20355200da4f1872899 Mon Sep 17 00:00:00 2001
From: Aaron Gable
Date: Wed, 3 Feb 2021 15:40:20 -0800
Subject: [PATCH 1/2] Support loading EC/ECDSA private keys
---
go.mod | 2 ++
main.go | 6 +++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/go.mod b/go.mod
index 0f868b4..d20f8af 100644
--- a/go.mod
+++ b/go.mod
@@ -1 +1,3 @@
module github.com/jsha/minica
+
+go 1.15
diff --git a/main.go b/main.go
index 9734969..af5899f 100644
--- a/main.go
+++ b/main.go
@@ -74,7 +74,11 @@ func readPrivateKey(keyContents []byte) (crypto.Signer, error) {
block, _ := pem.Decode(keyContents)
if block == nil {
return nil, fmt.Errorf("no PEM found")
- } else if block.Type != "RSA PRIVATE KEY" && block.Type != "ECDSA PRIVATE KEY" {
+ } else if block.Type == "RSA PRIVATE KEY" {
+ return x509.ParsePKCS1PrivateKey(block.Bytes)
+ } else if block.Type == "EC PRIVATE KEY" || block.Type == "ECDSA PRIVATE KEY" {
+ return x509.ParseECPrivateKey(block.Bytes)
+ } else {
return nil, fmt.Errorf("incorrect PEM type %s", block.Type)
}
return x509.ParsePKCS1PrivateKey(block.Bytes)
From 6f39c730eb028dbd58926e793f4f66416f673e82 Mon Sep 17 00:00:00 2001
From: Aaron Gable
Date: Wed, 3 Feb 2021 17:05:52 -0800
Subject: [PATCH 2/2] Full support for ECDSA issuance
---
main.go | 63 ++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 47 insertions(+), 16 deletions(-)
diff --git a/main.go b/main.go
index af5899f..30d357c 100644
--- a/main.go
+++ b/main.go
@@ -3,6 +3,8 @@ package main
import (
"bytes"
"crypto"
+ "crypto/ecdsa"
+ "crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
@@ -36,15 +38,15 @@ type issuer struct {
cert *x509.Certificate
}
-func getIssuer(keyFile, certFile string) (*issuer, error) {
+func getIssuer(keyFile, certFile string, alg x509.PublicKeyAlgorithm) (*issuer, error) {
keyContents, keyErr := ioutil.ReadFile(keyFile)
certContents, certErr := ioutil.ReadFile(certFile)
if os.IsNotExist(keyErr) && os.IsNotExist(certErr) {
- err := makeIssuer(keyFile, certFile)
+ err := makeIssuer(keyFile, certFile, alg)
if err != nil {
return nil, err
}
- return getIssuer(keyFile, certFile)
+ return getIssuer(keyFile, certFile, alg)
} else if keyErr != nil {
return nil, fmt.Errorf("%s (but %s exists)", keyErr, certFile)
} else if certErr != nil {
@@ -74,14 +76,25 @@ func readPrivateKey(keyContents []byte) (crypto.Signer, error) {
block, _ := pem.Decode(keyContents)
if block == nil {
return nil, fmt.Errorf("no PEM found")
+ } else if block.Type == "PRIVATE KEY" {
+ signer, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse PKCS8: %w", err)
+ }
+ switch t := signer.(type) {
+ case *rsa.PrivateKey:
+ return signer.(*rsa.PrivateKey), nil
+ case *ecdsa.PrivateKey:
+ return signer.(*ecdsa.PrivateKey), nil
+ default:
+ return nil, fmt.Errorf("unsupported PKCS8 key type: %t", t)
+ }
} else if block.Type == "RSA PRIVATE KEY" {
return x509.ParsePKCS1PrivateKey(block.Bytes)
} else if block.Type == "EC PRIVATE KEY" || block.Type == "ECDSA PRIVATE KEY" {
return x509.ParseECPrivateKey(block.Bytes)
- } else {
- return nil, fmt.Errorf("incorrect PEM type %s", block.Type)
}
- return x509.ParsePKCS1PrivateKey(block.Bytes)
+ return nil, fmt.Errorf("incorrect PEM type %s", block.Type)
}
func readCert(certContents []byte) (*x509.Certificate, error) {
@@ -94,8 +107,8 @@ func readCert(certContents []byte) (*x509.Certificate, error) {
return x509.ParseCertificate(block.Bytes)
}
-func makeIssuer(keyFile, certFile string) error {
- key, err := makeKey(keyFile)
+func makeIssuer(keyFile, certFile string, alg x509.PublicKeyAlgorithm) error {
+ key, err := makeKey(keyFile, alg)
if err != nil {
return err
}
@@ -106,12 +119,22 @@ func makeIssuer(keyFile, certFile string) error {
return nil
}
-func makeKey(filename string) (*rsa.PrivateKey, error) {
- key, err := rsa.GenerateKey(rand.Reader, 2048)
- if err != nil {
- return nil, err
+func makeKey(filename string, alg x509.PublicKeyAlgorithm) (crypto.Signer, error) {
+ var key crypto.Signer
+ var err error
+ switch {
+ case alg == x509.RSA:
+ key, err = rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ return nil, err
+ }
+ case alg == x509.ECDSA:
+ key, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
+ if err != nil {
+ return nil, err
+ }
}
- der := x509.MarshalPKCS1PrivateKey(key)
+ der, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}
@@ -121,7 +144,7 @@ func makeKey(filename string) (*rsa.PrivateKey, error) {
}
defer file.Close()
err = pem.Encode(file, &pem.Block{
- Type: "RSA PRIVATE KEY",
+ Type: "PRIVATE KEY",
Bytes: der,
})
if err != nil {
@@ -231,7 +254,7 @@ func sign(iss *issuer, domains []string, ipAddresses []string) (*x509.Certificat
if err != nil && !os.IsExist(err) {
return nil, err
}
- key, err := makeKey(fmt.Sprintf("%s/key.pem", cnFolder))
+ key, err := makeKey(fmt.Sprintf("%s/key.pem", cnFolder), x509.RSA)
if err != nil {
return nil, err
}
@@ -291,6 +314,7 @@ func split(s string) (results []string) {
func main2() error {
var caKey = flag.String("ca-key", "minica-key.pem", "Root private key filename, PEM encoded.")
var caCert = flag.String("ca-cert", "minica.pem", "Root certificate filename, PEM encoded.")
+ var caAlg = flag.String("ca-alg", "rsa", "Root keypair algorithm: RSA or ECDSA. Only used if generating new.")
var domains = flag.String("domains", "", "Comma separated domain names to include as Server Alternative Names.")
var ipAddresses = flag.String("ip-addresses", "", "Comma separated IP addresses to include as Server Alternative Names.")
flag.Usage = func() {
@@ -321,6 +345,13 @@ will not overwrite existing keys or certificates.
flag.Usage()
os.Exit(1)
}
+ alg := x509.RSA
+ if strings.ToLower(*caAlg) == "ecdsa" {
+ alg = x509.ECDSA
+ } else if strings.ToLower(*caCert) != "rsa" {
+ fmt.Printf("Unrecognized algorithm: %s (use RSA or ECDSA)\n", *caAlg)
+ os.Exit(1)
+ }
if len(flag.Args()) > 0 {
fmt.Printf("Extra arguments: %s (maybe there are spaces in your domain list?)\n", flag.Args())
os.Exit(1)
@@ -340,7 +371,7 @@ will not overwrite existing keys or certificates.
os.Exit(1)
}
}
- issuer, err := getIssuer(*caKey, *caCert)
+ issuer, err := getIssuer(*caKey, *caCert, alg)
if err != nil {
return err
}