cert.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // Copyright 2018 The mkcert 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. "net/mail"
  21. "net/url"
  22. "os"
  23. "os/user"
  24. "path/filepath"
  25. "regexp"
  26. "strconv"
  27. "strings"
  28. "time"
  29. pkcs12 "software.sslmate.com/src/go-pkcs12"
  30. )
  31. var (
  32. organizeName string
  33. organizeUnit string
  34. )
  35. var userAndHostname string
  36. func init() {
  37. if os.Getenv("ORGANIZE_NAME") != "" {
  38. organizeName = os.Getenv("ORGANIZE_NAME")
  39. } else {
  40. organizeName = "Indigenous CA"
  41. }
  42. if os.Getenv("ORGANIZE_UNIT") != "" {
  43. organizeUnit = os.Getenv("ORGANIZE_UNIT")
  44. } else {
  45. organizeUnit = "Indigenous certificate"
  46. }
  47. u, err := user.Current()
  48. if err == nil {
  49. userAndHostname = u.Username + "@"
  50. }
  51. if h, err := os.Hostname(); err == nil {
  52. userAndHostname += h
  53. }
  54. if err == nil && u.Name != "" && u.Name != u.Username {
  55. userAndHostname += " (" + u.Name + ")"
  56. }
  57. }
  58. func (m *mkcert) makeCert(hosts []string) {
  59. if m.caKey == nil {
  60. log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
  61. }
  62. priv, err := m.generateKey(false)
  63. fatalIfErr(err, "failed to generate certificate key")
  64. pub := priv.(crypto.Signer).Public()
  65. // Certificates last for 2 years and 3 months, which is always less than
  66. // 825 days, the limit that macOS/iOS apply to all certificates,
  67. // including custom roots. See https://support.apple.com/en-us/HT210176.
  68. expiration := time.Now().AddDate(2, 3, 0)
  69. tpl := &x509.Certificate{
  70. SerialNumber: randomSerialNumber(),
  71. Subject: pkix.Name{
  72. Organization: []string{organizeName},
  73. OrganizationalUnit: []string{organizeUnit},
  74. },
  75. NotBefore: time.Now(), NotAfter: expiration,
  76. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  77. }
  78. for _, h := range hosts {
  79. if ip := net.ParseIP(h); ip != nil {
  80. tpl.IPAddresses = append(tpl.IPAddresses, ip)
  81. } else if email, err := mail.ParseAddress(h); err == nil && email.Address == h {
  82. tpl.EmailAddresses = append(tpl.EmailAddresses, h)
  83. } else if uriName, err := url.Parse(h); err == nil && uriName.Scheme != "" && uriName.Host != "" {
  84. tpl.URIs = append(tpl.URIs, uriName)
  85. } else {
  86. tpl.DNSNames = append(tpl.DNSNames, h)
  87. }
  88. }
  89. if m.client {
  90. tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
  91. }
  92. if len(tpl.IPAddresses) > 0 || len(tpl.DNSNames) > 0 || len(tpl.URIs) > 0 {
  93. tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
  94. }
  95. if len(tpl.EmailAddresses) > 0 {
  96. tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
  97. }
  98. // IIS (the main target of PKCS #12 files), only shows the deprecated
  99. // Common Name in the UI. See issue #115.
  100. if m.pkcs12 {
  101. tpl.Subject.CommonName = hosts[0]
  102. }
  103. cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, pub, m.caKey)
  104. fatalIfErr(err, "failed to generate certificate")
  105. certFile, keyFile, p12File := m.fileNames(hosts)
  106. if !m.pkcs12 {
  107. certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
  108. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  109. fatalIfErr(err, "failed to encode certificate key")
  110. privPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privDER})
  111. if certFile == keyFile {
  112. err = ioutil.WriteFile(keyFile, append(certPEM, privPEM...), 0600)
  113. fatalIfErr(err, "failed to save certificate and key")
  114. } else {
  115. err = ioutil.WriteFile(certFile, certPEM, 0644)
  116. fatalIfErr(err, "failed to save certificate")
  117. err = ioutil.WriteFile(keyFile, privPEM, 0600)
  118. fatalIfErr(err, "failed to save certificate key")
  119. }
  120. } else {
  121. domainCert, _ := x509.ParseCertificate(cert)
  122. pfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, "changeit")
  123. fatalIfErr(err, "failed to generate PKCS#12")
  124. err = ioutil.WriteFile(p12File, pfxData, 0644)
  125. fatalIfErr(err, "failed to save PKCS#12")
  126. }
  127. m.printHosts(hosts)
  128. if !m.pkcs12 {
  129. if certFile == keyFile {
  130. log.Printf("\nThe certificate and key are at \"%s\" ✅\n\n", certFile)
  131. } else {
  132. log.Printf("\nThe certificate is at \"%s\" and the key at \"%s\" ✅\n\n", certFile, keyFile)
  133. }
  134. } else {
  135. log.Printf("\nThe PKCS#12 bundle is at \"%s\" ✅\n", p12File)
  136. log.Printf("\nThe legacy PKCS#12 encryption password is the often hardcoded default \"changeit\" ℹ️\n\n")
  137. }
  138. log.Printf("It will expire on %s 🗓\n\n", expiration.Format("2 January 2006"))
  139. }
  140. func (m *mkcert) printHosts(hosts []string) {
  141. secondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\*\.[0-9a-z_-]+$`)
  142. log.Printf("\nCreated a new certificate valid for the following names 📜")
  143. for _, h := range hosts {
  144. log.Printf(" - %q", h)
  145. if secondLvlWildcardRegexp.MatchString(h) {
  146. log.Printf(" Warning: many browsers don't support second-level wildcards like %q ⚠️", h)
  147. }
  148. }
  149. for _, h := range hosts {
  150. if strings.HasPrefix(h, "*.") {
  151. log.Printf("\nReminder: X.509 wildcards only go one level deep, so this won't match a.b.%s ℹ️", h[2:])
  152. break
  153. }
  154. }
  155. }
  156. func (m *mkcert) generateKey(rootCA bool) (crypto.PrivateKey, error) {
  157. if m.ecdsa {
  158. return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  159. }
  160. if rootCA {
  161. return rsa.GenerateKey(rand.Reader, 3072)
  162. }
  163. return rsa.GenerateKey(rand.Reader, 2048)
  164. }
  165. func (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {
  166. defaultName := strings.Replace(hosts[0], ":", "_", -1)
  167. defaultName = strings.Replace(defaultName, "*", "_wildcard", -1)
  168. if len(hosts) > 1 {
  169. defaultName += "+" + strconv.Itoa(len(hosts)-1)
  170. }
  171. if m.client {
  172. defaultName += "-client"
  173. }
  174. certFile = "./" + defaultName + ".pem"
  175. if m.certFile != "" {
  176. certFile = m.certFile
  177. }
  178. keyFile = "./" + defaultName + "-key.pem"
  179. if m.keyFile != "" {
  180. keyFile = m.keyFile
  181. }
  182. p12File = "./" + defaultName + ".p12"
  183. if m.p12File != "" {
  184. p12File = m.p12File
  185. }
  186. return
  187. }
  188. func randomSerialNumber() *big.Int {
  189. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  190. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  191. fatalIfErr(err, "failed to generate serial number")
  192. return serialNumber
  193. }
  194. func (m *mkcert) makeCertFromCSR() {
  195. if m.caKey == nil {
  196. log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
  197. }
  198. csrPEMBytes, err := ioutil.ReadFile(m.csrPath)
  199. fatalIfErr(err, "failed to read the CSR")
  200. csrPEM, _ := pem.Decode(csrPEMBytes)
  201. if csrPEM == nil {
  202. log.Fatalln("ERROR: failed to read the CSR: unexpected content")
  203. }
  204. if csrPEM.Type != "CERTIFICATE REQUEST" &&
  205. csrPEM.Type != "NEW CERTIFICATE REQUEST" {
  206. log.Fatalln("ERROR: failed to read the CSR: expected CERTIFICATE REQUEST, got " + csrPEM.Type)
  207. }
  208. csr, err := x509.ParseCertificateRequest(csrPEM.Bytes)
  209. fatalIfErr(err, "failed to parse the CSR")
  210. fatalIfErr(csr.CheckSignature(), "invalid CSR signature")
  211. expiration := time.Now().AddDate(2, 3, 0)
  212. tpl := &x509.Certificate{
  213. SerialNumber: randomSerialNumber(),
  214. Subject: csr.Subject,
  215. ExtraExtensions: csr.Extensions, // includes requested SANs, KUs and EKUs
  216. NotBefore: time.Now(), NotAfter: expiration,
  217. // If the CSR does not request a SAN extension, fix it up for them as
  218. // the Common Name field does not work in modern browsers. Otherwise,
  219. // this will get overridden.
  220. DNSNames: []string{csr.Subject.CommonName},
  221. // Likewise, if the CSR does not set KUs and EKUs, fix it up as Apple
  222. // platforms require serverAuth for TLS.
  223. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  224. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  225. }
  226. if m.client {
  227. tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
  228. }
  229. if len(csr.EmailAddresses) > 0 {
  230. tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
  231. }
  232. cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, csr.PublicKey, m.caKey)
  233. fatalIfErr(err, "failed to generate certificate")
  234. c, err := x509.ParseCertificate(cert)
  235. fatalIfErr(err, "failed to parse generated certificate")
  236. var hosts []string
  237. hosts = append(hosts, c.DNSNames...)
  238. hosts = append(hosts, c.EmailAddresses...)
  239. for _, ip := range c.IPAddresses {
  240. hosts = append(hosts, ip.String())
  241. }
  242. for _, uri := range c.URIs {
  243. hosts = append(hosts, uri.String())
  244. }
  245. certFile, _, _ := m.fileNames(hosts)
  246. err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
  247. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  248. fatalIfErr(err, "failed to save certificate")
  249. m.printHosts(hosts)
  250. log.Printf("\nThe certificate is at \"%s\" ✅\n\n", certFile)
  251. log.Printf("It will expire on %s 🗓\n\n", expiration.Format("2 January 2006"))
  252. }
  253. // loadCA will load or create the CA at CAROOT.
  254. func (m *mkcert) loadCA() {
  255. if !pathExists(filepath.Join(m.CAROOT, rootName)) {
  256. m.newCA()
  257. }
  258. certPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))
  259. fatalIfErr(err, "failed to read the CA certificate")
  260. certDERBlock, _ := pem.Decode(certPEMBlock)
  261. if certDERBlock == nil || certDERBlock.Type != "CERTIFICATE" {
  262. log.Fatalln("ERROR: failed to read the CA certificate: unexpected content")
  263. }
  264. m.caCert, err = x509.ParseCertificate(certDERBlock.Bytes)
  265. fatalIfErr(err, "failed to parse the CA certificate")
  266. if !pathExists(filepath.Join(m.CAROOT, rootKeyName)) {
  267. return // keyless mode, where only -install works
  268. }
  269. keyPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootKeyName))
  270. fatalIfErr(err, "failed to read the CA key")
  271. keyDERBlock, _ := pem.Decode(keyPEMBlock)
  272. if keyDERBlock == nil || keyDERBlock.Type != "PRIVATE KEY" {
  273. log.Fatalln("ERROR: failed to read the CA key: unexpected content")
  274. }
  275. m.caKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes)
  276. fatalIfErr(err, "failed to parse the CA key")
  277. }
  278. func (m *mkcert) newCA() {
  279. priv, err := m.generateKey(true)
  280. fatalIfErr(err, "failed to generate the CA key")
  281. pub := priv.(crypto.Signer).Public()
  282. spkiASN1, err := x509.MarshalPKIXPublicKey(pub)
  283. fatalIfErr(err, "failed to encode public key")
  284. var spki struct {
  285. Algorithm pkix.AlgorithmIdentifier
  286. SubjectPublicKey asn1.BitString
  287. }
  288. _, err = asn1.Unmarshal(spkiASN1, &spki)
  289. fatalIfErr(err, "failed to decode public key")
  290. skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
  291. tpl := &x509.Certificate{
  292. SerialNumber: randomSerialNumber(),
  293. Subject: pkix.Name{
  294. Organization: []string{organizeName},
  295. OrganizationalUnit: []string{userAndHostname},
  296. // The CommonName is required by iOS to show the certificate in the
  297. // "Certificate Trust Settings" menu.
  298. // https://github.com/FiloSottile/mkcert/issues/47
  299. CommonName: organizeName,
  300. },
  301. SubjectKeyId: skid[:],
  302. NotAfter: time.Now().AddDate(50, 0, 0),
  303. NotBefore: time.Now(),
  304. KeyUsage: x509.KeyUsageCertSign,
  305. BasicConstraintsValid: true,
  306. IsCA: true,
  307. MaxPathLenZero: true,
  308. }
  309. cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, pub, priv)
  310. fatalIfErr(err, "failed to generate CA certificate")
  311. privDER, err := x509.MarshalPKCS8PrivateKey(priv)
  312. fatalIfErr(err, "failed to encode CA key")
  313. err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootKeyName), pem.EncodeToMemory(
  314. &pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0400)
  315. fatalIfErr(err, "failed to save CA key")
  316. err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootName), pem.EncodeToMemory(
  317. &pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
  318. fatalIfErr(err, "failed to save CA certificate")
  319. log.Printf("Created a new local CA 💥\n")
  320. }
  321. func (m *mkcert) caUniqueName() string {
  322. return organizeName + m.caCert.SerialNumber.String()
  323. }