cert.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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"
  7. "crypto/ecdsa"
  8. "crypto/elliptic"
  9. "crypto/rand"
  10. "crypto/rsa"
  11. "crypto/sha1"
  12. "crypto/x509"
  13. "crypto/x509/pkix"
  14. "encoding/asn1"
  15. "encoding/pem"
  16. "io/ioutil"
  17. "log"
  18. "math/big"
  19. "net"
  20. "os"
  21. "os/user"
  22. "path/filepath"
  23. "regexp"
  24. "strconv"
  25. "strings"
  26. "time"
  27. pkcs12 "software.sslmate.com/src/go-pkcs12"
  28. )
  29. var userAndHostname string
  30. func init() {
  31. u, _ := user.Current()
  32. if u != nil {
  33. userAndHostname = u.Username + "@"
  34. }
  35. hostname, _ := os.Hostname()
  36. userAndHostname += hostname
  37. }
  38. func (m *mkcert) makeCert(hosts []string) {
  39. if m.caKey == nil {
  40. log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
  41. }
  42. priv, err := m.generateKey(false)
  43. fatalIfErr(err, "failed to generate certificate key")
  44. pub := priv.(crypto.Signer).Public()
  45. tpl := &x509.Certificate{
  46. SerialNumber: randomSerialNumber(),
  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. if m.client {
  65. tpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
  66. }
  67. // IIS (the main target of PKCS #12 files), only shows the deprecated
  68. // Common Name in the UI. See issue #115.
  69. if m.pkcs12 {
  70. tpl.Subject.CommonName = hosts[0]
  71. }
  72. cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, pub, m.caKey)
  73. fatalIfErr(err, "failed to generate certificate")
  74. certFile, keyFile, p12File := m.fileNames(hosts)
  75. if !m.pkcs12 {
  76. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  77. fatalIfErr(err, "failed to encode certificate key")
  78. err = ioutil.WriteFile(keyFile, pem.EncodeToMemory(
  79. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0600)
  80. fatalIfErr(err, "failed to save certificate key")
  81. err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
  82. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  83. fatalIfErr(err, "failed to save certificate")
  84. } else {
  85. domainCert, _ := x509.ParseCertificate(cert)
  86. pfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, "changeit")
  87. fatalIfErr(err, "failed to generate PKCS#12")
  88. err = ioutil.WriteFile(p12File, pfxData, 0644)
  89. fatalIfErr(err, "failed to save PKCS#12")
  90. }
  91. m.printHosts(hosts)
  92. if !m.pkcs12 {
  93. log.Printf("\nThe certificate is at \"%s\" and the key at \"%s\" ✅\n\n", certFile, keyFile)
  94. } else {
  95. log.Printf("\nThe PKCS#12 bundle is at \"%s\" ✅\n", p12File)
  96. log.Printf("\nThe legacy PKCS#12 encryption password is the often hardcoded default \"changeit\" ℹ️\n\n")
  97. }
  98. }
  99. func (m *mkcert) printHosts(hosts []string) {
  100. secondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\*\.[0-9a-z_-]+$`)
  101. log.Printf("\nCreated a new certificate valid for the following names 📜")
  102. for _, h := range hosts {
  103. log.Printf(" - %q", h)
  104. if secondLvlWildcardRegexp.MatchString(h) {
  105. log.Printf(" Warning: many browsers don't support second-level wildcards like %q ⚠️", h)
  106. }
  107. }
  108. for _, h := range hosts {
  109. if strings.HasPrefix(h, "*.") {
  110. log.Printf("\nReminder: X.509 wildcards only go one level deep, so this won't match a.b.%s ℹ️", h[2:])
  111. break
  112. }
  113. }
  114. }
  115. func (m *mkcert) generateKey(rootCA bool) (crypto.PrivateKey, error) {
  116. if m.ecdsa {
  117. return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  118. }
  119. if rootCA {
  120. return rsa.GenerateKey(rand.Reader, 3072)
  121. }
  122. return rsa.GenerateKey(rand.Reader, 2048)
  123. }
  124. func (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {
  125. defaultName := strings.Replace(hosts[0], ":", "_", -1)
  126. defaultName = strings.Replace(defaultName, "*", "_wildcard", -1)
  127. if len(hosts) > 1 {
  128. defaultName += "+" + strconv.Itoa(len(hosts)-1)
  129. }
  130. if m.client {
  131. defaultName += "-client"
  132. }
  133. certFile = "./" + defaultName + ".pem"
  134. if m.certFile != "" {
  135. certFile = m.certFile
  136. }
  137. keyFile = "./" + defaultName + "-key.pem"
  138. if m.keyFile != "" {
  139. keyFile = m.keyFile
  140. }
  141. p12File = "./" + defaultName + ".p12"
  142. if m.p12File != "" {
  143. p12File = m.p12File
  144. }
  145. return
  146. }
  147. func randomSerialNumber() *big.Int {
  148. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  149. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  150. fatalIfErr(err, "failed to generate serial number")
  151. return serialNumber
  152. }
  153. func (m *mkcert) makeCertFromCSR() {
  154. if m.caKey == nil {
  155. log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
  156. }
  157. csrPEMBytes, err := ioutil.ReadFile(m.csrPath)
  158. fatalIfErr(err, "failed to read the CSR")
  159. csrPEM, _ := pem.Decode(csrPEMBytes)
  160. if csrPEM == nil {
  161. log.Fatalln("ERROR: failed to read the CSR: unexpected content")
  162. }
  163. if csrPEM.Type != "CERTIFICATE REQUEST" {
  164. log.Fatalln("ERROR: failed to read the CSR: expected CERTIFICATE REQUEST, got " + csrPEM.Type)
  165. }
  166. csr, err := x509.ParseCertificateRequest(csrPEM.Bytes)
  167. fatalIfErr(err, "failed to parse the CSR")
  168. fatalIfErr(csr.CheckSignature(), "invalid CSR signature")
  169. tpl := &x509.Certificate{
  170. SerialNumber: randomSerialNumber(),
  171. Subject: csr.Subject,
  172. ExtraExtensions: csr.Extensions, // includes requested SANs
  173. NotAfter: time.Now().AddDate(10, 0, 0),
  174. NotBefore: time.Now(),
  175. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  176. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  177. BasicConstraintsValid: true,
  178. // If the CSR does not request a SAN extension, fix it up for them as
  179. // the Common Name field does not work in modern browsers. Otherwise,
  180. // this will get overridden.
  181. DNSNames: []string{csr.Subject.CommonName},
  182. }
  183. cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, csr.PublicKey, m.caKey)
  184. fatalIfErr(err, "failed to generate certificate")
  185. var hosts []string
  186. hosts = append(hosts, csr.DNSNames...)
  187. hosts = append(hosts, csr.EmailAddresses...)
  188. for _, ip := range csr.IPAddresses {
  189. hosts = append(hosts, ip.String())
  190. }
  191. if len(hosts) == 0 {
  192. hosts = []string{csr.Subject.CommonName}
  193. }
  194. certFile, _, _ := m.fileNames(hosts)
  195. err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
  196. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  197. fatalIfErr(err, "failed to save certificate")
  198. m.printHosts(hosts)
  199. log.Printf("\nThe certificate is at \"%s\" ✅\n\n", certFile)
  200. }
  201. // loadCA will load or create the CA at CAROOT.
  202. func (m *mkcert) loadCA() {
  203. if _, err := os.Stat(filepath.Join(m.CAROOT, rootName)); os.IsNotExist(err) {
  204. m.newCA()
  205. } else {
  206. log.Printf("Using the local CA at \"%s\" ✨\n", m.CAROOT)
  207. }
  208. certPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))
  209. fatalIfErr(err, "failed to read the CA certificate")
  210. certDERBlock, _ := pem.Decode(certPEMBlock)
  211. if certDERBlock == nil || certDERBlock.Type != "CERTIFICATE" {
  212. log.Fatalln("ERROR: failed to read the CA certificate: unexpected content")
  213. }
  214. m.caCert, err = x509.ParseCertificate(certDERBlock.Bytes)
  215. fatalIfErr(err, "failed to parse the CA certificate")
  216. if _, err := os.Stat(filepath.Join(m.CAROOT, rootKeyName)); os.IsNotExist(err) {
  217. return // keyless mode, where only -install works
  218. }
  219. keyPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootKeyName))
  220. fatalIfErr(err, "failed to read the CA key")
  221. keyDERBlock, _ := pem.Decode(keyPEMBlock)
  222. if keyDERBlock == nil || keyDERBlock.Type != "PRIVATE KEY" {
  223. log.Fatalln("ERROR: failed to read the CA key: unexpected content")
  224. }
  225. m.caKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes)
  226. fatalIfErr(err, "failed to parse the CA key")
  227. }
  228. func (m *mkcert) newCA() {
  229. priv, err := m.generateKey(true)
  230. fatalIfErr(err, "failed to generate the CA key")
  231. pub := priv.(crypto.Signer).Public()
  232. spkiASN1, err := x509.MarshalPKIXPublicKey(pub)
  233. fatalIfErr(err, "failed to encode public key")
  234. var spki struct {
  235. Algorithm pkix.AlgorithmIdentifier
  236. SubjectPublicKey asn1.BitString
  237. }
  238. _, err = asn1.Unmarshal(spkiASN1, &spki)
  239. fatalIfErr(err, "failed to decode public key")
  240. skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
  241. tpl := &x509.Certificate{
  242. SerialNumber: randomSerialNumber(),
  243. Subject: pkix.Name{
  244. Organization: []string{"mkcert development CA"},
  245. OrganizationalUnit: []string{userAndHostname},
  246. // The CommonName is required by iOS to show the certificate in the
  247. // "Certificate Trust Settings" menu.
  248. // https://github.com/FiloSottile/mkcert/issues/47
  249. CommonName: "mkcert " + userAndHostname,
  250. },
  251. SubjectKeyId: skid[:],
  252. NotAfter: time.Now().AddDate(10, 0, 0),
  253. NotBefore: time.Now(),
  254. KeyUsage: x509.KeyUsageCertSign,
  255. BasicConstraintsValid: true,
  256. IsCA: true,
  257. MaxPathLenZero: true,
  258. }
  259. cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, pub, priv)
  260. fatalIfErr(err, "failed to generate CA certificate")
  261. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  262. fatalIfErr(err, "failed to encode CA key")
  263. err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootKeyName), pem.EncodeToMemory(
  264. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0400)
  265. fatalIfErr(err, "failed to save CA key")
  266. err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootName), pem.EncodeToMemory(
  267. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  268. fatalIfErr(err, "failed to save CA key")
  269. log.Printf("Created a new local CA at \"%s\" 💥\n", m.CAROOT)
  270. }
  271. func (m *mkcert) caUniqueName() string {
  272. return "mkcert development CA " + m.caCert.SerialNumber.String()
  273. }