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") }