Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom #537

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
172 changes: 172 additions & 0 deletions cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,105 @@ func init() {
}
}

//add params of custom cert expiration time
func (m *mkcert) makeCertCustom(hosts []string,years int) {
if m.caKey == nil {
log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
}

priv, err := m.generateKey(false)
fatalIfErr(err, "failed to generate certificate key")
pub := priv.(crypto.Signer).Public()

// Certificates last for 2 years and 3 months, which is always less than
// 825 days, the limit that macOS/iOS apply to all certificates,
// including custom roots. See https://support.apple.com/en-us/HT210176.
expiration := time.Now().AddDate(years, 0, 0)

tpl := &x509.Certificate{
SerialNumber: randomSerialNumber(),
Subject: pkix.Name{
Organization: []string{"mkcert development certificate"},
OrganizationalUnit: []string{userAndHostname},
},

NotBefore: time.Now(), NotAfter: expiration,

KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
}

for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
tpl.IPAddresses = append(tpl.IPAddresses, ip)
} else if email, err := mail.ParseAddress(h); err == nil && email.Address == h {
tpl.EmailAddresses = append(tpl.EmailAddresses, h)
} else if uriName, err := url.Parse(h); err == nil && uriName.Scheme != "" && uriName.Host != "" {
tpl.URIs = append(tpl.URIs, uriName)
} else {
tpl.DNSNames = append(tpl.DNSNames, h)
}
}

if m.client {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
}
if len(tpl.IPAddresses) > 0 || len(tpl.DNSNames) > 0 || len(tpl.URIs) > 0 {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
}
if len(tpl.EmailAddresses) > 0 {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
}

// IIS (the main target of PKCS #12 files), only shows the deprecated
// Common Name in the UI. See issue #115.
if m.pkcs12 {
tpl.Subject.CommonName = hosts[0]
}

cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, pub, m.caKey)
fatalIfErr(err, "failed to generate certificate")

certFile, keyFile, p12File := m.fileNames(hosts)

if !m.pkcs12 {
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
privDER, err := x509.MarshalPKCS8PrivateKey(priv)
fatalIfErr(err, "failed to encode certificate key")
privPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privDER})

if certFile == keyFile {
err = ioutil.WriteFile(keyFile, append(certPEM, privPEM...), 0600)
fatalIfErr(err, "failed to save certificate and key")
} else {
err = ioutil.WriteFile(certFile, certPEM, 0644)
fatalIfErr(err, "failed to save certificate")
err = ioutil.WriteFile(keyFile, privPEM, 0600)
fatalIfErr(err, "failed to save certificate key")
}
} else {
domainCert, _ := x509.ParseCertificate(cert)
pfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, "changeit")
fatalIfErr(err, "failed to generate PKCS#12")
err = ioutil.WriteFile(p12File, pfxData, 0644)
fatalIfErr(err, "failed to save PKCS#12")
}

m.printHosts(hosts)

if !m.pkcs12 {
if certFile == keyFile {
log.Printf("\nThe certificate and key are at \"%s\" ✅\n\n", certFile)
} else {
log.Printf("\nThe certificate is at \"%s\" and the key at \"%s\" ✅\n\n", certFile, keyFile)
}
} else {
log.Printf("\nThe PKCS#12 bundle is at \"%s\" ✅\n", p12File)
log.Printf("\nThe legacy PKCS#12 encryption password is the often hardcoded default \"changeit\" ℹ️\n\n")
}

log.Printf("It will expire on %s 🗓\n\n", expiration.Format("2 January 2006"))
}

