cert.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "crypto/rand"
  7. "crypto/rsa"
  8. "crypto/sha1"
  9. "crypto/x509"
  10. "crypto/x509/pkix"
  11. "encoding/asn1"
  12. "encoding/pem"
  13. "io/ioutil"
  14. "log"
  15. "math/big"
  16. "net"
  17. "os"
  18. "os/exec"
  19. "os/user"
  20. "path/filepath"
  21. "regexp"
  22. "strconv"
  23. "strings"
  24. "time"
  25. )
  26. var userAndHostname string
  27. func init() {
  28. u, _ := user.Current()
  29. if u != nil {
  30. userAndHostname = u.Username + "@"
  31. }
  32. out, _ := exec.Command("hostname").Output()
  33. userAndHostname += strings.TrimSpace(string(out))
  34. }
  35. func (m *mkcert) makeCert(hosts []string) {
  36. if m.caKey == nil {
  37. log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
  38. }
  39. priv, err := rsa.GenerateKey(rand.Reader, 2048)
  40. fatalIfErr(err, "failed to generate certificate key")
  41. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  42. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  43. fatalIfErr(err, "failed to generate serial number")
  44. tpl := &x509.Certificate{
  45. SerialNumber: serialNumber,
  46. Subject: pkix.Name{
  47. Organization: []string{"mkcert development certificate"},
  48. OrganizationalUnit: []string{userAndHostname},
  49. },
  50. NotAfter: time.Now().AddDate(10, 0, 0),
  51. NotBefore: time.Now(),
  52. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  53. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  54. BasicConstraintsValid: true,
  55. }
  56. for _, h := range hosts {
  57. if ip := net.ParseIP(h); ip != nil {
  58. tpl.IPAddresses = append(tpl.IPAddresses, ip)
  59. } else {
  60. tpl.DNSNames = append(tpl.DNSNames, h)
  61. }
  62. }
  63. pub := priv.PublicKey
  64. cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, &pub, m.caKey)
  65. fatalIfErr(err, "failed to generate certificate")
  66. filename := strings.Replace(hosts[0], ":", "_", -1)
  67. filename = strings.Replace(filename, "*", "_wildcard", -1)
  68. if len(hosts) > 1 {
  69. filename += "+" + strconv.Itoa(len(hosts)-1)
  70. }
  71. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  72. fatalIfErr(err, "failed to encode certificate key")
  73. err = ioutil.WriteFile(filename+"-key.pem", pem.EncodeToMemory(
  74. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0600)
  75. fatalIfErr(err, "failed to save certificate key")
  76. err = ioutil.WriteFile(filename+".pem", pem.EncodeToMemory(
  77. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  78. fatalIfErr(err, "failed to save certificate key")
  79. secondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\*\.[0-9a-z_-]+$`)
  80. log.Printf("\nCreated a new certificate valid for the following names 📜")
  81. for _, h := range hosts {
  82. log.Printf(" - %q", h)
  83. if secondLvlWildcardRegexp.MatchString(h) {
  84. log.Printf(" Warning: many browsers don't support second-level wildcards like %q ⚠️", h)
  85. }
  86. }
  87. log.Printf("\nThe certificate is at \"./%s.pem\" and the key at \"./%s-key.pem\" ✅\n\n", filename, filename)
  88. }
  89. // loadCA will load or create the CA at CAROOT.
  90. func (m *mkcert) loadCA() {
  91. if _, err := os.Stat(filepath.Join(m.CAROOT, rootName)); os.IsNotExist(err) {
  92. m.newCA()
  93. } else {
  94. log.Printf("Using the local CA at \"%s\" ✨\n", m.CAROOT)
  95. }
  96. certPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))
  97. fatalIfErr(err, "failed to read the CA certificate")
  98. certDERBlock, _ := pem.Decode(certPEMBlock)
  99. if certDERBlock == nil || certDERBlock.Type != "CERTIFICATE" {
  100. log.Fatalln("ERROR: failed to read the CA certificate: unexpected content")
  101. }
  102. m.caCert, err = x509.ParseCertificate(certDERBlock.Bytes)
  103. fatalIfErr(err, "failed to parse the CA certificate")
  104. if _, err := os.Stat(filepath.Join(m.CAROOT, keyName)); os.IsNotExist(err) {
  105. return // keyless mode, where only -install works
  106. }
  107. keyPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, keyName))
  108. fatalIfErr(err, "failed to read the CA key")
  109. keyDERBlock, _ := pem.Decode(keyPEMBlock)
  110. if keyDERBlock == nil || keyDERBlock.Type != "PRIVATE KEY" {
  111. log.Fatalln("ERROR: failed to read the CA key: unexpected content")
  112. }
  113. m.caKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes)
  114. fatalIfErr(err, "failed to parse the CA key")
  115. }
  116. func (m *mkcert) newCA() {
  117. priv, err := rsa.GenerateKey(rand.Reader, 3072)
  118. fatalIfErr(err, "failed to generate the CA key")
  119. pub := priv.PublicKey
  120. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  121. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  122. fatalIfErr(err, "failed to generate serial number")
  123. spkiASN1, err := x509.MarshalPKIXPublicKey(&pub)
  124. fatalIfErr(err, "failed to encode public key")
  125. var spki struct {
  126. Algorithm pkix.AlgorithmIdentifier
  127. SubjectPublicKey asn1.BitString
  128. }
  129. _, err = asn1.Unmarshal(spkiASN1, &spki)
  130. fatalIfErr(err, "failed to decode public key")
  131. skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
  132. tpl := &x509.Certificate{
  133. SerialNumber: serialNumber,
  134. Subject: pkix.Name{
  135. Organization: []string{"mkcert development CA"},
  136. OrganizationalUnit: []string{userAndHostname},
  137. // The CommonName is required by iOS to show the certificate in the
  138. // "Certificate Trust Settings" menu.
  139. // https://github.com/FiloSottile/mkcert/issues/47
  140. CommonName: "mkcert " + userAndHostname,
  141. },
  142. SubjectKeyId: skid[:],
  143. NotAfter: time.Now().AddDate(10, 0, 0),
  144. NotBefore: time.Now(),
  145. KeyUsage: x509.KeyUsageCertSign,
  146. BasicConstraintsValid: true,
  147. IsCA: true,
  148. MaxPathLenZero: true,
  149. }
  150. cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, &pub, priv)
  151. fatalIfErr(err, "failed to generate CA certificate")
  152. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  153. fatalIfErr(err, "failed to encode CA key")
  154. err = ioutil.WriteFile(filepath.Join(m.CAROOT, keyName), pem.EncodeToMemory(
  155. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0400)
  156. fatalIfErr(err, "failed to save CA key")
  157. err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootName), pem.EncodeToMemory(
  158. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  159. fatalIfErr(err, "failed to save CA key")
  160. log.Printf("Created a new local CA at \"%s\" 💥\n", m.CAROOT)
  161. }
  162. func (m *mkcert) caUniqueName() string {
  163. return "mkcert development CA " + m.caCert.SerialNumber.String()
  164. }