diff --git a/ca.go b/ca.go index 3872a5b..18f8dd2 100644 --- a/ca.go +++ b/ca.go @@ -19,19 +19,23 @@ type Ca struct { KeyPEM []byte } -func NewCa(Organization, OrganizationalUnit, CommonName string) *Ca { +func NewCa(Organization, OrganizationalUnit, CommonName string, start, end time.Time) *Ca { var l []string + if Organization != "" { + l = []string{Organization} + } + var l1 []string if OrganizationalUnit != "" { - l = []string{OrganizationalUnit} + l1 = []string{OrganizationalUnit} } c := &x509.Certificate{ Subject: pkix.Name{ - Organization: []string{Organization}, - OrganizationalUnit: l, + Organization: l, + OrganizationalUnit: l1, CommonName: CommonName, }, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(10, 0, 0), + NotBefore: start, + NotAfter: end, IsCA: true, KeyUsage: x509.KeyUsageCertSign, BasicConstraintsValid: true, diff --git a/cert.go b/cert.go index 061ff86..a594728 100644 --- a/cert.go +++ b/cert.go @@ -23,18 +23,22 @@ type Cert struct { KeyPEM []byte } -func NewCert(caPEM, caKeyPEM []byte, Organization, OrganizationalUnit string) *Cert { +func NewCert(caPEM, caKeyPEM []byte, Organization, OrganizationalUnit string, start, end time.Time) *Cert { var l []string + if Organization != "" { + l = []string{Organization} + } + var l1 []string if OrganizationalUnit != "" { - l = []string{OrganizationalUnit} + l1 = []string{OrganizationalUnit} } c := &x509.Certificate{ Subject: pkix.Name{ - Organization: []string{Organization}, - OrganizationalUnit: l, + Organization: l, + OrganizationalUnit: l1, }, - NotBefore: time.Date(2019, time.June, 1, 0, 0, 0, 0, time.UTC), - NotAfter: time.Now().AddDate(10, 0, 0), + NotBefore: start, + NotAfter: end, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, BasicConstraintsValid: true, @@ -48,16 +52,16 @@ func NewCert(caPEM, caKeyPEM []byte, Organization, OrganizationalUnit string) *C func (c *Cert) SetIPAddresses(ips []net.IP) { c.C.IPAddresses = ips - if len(ips) > 0 { - c.C.Subject.CommonName = ips[0].String() - } + c.C.Subject.CommonName = ips[0].String() } func (c *Cert) SetDNSNames(domains []string) { c.C.DNSNames = domains - if len(domains) > 0 { - c.C.Subject.CommonName = domains[0] - } + c.C.Subject.CommonName = domains[0] +} + +func (c *Cert) SetCommonName(commonName string) { + c.C.Subject.CommonName = commonName } func (c *Cert) Create() error { diff --git a/cli/mad/main.go b/cli/mad/main.go index fc71ee8..bdb811e 100644 --- a/cli/mad/main.go +++ b/cli/mad/main.go @@ -2,11 +2,10 @@ package main import ( "errors" - "io/ioutil" "log" "net" - _ "net/http/pprof" "os" + "time" "github.com/txthinking/mad" "github.com/urfave/cli/v2" @@ -14,8 +13,8 @@ import ( func main() { app := cli.NewApp() - app.Name = "Mad" - app.Version = "20240428" + app.Name = "mad" + app.Version = "20240923" app.Usage = "Generate root CA and derivative certificate for any domains and any IPs" app.Authors = []*cli.Author{ { @@ -36,7 +35,7 @@ func main() { &cli.StringFlag{ Name: "key", Usage: "Key file which will be created or overwritten", - Value: "ca_key.pem", + Value: "ca.key.pem", }, &cli.StringFlag{ Name: "organization", @@ -50,13 +49,36 @@ func main() { Name: "commonName", Value: "github.com/txthinking/mad", }, + &cli.StringFlag{ + Name: "start", + Usage: "Certificate valid start time, such as: '2024-09-22T13:07:38+08:00'. If empty, it is the current time", + }, + &cli.StringFlag{ + Name: "end", + Usage: "Certificate valid end time, such as: '2024-09-22T13:07:38+08:00'. If empty, it is start time add 10 years", + }, &cli.BoolFlag{ Name: "install", - Usage: "Install CA", + Usage: "Install immediately after creation", }, }, Action: func(c *cli.Context) error { - ca := mad.NewCa(c.String("organization"), c.String("organizationUnit"), c.String("commonName")) + var err error + start := time.Now() + if c.String("start") != "" { + start, err = time.Parse(time.RFC3339, c.String("start")) + if err != nil { + return err + } + } + end := start.AddDate(10, 0, 0) + if c.String("end") != "" { + end, err = time.Parse(time.RFC3339, c.String("end")) + if err != nil { + return err + } + } + ca := mad.NewCa(c.String("organization"), c.String("organizationUnit"), c.String("commonName"), start, end) if err := ca.Create(); err != nil { return err } @@ -82,8 +104,12 @@ func main() { }, &cli.StringFlag{ Name: "ca_key", + Usage: "Deprecated, please use --caKey", + }, + &cli.StringFlag{ + Name: "caKey", Usage: "ROOT Key file path", - Value: "ca_key.pem", + Value: "ca.key.pem", }, &cli.StringFlag{ Name: "cert", @@ -93,7 +119,7 @@ func main() { &cli.StringFlag{ Name: "key", Usage: "Certificate key file which will be created or overwritten", - Value: "cert_key.pem", + Value: "cert.key.pem", }, &cli.StringFlag{ Name: "organization", @@ -111,17 +137,51 @@ func main() { Name: "domain", Usage: "Domain name", }, + &cli.StringFlag{ + Name: "commonName", + Usage: "If empty, the first domain or IP will be used", + }, + &cli.StringFlag{ + Name: "start", + Usage: "Certificate valid start time, such as: '2024-09-22T13:07:38+08:00'. If empty, it is the current time", + }, + &cli.StringFlag{ + Name: "end", + Usage: "Certificate valid end time, such as: '2024-09-22T13:07:38+08:00'. If empty, it is start time add 10 years", + }, }, Action: func(c *cli.Context) error { - ca, err := ioutil.ReadFile(c.String("ca")) + ca, err := os.ReadFile(c.String("ca")) if err != nil { return err } - caKey, err := ioutil.ReadFile(c.String("ca_key")) - if err != nil { - return err + var caKey []byte + if c.String("ca_key") != "" { + caKey, err = os.ReadFile(c.String("ca_key")) + if err != nil { + return err + } + } else { + caKey, err = os.ReadFile(c.String("caKey")) + if err != nil { + return err + } } - cert := mad.NewCert(ca, caKey, c.String("organization"), c.String("organizationUnit")) + start := time.Now() + if c.String("start") != "" { + start, err = time.Parse(time.RFC3339, c.String("start")) + if err != nil { + return err + } + } + end := start.AddDate(10, 0, 0) + if c.String("end") != "" { + end, err = time.Parse(time.RFC3339, c.String("end")) + if err != nil { + return err + } + } + cert := mad.NewCert(ca, caKey, c.String("organization"), c.String("organizationUnit"), start, end) ips := make([]net.IP, 0) for _, v := range c.StringSlice("ip") { ip := net.ParseIP(v) @@ -130,8 +190,15 @@ func main() { } ips = append(ips, ip) } - cert.SetIPAddresses(ips) - cert.SetDNSNames(c.StringSlice("domain")) + if len(ips) > 0 { + cert.SetIPAddresses(ips) + } + if len(c.StringSlice("domain")) > 0 { + cert.SetDNSNames(c.StringSlice("domain")) + } + if c.String("commonName") != "" { + cert.SetCommonName(c.String("commonName")) + } if err := cert.Create(); err != nil { return err }