func (m *mkcert) makeCert(hosts []string) {
if m.caKey == nil {
log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
Expand Down Expand Up @@ -206,6 +305,79 @@ func randomSerialNumber() *big.Int {
return serialNumber
}

//add params of custom cert expiration time
func (m *mkcert) makeCertFromCSRCustom(year int) {
if m.caKey == nil {
log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
}

csrPEMBytes, err := ioutil.ReadFile(m.csrPath)
fatalIfErr(err, "failed to read the CSR")
csrPEM, _ := pem.Decode(csrPEMBytes)
if csrPEM == nil {
log.Fatalln("ERROR: failed to read the CSR: unexpected content")
}
if csrPEM.Type != "CERTIFICATE REQUEST" &&
csrPEM.Type != "NEW CERTIFICATE REQUEST" {
log.Fatalln("ERROR: failed to read the CSR: expected CERTIFICATE REQUEST, got " + csrPEM.Type)
}
csr, err := x509.ParseCertificateRequest(csrPEM.Bytes)
fatalIfErr(err, "failed to parse the CSR")
fatalIfErr(csr.CheckSignature(), "invalid CSR signature")

expiration := time.Now().AddDate(year, 0, 0)
tpl := &x509.Certificate{
SerialNumber: randomSerialNumber(),
Subject: csr.Subject,
ExtraExtensions: csr.Extensions, // includes requested SANs, KUs and EKUs

NotBefore: time.Now(), NotAfter: expiration,

// If the CSR does not request a SAN extension, fix it up for them as
// the Common Name field does not work in modern browsers. Otherwise,
// this will get overridden.
DNSNames: []string{csr.Subject.CommonName},

// Likewise, if the CSR does not set KUs and EKUs, fix it up as Apple
// platforms require serverAuth for TLS.
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}

if m.client {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
}
if len(csr.EmailAddresses) > 0 {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
}

cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, csr.PublicKey, m.caKey)
fatalIfErr(err, "failed to generate certificate")
c, err := x509.ParseCertificate(cert)
fatalIfErr(err, "failed to parse generated certificate")

var hosts []string
hosts = append(hosts, c.DNSNames...)
hosts = append(hosts, c.EmailAddresses...)
for _, ip := range c.IPAddresses {
hosts = append(hosts, ip.String())
}
for _, uri := range c.URIs {
hosts = append(hosts, uri.String())
}
certFile, _, _ := m.fileNames(hosts)

err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
fatalIfErr(err, "failed to save certificate")

m.printHosts(hosts)

log.Printf("\nThe certificate is at \"%s\" ✅\n\n", certFile)

log.Printf("It will expire on %s 🗓\n\n", expiration.Format("2 January 2006"))
}

func (m *mkcert) makeCertFromCSR() {
if m.caKey == nil {
log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
Expand Down
15 changes: 13 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const shortUsage = `Usage of mkcert:
$ mkcert -uninstall
Uninstall the local CA (but do not delete it).

$ mkcert

`

const advancedUsage = `Advanced options:
Expand Down Expand Up @@ -103,6 +105,8 @@ func main() {
keyFileFlag = flag.String("key-file", "", "")
p12FileFlag = flag.String("p12-file", "", "")
versionFlag = flag.Bool("version", false, "")
//add params of custom cert expiration time
certYears = flag.Int("cert-years", 10, "10")
)
flag.Usage = func() {
fmt.Fprint(flag.CommandLine.Output(), shortUsage)
Expand Down Expand Up @@ -146,6 +150,7 @@ func main() {
installMode: *installFlag, uninstallMode: *uninstallFlag, csrPath: *csrFlag,
pkcs12: *pkcs12Flag, ecdsa: *ecdsaFlag, client: *clientFlag,
certFile: *certFileFlag, keyFile: *keyFileFlag, p12File: *p12FileFlag,
certYears:*certYears,
}).Run(flag.Args())
}

Expand All @@ -166,6 +171,8 @@ type mkcert struct {
// will keep failing until the next execution. TODO: maybe execve?
// https://github.com/golang/go/issues/24540 (thanks, myself)
ignoreCheckFailure bool
//add params of custom cert expiration time
certYears int
}

func (m *mkcert) Run(args []string) {
Expand Down Expand Up @@ -204,7 +211,9 @@ func (m *mkcert) Run(args []string) {
}

if m.csrPath != "" {
m.makeCertFromCSR()
//m.makeCertFromCSR()
//add params of custom cert expiration time
m.makeCertFromCSRCustom(m.certYears)
return
}

Expand Down Expand Up @@ -234,7 +243,9 @@ func (m *mkcert) Run(args []string) {
}
}

m.makeCert(args)
//m.makeCert(args)
//add params of custom cert expiration time
m.makeCertCustom(args,m.certYears)
}

func getCAROOT() string {
Expand Down