2019-06-01 13:58:20 +00:00
|
|
|
|
// Copyright 2018 The mkcert Authors. All rights reserved.
|
2018-07-04 04:06:50 +00:00
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2018-06-28 03:43:51 +00:00
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
2019-02-02 20:26:03 +00:00
|
|
|
|
"crypto"
|
|
|
|
|
"crypto/ecdsa"
|
|
|
|
|
"crypto/elliptic"
|
2018-06-28 03:43:51 +00:00
|
|
|
|
"crypto/rand"
|
|
|
|
|
"crypto/rsa"
|
2018-07-30 01:14:15 +00:00
|
|
|
|
"crypto/sha1"
|
2018-06-28 03:43:51 +00:00
|
|
|
|
"crypto/x509"
|
|
|
|
|
"crypto/x509/pkix"
|
2018-07-30 01:14:15 +00:00
|
|
|
|
"encoding/asn1"
|
2018-06-28 03:43:51 +00:00
|
|
|
|
"encoding/pem"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"log"
|
|
|
|
|
"math/big"
|
|
|
|
|
"net"
|
2019-04-12 02:59:44 +00:00
|
|
|
|
"net/mail"
|
2019-07-05 04:16:19 +00:00
|
|
|
|
"net/url"
|
2018-06-28 03:43:51 +00:00
|
|
|
|
"os"
|
2018-07-03 23:52:18 +00:00
|
|
|
|
"os/user"
|
2018-06-28 03:43:51 +00:00
|
|
|
|
"path/filepath"
|
2018-07-03 20:55:12 +00:00
|
|
|
|
"regexp"
|
2018-06-28 03:43:51 +00:00
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
2018-08-13 03:21:46 +00:00
|
|
|
|
|
2019-02-02 19:45:36 +00:00
|
|
|
|
pkcs12 "software.sslmate.com/src/go-pkcs12"
|
2018-06-28 03:43:51 +00:00
|
|
|
|
)
|
|
|
|
|
|
2018-07-03 23:52:18 +00:00
|
|
|
|
var userAndHostname string
|
|
|
|
|
|
|
|
|
|
func init() {
|
2019-06-02 11:15:03 +00:00
|
|
|
|
u, err := user.Current()
|
|
|
|
|
if err == nil {
|
2018-07-03 23:52:18 +00:00
|
|
|
|
userAndHostname = u.Username + "@"
|
|
|
|
|
}
|
2019-06-02 11:15:03 +00:00
|
|
|
|
if h, err := os.Hostname(); err == nil {
|
|
|
|
|
userAndHostname += h
|
|
|
|
|
}
|
|
|
|
|
if err == nil && u.Name != "" && u.Name != u.Username {
|
|
|
|
|
userAndHostname += " (" + u.Name + ")"
|
|
|
|
|
}
|
2018-06-28 03:43:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-02 20:26:03 +00:00
|
|
|
|
priv, err := m.generateKey(false)
|
2018-06-28 03:43:51 +00:00
|
|
|
|
fatalIfErr(err, "failed to generate certificate key")
|
2019-02-02 20:26:03 +00:00
|
|
|
|
pub := priv.(crypto.Signer).Public()
|
2018-06-28 03:43:51 +00:00
|
|
|
|
|
2020-10-25 23:15:16 +00:00
|
|
|
|
// 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(2, 3, 0)
|
|
|
|
|
|
2018-06-28 03:43:51 +00:00
|
|
|
|
tpl := &x509.Certificate{
|
2019-02-02 23:51:24 +00:00
|
|
|
|
SerialNumber: randomSerialNumber(),
|
2018-06-28 03:43:51 +00:00
|
|
|
|
Subject: pkix.Name{
|
2018-07-03 23:52:18 +00:00
|
|
|
|
Organization: []string{"mkcert development certificate"},
|
|
|
|
|
OrganizationalUnit: []string{userAndHostname},
|
2018-06-28 03:43:51 +00:00
|
|
|
|
},
|
|
|
|
|
|
2020-10-27 11:51:28 +00:00
|
|
|
|
NotBefore: time.Now(), NotAfter: expiration,
|
2018-06-28 03:43:51 +00:00
|
|
|
|
|
2020-10-27 11:51:28 +00:00
|
|
|
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
2018-06-28 03:43:51 +00:00
|
|
|
|
}
|
2019-04-12 02:59:44 +00:00
|
|
|
|
|
2018-06-28 03:43:51 +00:00
|
|
|
|
for _, h := range hosts {
|
|
|
|
|
if ip := net.ParseIP(h); ip != nil {
|
|
|
|
|
tpl.IPAddresses = append(tpl.IPAddresses, ip)
|
2019-04-12 02:59:44 +00:00
|
|
|
|
} else if email, err := mail.ParseAddress(h); err == nil && email.Address == h {
|
|
|
|
|
tpl.EmailAddresses = append(tpl.EmailAddresses, h)
|
2019-07-05 04:16:19 +00:00
|
|
|
|
} else if uriName, err := url.Parse(h); err == nil && uriName.Scheme != "" && uriName.Host != "" {
|
|
|
|
|
tpl.URIs = append(tpl.URIs, uriName)
|
2018-06-28 03:43:51 +00:00
|
|
|
|
} else {
|
|
|
|
|
tpl.DNSNames = append(tpl.DNSNames, h)
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-12 02:59:44 +00:00
|
|
|
|
|
2019-02-02 20:44:12 +00:00
|
|
|
|
if m.client {
|
2020-10-27 11:51:28 +00:00
|
|
|
|
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)
|
2019-04-12 02:59:44 +00:00
|
|
|
|
}
|
|
|
|
|
if len(tpl.EmailAddresses) > 0 {
|
2020-10-27 11:25:23 +00:00
|
|
|
|
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
|
2019-02-02 20:44:12 +00:00
|
|
|
|
}
|
2018-06-28 03:43:51 +00:00
|
|
|
|
|
2019-02-02 19:45:36 +00:00
|
|
|
|
// 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]
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-02 20:26:03 +00:00
|
|
|
|
cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, pub, m.caKey)
|
2018-06-28 03:43:51 +00:00
|
|
|
|
fatalIfErr(err, "failed to generate certificate")
|
2019-01-06 23:55:49 +00:00
|
|
|
|
|
|
|
|
|
certFile, keyFile, p12File := m.fileNames(hosts)
|
|
|
|
|
|
2018-08-13 03:21:46 +00:00
|
|
|
|
if !m.pkcs12 {
|
2020-10-27 11:34:17 +00:00
|
|
|
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
|
2018-08-13 03:21:46 +00:00
|
|
|
|
privDER, err := x509.MarshalPKCS8PrivateKey(priv)
|
|
|
|
|
fatalIfErr(err, "failed to encode certificate key")
|
2020-10-27 11:34:17 +00:00
|
|
|
|
privPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privDER})
|
2018-06-28 03:43:51 +00:00
|
|
|
|
|
2020-10-27 11:34:17 +00:00
|
|
|
|
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")
|
|
|
|
|
}
|
2018-08-13 03:21:46 +00:00
|
|
|
|
} 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")
|
2019-01-06 23:55:49 +00:00
|
|
|
|
err = ioutil.WriteFile(p12File, pfxData, 0644)
|
2018-08-13 03:21:46 +00:00
|
|
|
|
fatalIfErr(err, "failed to save PKCS#12")
|
|
|
|
|
}
|
2018-07-04 20:35:53 +00:00
|
|
|
|
|
2019-02-02 23:51:24 +00:00
|
|
|
|
m.printHosts(hosts)
|
|
|
|
|
|
|
|
|
|
if !m.pkcs12 {
|
2020-10-27 11:34:17 +00:00
|
|
|
|
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)
|
|
|
|
|
}
|
2019-02-02 23:51:24 +00:00
|
|
|
|
} 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")
|
|
|
|
|
}
|
2020-10-25 23:15:16 +00:00
|
|
|
|
|
|
|
|
|
log.Printf("It will expire on %s 🗓\n\n", expiration.Format("2 January 2006"))
|
2019-02-02 23:51:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mkcert) printHosts(hosts []string) {
|
2018-07-03 20:55:12 +00:00
|
|
|
|
secondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\*\.[0-9a-z_-]+$`)
|
2018-06-28 03:43:51 +00:00
|
|
|
|
log.Printf("\nCreated a new certificate valid for the following names 📜")
|
|
|
|
|
for _, h := range hosts {
|
|
|
|
|
log.Printf(" - %q", h)
|
2018-07-03 20:55:12 +00:00
|
|
|
|
if secondLvlWildcardRegexp.MatchString(h) {
|
|
|
|
|
log.Printf(" Warning: many browsers don't support second-level wildcards like %q ⚠️", h)
|
|
|
|
|
}
|
2018-06-28 03:43:51 +00:00
|
|
|
|
}
|
2018-08-13 03:21:46 +00:00
|
|
|
|
|
2019-01-06 23:23:59 +00:00
|
|
|
|
for _, h := range hosts {
|
|
|
|
|
if strings.HasPrefix(h, "*.") {
|
|
|
|
|
log.Printf("\nReminder: X.509 wildcards only go one level deep, so this won't match a.b.%s ℹ️", h[2:])
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-28 03:43:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-02 20:26:03 +00:00
|
|
|
|
func (m *mkcert) generateKey(rootCA bool) (crypto.PrivateKey, error) {
|
|
|
|
|
if m.ecdsa {
|
|
|
|
|
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
|
|
|
}
|
|
|
|
|
if rootCA {
|
|
|
|
|
return rsa.GenerateKey(rand.Reader, 3072)
|
|
|
|
|
}
|
|
|
|
|
return rsa.GenerateKey(rand.Reader, 2048)
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-06 23:55:49 +00:00
|
|
|
|
func (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {
|
|
|
|
|
defaultName := strings.Replace(hosts[0], ":", "_", -1)
|
|
|
|
|
defaultName = strings.Replace(defaultName, "*", "_wildcard", -1)
|
|
|
|
|
if len(hosts) > 1 {
|
|
|
|
|
defaultName += "+" + strconv.Itoa(len(hosts)-1)
|
|
|
|
|
}
|
2019-02-05 19:19:51 +00:00
|
|
|
|
if m.client {
|
|
|
|
|
defaultName += "-client"
|
|
|
|
|
}
|
2019-01-06 23:55:49 +00:00
|
|
|
|
|
|
|
|
|
certFile = "./" + defaultName + ".pem"
|
|
|
|
|
if m.certFile != "" {
|
|
|
|
|
certFile = m.certFile
|
|
|
|
|
}
|
|
|
|
|
keyFile = "./" + defaultName + "-key.pem"
|
|
|
|
|
if m.keyFile != "" {
|
|
|
|
|
keyFile = m.keyFile
|
|
|
|
|
}
|
|
|
|
|
p12File = "./" + defaultName + ".p12"
|
|
|
|
|
if m.p12File != "" {
|
|
|
|
|
p12File = m.p12File
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-02 23:51:24 +00:00
|
|
|
|
func randomSerialNumber() *big.Int {
|
|
|
|
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
|
|
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
|
|
|
fatalIfErr(err, "failed to generate serial number")
|
|
|
|
|
return serialNumber
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
}
|
2020-10-25 16:59:00 +00:00
|
|
|
|
if csrPEM.Type != "CERTIFICATE REQUEST" &&
|
|
|
|
|
csrPEM.Type != "NEW CERTIFICATE REQUEST" {
|
2019-02-02 23:51:24 +00:00
|
|
|
|
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")
|
|
|
|
|
|
2020-10-27 11:54:36 +00:00
|
|
|
|
expiration := time.Now().AddDate(2, 3, 0)
|
2019-02-02 23:51:24 +00:00
|
|
|
|
tpl := &x509.Certificate{
|
|
|
|
|
SerialNumber: randomSerialNumber(),
|
|
|
|
|
Subject: csr.Subject,
|
2020-10-27 11:54:36 +00:00
|
|
|
|
ExtraExtensions: csr.Extensions, // includes requested SANs, KUs and EKUs
|
2019-02-02 23:51:24 +00:00
|
|
|
|
|
2020-10-27 11:54:36 +00:00
|
|
|
|
NotBefore: time.Now(), NotAfter: expiration,
|
2019-02-02 23:51:24 +00:00
|
|
|
|
|
|
|
|
|
// 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},
|
2020-10-27 11:54:36 +00:00
|
|
|
|
|
|
|
|
|
// 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)
|
2019-02-02 23:51:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, csr.PublicKey, m.caKey)
|
|
|
|
|
fatalIfErr(err, "failed to generate certificate")
|
2022-04-25 18:05:46 +00:00
|
|
|
|
c, err := x509.ParseCertificate(cert)
|
|
|
|
|
fatalIfErr(err, "failed to parse generated certificate")
|
2019-02-02 23:51:24 +00:00
|
|
|
|
|
|
|
|
|
var hosts []string
|
2022-04-25 18:05:46 +00:00
|
|
|
|
hosts = append(hosts, c.DNSNames...)
|
|
|
|
|
hosts = append(hosts, c.EmailAddresses...)
|
|
|
|
|
for _, ip := range c.IPAddresses {
|
2019-02-02 23:51:24 +00:00
|
|
|
|
hosts = append(hosts, ip.String())
|
|
|
|
|
}
|
2022-04-25 18:05:46 +00:00
|
|
|
|
for _, uri := range c.URIs {
|
2020-10-27 11:54:36 +00:00
|
|
|
|
hosts = append(hosts, uri.String())
|
2019-02-02 23:51:24 +00:00
|
|
|
|
}
|
|
|
|
|
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)
|
2020-10-27 11:54:36 +00:00
|
|
|
|
|
|
|
|
|
log.Printf("It will expire on %s 🗓\n\n", expiration.Format("2 January 2006"))
|
2019-02-02 23:51:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-28 03:43:51 +00:00
|
|
|
|
// loadCA will load or create the CA at CAROOT.
|
|
|
|
|
func (m *mkcert) loadCA() {
|
2019-06-01 13:55:58 +00:00
|
|
|
|
if !pathExists(filepath.Join(m.CAROOT, rootName)) {
|
2018-06-28 03:43:51 +00:00
|
|
|
|
m.newCA()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
certPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))
|
|
|
|
|
fatalIfErr(err, "failed to read the CA certificate")
|
|
|
|
|
certDERBlock, _ := pem.Decode(certPEMBlock)
|
|
|
|
|
if certDERBlock == nil || certDERBlock.Type != "CERTIFICATE" {
|
|
|
|
|
log.Fatalln("ERROR: failed to read the CA certificate: unexpected content")
|
|
|
|
|
}
|
|
|
|
|
m.caCert, err = x509.ParseCertificate(certDERBlock.Bytes)
|
|
|
|
|
fatalIfErr(err, "failed to parse the CA certificate")
|
|
|
|
|
|
2019-06-01 13:55:58 +00:00
|
|
|
|
if !pathExists(filepath.Join(m.CAROOT, rootKeyName)) {
|
2018-06-28 03:43:51 +00:00
|
|
|
|
return // keyless mode, where only -install works
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-06 23:55:49 +00:00
|
|
|
|
keyPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootKeyName))
|
2018-06-28 03:43:51 +00:00
|
|
|
|
fatalIfErr(err, "failed to read the CA key")
|
|
|
|
|
keyDERBlock, _ := pem.Decode(keyPEMBlock)
|
|
|
|
|
if keyDERBlock == nil || keyDERBlock.Type != "PRIVATE KEY" {
|
|
|
|
|
log.Fatalln("ERROR: failed to read the CA key: unexpected content")
|
|
|
|
|
}
|
|
|
|
|
m.caKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes)
|
|
|
|
|
fatalIfErr(err, "failed to parse the CA key")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mkcert) newCA() {
|
2019-02-02 20:26:03 +00:00
|
|
|
|
priv, err := m.generateKey(true)
|
2018-06-28 03:43:51 +00:00
|
|
|
|
fatalIfErr(err, "failed to generate the CA key")
|
2019-02-02 20:26:03 +00:00
|
|
|
|
pub := priv.(crypto.Signer).Public()
|
2018-06-28 03:43:51 +00:00
|
|
|
|
|
2019-02-02 20:26:03 +00:00
|
|
|
|
spkiASN1, err := x509.MarshalPKIXPublicKey(pub)
|
2018-07-30 01:14:15 +00:00
|
|
|
|
fatalIfErr(err, "failed to encode public key")
|
|
|
|
|
|
|
|
|
|
var spki struct {
|
|
|
|
|
Algorithm pkix.AlgorithmIdentifier
|
|
|
|
|
SubjectPublicKey asn1.BitString
|
|
|
|
|
}
|
|
|
|
|
_, err = asn1.Unmarshal(spkiASN1, &spki)
|
|
|
|
|
fatalIfErr(err, "failed to decode public key")
|
|
|
|
|
|
|
|
|
|
skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
|
|
|
|
|
|
2018-06-28 03:43:51 +00:00
|
|
|
|
tpl := &x509.Certificate{
|
2019-02-02 23:51:24 +00:00
|
|
|
|
SerialNumber: randomSerialNumber(),
|
2018-07-03 23:52:18 +00:00
|
|
|
|
Subject: pkix.Name{
|
|
|
|
|
Organization: []string{"mkcert development CA"},
|
|
|
|
|
OrganizationalUnit: []string{userAndHostname},
|
2018-07-30 01:15:25 +00:00
|
|
|
|
|
|
|
|
|
// The CommonName is required by iOS to show the certificate in the
|
|
|
|
|
// "Certificate Trust Settings" menu.
|
|
|
|
|
// https://github.com/FiloSottile/mkcert/issues/47
|
|
|
|
|
CommonName: "mkcert " + userAndHostname,
|
2018-07-03 23:52:18 +00:00
|
|
|
|
},
|
2018-07-30 01:14:15 +00:00
|
|
|
|
SubjectKeyId: skid[:],
|
2018-06-28 03:43:51 +00:00
|
|
|
|
|
|
|
|
|
NotAfter: time.Now().AddDate(10, 0, 0),
|
2018-07-03 23:52:18 +00:00
|
|
|
|
NotBefore: time.Now(),
|
2018-06-28 03:43:51 +00:00
|
|
|
|
|
|
|
|
|
KeyUsage: x509.KeyUsageCertSign,
|
|
|
|
|
|
|
|
|
|
BasicConstraintsValid: true,
|
2019-01-06 22:38:49 +00:00
|
|
|
|
IsCA: true,
|
|
|
|
|
MaxPathLenZero: true,
|
2018-06-28 03:43:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-02 20:26:03 +00:00
|
|
|
|
cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, pub, priv)
|
2018-06-28 03:43:51 +00:00
|
|
|
|
fatalIfErr(err, "failed to generate CA certificate")
|
|
|
|
|
|
|
|
|
|
privDER, err := x509.MarshalPKCS8PrivateKey(priv)
|
|
|
|
|
fatalIfErr(err, "failed to encode CA key")
|
2019-01-06 23:55:49 +00:00
|
|
|
|
err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootKeyName), pem.EncodeToMemory(
|
2018-06-28 03:43:51 +00:00
|
|
|
|
&pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0400)
|
|
|
|
|
fatalIfErr(err, "failed to save CA key")
|
|
|
|
|
|
|
|
|
|
err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootName), pem.EncodeToMemory(
|
|
|
|
|
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
|
2022-04-26 15:51:39 +00:00
|
|
|
|
fatalIfErr(err, "failed to save CA certificate")
|
2018-06-28 03:43:51 +00:00
|
|
|
|
|
2020-10-25 17:27:41 +00:00
|
|
|
|
log.Printf("Created a new local CA 💥\n")
|
2018-06-28 03:43:51 +00:00
|
|
|
|
}
|
2018-06-28 05:29:20 +00:00
|
|
|
|
|
|
|
|
|
func (m *mkcert) caUniqueName() string {
|
|
|
|
|
return "mkcert development CA " + m.caCert.SerialNumber.String()
|
|
|
|
|
}
|