cert.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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. "software.sslmate.com/src/go-pkcs12"
  26. )
  27. var userAndHostname string
  28. func init() {
  29. u, _ := user.Current()
  30. if u != nil {
  31. userAndHostname = u.Username + "@"
  32. }
  33. out, _ := exec.Command("hostname").Output()
  34. userAndHostname += strings.TrimSpace(string(out))
  35. }
  36. func (m *mkcert) makeCert(hosts []string) {
  37. if m.caKey == nil {
  38. log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
  39. }
  40. priv, err := rsa.GenerateKey(rand.Reader, 2048)
  41. fatalIfErr(err, "failed to generate certificate key")
  42. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  43. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  44. fatalIfErr(err, "failed to generate serial number")
  45. tpl := &x509.Certificate{
  46. SerialNumber: serialNumber,
  47. Subject: pkix.Name{
  48. Organization: []string{"mkcert development certificate"},
  49. OrganizationalUnit: []string{userAndHostname},
  50. },
  51. NotAfter: time.Now().AddDate(10, 0, 0),
  52. NotBefore: time.Now(),
  53. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  54. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  55. BasicConstraintsValid: true,
  56. }
  57. for _, h := range hosts {
  58. if ip := net.ParseIP(h); ip != nil {
  59. tpl.IPAddresses = append(tpl.IPAddresses, ip)
  60. } else {
  61. tpl.DNSNames = append(tpl.DNSNames, h)
  62. }
  63. }
  64. pub := priv.PublicKey
  65. cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, &pub, m.caKey)
  66. fatalIfErr(err, "failed to generate certificate")
  67. certFile, keyFile, p12File := m.fileNames(hosts)
  68. if !m.pkcs12 {
  69. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  70. fatalIfErr(err, "failed to encode certificate key")
  71. err = ioutil.WriteFile(keyFile, pem.EncodeToMemory(
  72. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0600)
  73. fatalIfErr(err, "failed to save certificate key")
  74. err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
  75. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  76. fatalIfErr(err, "failed to save certificate key")
  77. } else {
  78. domainCert, _ := x509.ParseCertificate(cert)
  79. pfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, "changeit")
  80. fatalIfErr(err, "failed to generate PKCS#12")
  81. err = ioutil.WriteFile(p12File, pfxData, 0644)
  82. fatalIfErr(err, "failed to save PKCS#12")
  83. }
  84. secondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\*\.[0-9a-z_-]+$`)
  85. log.Printf("\nCreated a new certificate valid for the following names 📜")
  86. for _, h := range hosts {
  87. log.Printf(" - %q", h)
  88. if secondLvlWildcardRegexp.MatchString(h) {
  89. log.Printf(" Warning: many browsers don't support second-level wildcards like %q ⚠️", h)
  90. }
  91. }
  92. for _, h := range hosts {
  93. if strings.HasPrefix(h, "*.") {
  94. log.Printf("\nReminder: X.509 wildcards only go one level deep, so this won't match a.b.%s ℹ️", h[2:])
  95. break
  96. }
  97. }
  98. if !m.pkcs12 {
  99. log.Printf("\nThe certificate is at \"%s\" and the key at \"%s\" ✅\n\n", certFile, keyFile)
  100. } else {
  101. log.Printf("\nThe PKCS#12 bundle is at \"%s\" ✅\n", p12File)
  102. log.Printf("\nThe legacy PKCS#12 encryption password is the often hardcoded default \"changeit\" ℹ️\n\n")
  103. }
  104. }
  105. func (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {
  106. defaultName := strings.Replace(hosts[0], ":", "_", -1)
  107. defaultName = strings.Replace(defaultName, "*", "_wildcard", -1)
  108. if len(hosts) > 1 {
  109. defaultName += "+" + strconv.Itoa(len(hosts)-1)
  110. }
  111. certFile = "./" + defaultName + ".pem"
  112. if m.certFile != "" {
  113. certFile = m.certFile
  114. }
  115. keyFile = "./" + defaultName + "-key.pem"
  116. if m.keyFile != "" {
  117. keyFile = m.keyFile
  118. }
  119. p12File = "./" + defaultName + ".p12"
  120. if m.p12File != "" {
  121. p12File = m.p12File
  122. }
  123. return
  124. }
  125. // loadCA will load or create the CA at CAROOT.
  126. func (m *mkcert) loadCA() {
  127. if _, err := os.Stat(filepath.Join(m.CAROOT, rootName)); os.IsNotExist(err) {
  128. m.newCA()
  129. } else {
  130. log.Printf("Using the local CA at \"%s\" ✨\n", m.CAROOT)
  131. }
  132. certPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))
  133. fatalIfErr(err, "failed to read the CA certificate")
  134. certDERBlock, _ := pem.Decode(certPEMBlock)
  135. if certDERBlock == nil || certDERBlock.Type != "CERTIFICATE" {
  136. log.Fatalln("ERROR: failed to read the CA certificate: unexpected content")
  137. }
  138. m.caCert, err = x509.ParseCertificate(certDERBlock.Bytes)
  139. fatalIfErr(err, "failed to parse the CA certificate")
  140. if _, err := os.Stat(filepath.Join(m.CAROOT, rootKeyName)); os.IsNotExist(err) {
  141. return // keyless mode, where only -install works
  142. }
  143. keyPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootKeyName))
  144. fatalIfErr(err, "failed to read the CA key")
  145. keyDERBlock, _ := pem.Decode(keyPEMBlock)
  146. if keyDERBlock == nil || keyDERBlock.Type != "PRIVATE KEY" {
  147. log.Fatalln("ERROR: failed to read the CA key: unexpected content")
  148. }
  149. m.caKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes)
  150. fatalIfErr(err, "failed to parse the CA key")
  151. }
  152. func (m *mkcert) newCA() {
  153. priv, err := rsa.GenerateKey(rand.Reader, 3072)
  154. fatalIfErr(err, "failed to generate the CA key")
  155. pub := priv.PublicKey
  156. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  157. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  158. fatalIfErr(err, "failed to generate serial number")
  159. spkiASN1, err := x509.MarshalPKIXPublicKey(&pub)
  160. fatalIfErr(err, "failed to encode public key")
  161. var spki struct {
  162. Algorithm pkix.AlgorithmIdentifier
  163. SubjectPublicKey asn1.BitString
  164. }
  165. _, err = asn1.Unmarshal(spkiASN1, &spki)
  166. fatalIfErr(err, "failed to decode public key")
  167. skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
  168. tpl := &x509.Certificate{
  169. SerialNumber: serialNumber,
  170. Subject: pkix.Name{
  171. Organization: []string{"mkcert development CA"},
  172. OrganizationalUnit: []string{userAndHostname},
  173. // The CommonName is required by iOS to show the certificate in the
  174. // "Certificate Trust Settings" menu.
  175. // https://github.com/FiloSottile/mkcert/issues/47
  176. CommonName: "mkcert " + userAndHostname,
  177. },
  178. SubjectKeyId: skid[:],
  179. NotAfter: time.Now().AddDate(10, 0, 0),
  180. NotBefore: time.Now(),
  181. KeyUsage: x509.KeyUsageCertSign,
  182. BasicConstraintsValid: true,
  183. IsCA: true,
  184. MaxPathLenZero: true,
  185. }
  186. cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, &pub, priv)
  187. fatalIfErr(err, "failed to generate CA certificate")
  188. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  189. fatalIfErr(err, "failed to encode CA key")
  190. err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootKeyName), pem.EncodeToMemory(
  191. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0400)
  192. fatalIfErr(err, "failed to save CA key")
  193. err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootName), pem.EncodeToMemory(
  194. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  195. fatalIfErr(err, "failed to save CA key")
  196. log.Printf("Created a new local CA at \"%s\" 💥\n", m.CAROOT)
  197. }
  198. func (m *mkcert) caUniqueName() string {
  199. return "mkcert development CA " + m.caCert.SerialNumber.String()
  200. }