cert.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package main
  2. import (
  3. "crypto/rand"
  4. "crypto/rsa"
  5. "crypto/x509"
  6. "crypto/x509/pkix"
  7. "encoding/pem"
  8. "io/ioutil"
  9. "log"
  10. "math/big"
  11. "net"
  12. "os"
  13. "path/filepath"
  14. "strconv"
  15. "strings"
  16. "time"
  17. )
  18. var rootSubject = pkix.Name{
  19. Organization: []string{"mkcert development CA"},
  20. }
  21. func (m *mkcert) makeCert(hosts []string) {
  22. if m.caKey == nil {
  23. log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
  24. }
  25. priv, err := rsa.GenerateKey(rand.Reader, 2048)
  26. fatalIfErr(err, "failed to generate certificate key")
  27. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  28. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  29. fatalIfErr(err, "failed to generate serial number")
  30. tpl := &x509.Certificate{
  31. SerialNumber: serialNumber,
  32. Subject: pkix.Name{
  33. Organization: []string{"mkcert development certificate"},
  34. },
  35. NotAfter: time.Now().AddDate(10, 0, 0),
  36. NotBefore: time.Now().AddDate(0, 0, -1),
  37. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  38. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  39. BasicConstraintsValid: true,
  40. }
  41. for _, h := range hosts {
  42. if ip := net.ParseIP(h); ip != nil {
  43. tpl.IPAddresses = append(tpl.IPAddresses, ip)
  44. } else {
  45. tpl.DNSNames = append(tpl.DNSNames, h)
  46. }
  47. }
  48. pub := priv.PublicKey
  49. cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, &pub, m.caKey)
  50. fatalIfErr(err, "failed to generate certificate")
  51. filename := strings.Replace(hosts[0], ":", "_", -1)
  52. filename = strings.Replace(filename, "*", "_wildcard", -1)
  53. if len(hosts) > 1 {
  54. filename += "+" + strconv.Itoa(len(hosts)-1)
  55. }
  56. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  57. fatalIfErr(err, "failed to encode certificate key")
  58. err = ioutil.WriteFile(filename+"-key.pem", pem.EncodeToMemory(
  59. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0600)
  60. fatalIfErr(err, "failed to save certificate key")
  61. err = ioutil.WriteFile(filename+".pem", pem.EncodeToMemory(
  62. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  63. fatalIfErr(err, "failed to save certificate key")
  64. log.Printf("\nCreated a new certificate valid for the following names 📜")
  65. for _, h := range hosts {
  66. log.Printf(" - %q", h)
  67. }
  68. log.Printf("\nThe certificate is at \"./%s.pem\" and the key at \"./%s-key.pem\" ✅\n\n", filename, filename)
  69. }
  70. // loadCA will load or create the CA at CAROOT.
  71. func (m *mkcert) loadCA() {
  72. if _, err := os.Stat(filepath.Join(m.CAROOT, rootName)); os.IsNotExist(err) {
  73. m.newCA()
  74. } else {
  75. log.Printf("Using the local CA at \"%s\" ✨\n", m.CAROOT)
  76. }
  77. certPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))
  78. fatalIfErr(err, "failed to read the CA certificate")
  79. certDERBlock, _ := pem.Decode(certPEMBlock)
  80. if certDERBlock == nil || certDERBlock.Type != "CERTIFICATE" {
  81. log.Fatalln("ERROR: failed to read the CA certificate: unexpected content")
  82. }
  83. m.caCert, err = x509.ParseCertificate(certDERBlock.Bytes)
  84. fatalIfErr(err, "failed to parse the CA certificate")
  85. if _, err := os.Stat(filepath.Join(m.CAROOT, keyName)); os.IsNotExist(err) {
  86. return // keyless mode, where only -install works
  87. }
  88. keyPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, keyName))
  89. fatalIfErr(err, "failed to read the CA key")
  90. keyDERBlock, _ := pem.Decode(keyPEMBlock)
  91. if keyDERBlock == nil || keyDERBlock.Type != "PRIVATE KEY" {
  92. log.Fatalln("ERROR: failed to read the CA key: unexpected content")
  93. }
  94. m.caKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes)
  95. fatalIfErr(err, "failed to parse the CA key")
  96. }
  97. func (m *mkcert) newCA() {
  98. priv, err := rsa.GenerateKey(rand.Reader, 3072)
  99. fatalIfErr(err, "failed to generate the CA key")
  100. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  101. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  102. fatalIfErr(err, "failed to generate serial number")
  103. tpl := &x509.Certificate{
  104. SerialNumber: serialNumber,
  105. Subject: rootSubject,
  106. NotAfter: time.Now().AddDate(10, 0, 0),
  107. NotBefore: time.Now().AddDate(0, 0, -1),
  108. KeyUsage: x509.KeyUsageCertSign,
  109. BasicConstraintsValid: true,
  110. IsCA: true,
  111. MaxPathLenZero: true,
  112. }
  113. pub := priv.PublicKey
  114. cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, &pub, priv)
  115. fatalIfErr(err, "failed to generate CA certificate")
  116. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  117. fatalIfErr(err, "failed to encode CA key")
  118. err = ioutil.WriteFile(filepath.Join(m.CAROOT, keyName), pem.EncodeToMemory(
  119. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0400)
  120. fatalIfErr(err, "failed to save CA key")
  121. err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootName), pem.EncodeToMemory(
  122. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  123. fatalIfErr(err, "failed to save CA key")
  124. log.Printf("Created a new local CA at \"%s\" 💥\n", m.CAROOT)
  125. }
  126. func (m *mkcert) caUniqueName() string {
  127. return "mkcert development CA " + m.caCert.SerialNumber.String()
  128. }