From e0b31b11f06e6bed54539a15e50ea1ae63e9794b Mon Sep 17 00:00:00 2001
From: Noah Pederson
Date: Sat, 27 Apr 2024 13:05:14 -0500
Subject: [PATCH 1/4] Ignore minica file
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a4281b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+minica
From f3890d4a06f5ae62a183b2d4e08625ebc0d88b6c Mon Sep 17 00:00:00 2001
From: Noah Pederson
Date: Sat, 27 Apr 2024 13:08:09 -0500
Subject: [PATCH 2/4] Add URIs and Email SAN fields
Additionall, modernizes a bit for newer versions of Go:
- Use more idiomatic errors in a few places
- Remove use of deprecated ioutil package
Fixes incorrect name for SANS: Server => Subject
---
main.go | 50 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 34 insertions(+), 16 deletions(-)
diff --git a/main.go b/main.go
index 591bf4f..0ef120a 100644
--- a/main.go
+++ b/main.go
@@ -15,10 +15,11 @@ import (
"encoding/pem"
"flag"
"fmt"
- "io/ioutil"
"log"
"math"
"math/big"
+ "net/mail"
+ "net/url"
"net"
"os"
"regexp"
@@ -39,8 +40,8 @@ type issuer struct {
}
func getIssuer(keyFile, certFile string, alg x509.PublicKeyAlgorithm) (*issuer, error) {
- keyContents, keyErr := ioutil.ReadFile(keyFile)
- certContents, certErr := ioutil.ReadFile(certFile)
+ keyContents, keyErr := os.ReadFile(keyFile)
+ certContents, certErr := os.ReadFile(certFile)
if os.IsNotExist(keyErr) && os.IsNotExist(certErr) {
err := makeIssuer(keyFile, certFile, alg)
if err != nil {
@@ -240,12 +241,16 @@ func calculateSKID(pubKey crypto.PublicKey) ([]byte, error) {
return skid[:], nil
}
-func sign(iss *issuer, domains []string, ipAddresses []string, alg x509.PublicKeyAlgorithm) (*x509.Certificate, error) {
+func sign(iss *issuer, domains []string, ipAddresses []net.IP, emails []string, uris []*url.URL, alg x509.PublicKeyAlgorithm) (*x509.Certificate, error) {
var cn string
if len(domains) > 0 {
cn = domains[0]
} else if len(ipAddresses) > 0 {
- cn = ipAddresses[0]
+ cn = ipAddresses[0].String()
+ } else if len(emails) > 0 {
+ cn = emails[0]
+ } else if len(uris) > 0 {
+ cn = uris[0].String()
} else {
return nil, fmt.Errorf("must specify at least one domain name or IP address")
}
@@ -258,17 +263,15 @@ func sign(iss *issuer, domains []string, ipAddresses []string, alg x509.PublicKe
if err != nil {
return nil, err
}
- parsedIPs, err := parseIPs(ipAddresses)
- if err != nil {
- return nil, err
- }
serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
return nil, err
}
template := &x509.Certificate{
DNSNames: domains,
- IPAddresses: parsedIPs,
+ IPAddresses: ipAddresses,
+ EmailAddresses: emails,
+ URIs: uris,
Subject: pkix.Name{
CommonName: cn,
},
@@ -315,7 +318,9 @@ 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", "ecdsa", "Algorithm for any new keypairs: RSA or ECDSA.")
- var domains = flag.String("domains", "", "Comma separated domain names to include as Server Alternative Names.")
+ var domains = flag.String("domains", "", "Comma separated domain names to include as Subject Alternative Names.")
+ var emails = flag.String("emails", "", "Comma separated email addresses to include as Subject Alternative Names.")
+ var uris = flag.String("uris", "", "Comma separated URIs to include as Subject Alternative Names.")
var ipAddresses = flag.String("ip-addresses", "", "Comma separated IP addresses to include as Server Alternative Names.")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
@@ -341,7 +346,7 @@ will not overwrite existing keys or certificates.
flag.PrintDefaults()
}
flag.Parse()
- if *domains == "" && *ipAddresses == "" {
+ if *domains == "" && *ipAddresses == "" && *emails == "" {
flag.Usage()
os.Exit(1)
}
@@ -365,16 +370,29 @@ will not overwrite existing keys or certificates.
}
}
ipSlice := split(*ipAddresses)
- for _, ip := range ipSlice {
- if net.ParseIP(ip) == nil {
- fmt.Printf("Invalid IP address %q\n", ip)
+ parsedIPs, err := parseIPs(ipSlice); if err != nil {
+ fmt.Println(err.Error())
+ os.Exit(1)
+ }
+ emailSlice := split(*emails)
+ for _, email := range emailSlice {
+ _, err := mail.ParseAddress(email); if err != nil {
+ fmt.Printf("Invalid email address %q\n%e\n", email, err)
os.Exit(1)
}
}
+ uriSlice := split(*uris)
+ parsedURIs := make([]*url.URL, 0, len(uriSlice))
+ for _, uri := range uriSlice {
+ parsedURI, err := url.Parse(uri); if err != nil {
+ fmt.Printf("Invalid URI %q\n%e\n", uri, err)
+ }
+ parsedURIs = append(parsedURIs, parsedURI)
+ }
issuer, err := getIssuer(*caKey, *caCert, alg)
if err != nil {
return err
}
- _, err = sign(issuer, domainSlice, ipSlice, alg)
+ _, err = sign(issuer, domainSlice, parsedIPs, emailSlice, parsedURIs, alg)
return err
}
From 35b93e587e81ccd01b590b4062b7296d47e12e98 Mon Sep 17 00:00:00 2001
From: Noah Pederson
Date: Sat, 27 Apr 2024 13:15:33 -0500
Subject: [PATCH 3/4] Bump Go version
---
go.mod | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/go.mod b/go.mod
index d20f8af..6bf3c60 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
module github.com/jsha/minica
-go 1.15
+go 1.21
From 7713fef7f90e3d66fa4cdb0ef1c1a5e781e50d7c Mon Sep 17 00:00:00 2001
From: Noah Pederson
Date: Sat, 27 Apr 2024 13:53:59 -0500
Subject: [PATCH 4/4] Emails take precidence for CN over URIs and IPs
---
main.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/main.go b/main.go
index 0ef120a..8eabf6c 100644
--- a/main.go
+++ b/main.go
@@ -245,12 +245,12 @@ func sign(iss *issuer, domains []string, ipAddresses []net.IP, emails []string,
var cn string
if len(domains) > 0 {
cn = domains[0]
- } else if len(ipAddresses) > 0 {
- cn = ipAddresses[0].String()
} else if len(emails) > 0 {
cn = emails[0]
} else if len(uris) > 0 {
cn = uris[0].String()
+ } else if len(ipAddresses) > 0 {
+ cn = ipAddresses[0].String()
} else {
return nil, fmt.Errorf("must specify at least one domain name or IP address")
}