[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug report 🐞\nabout: Did mkcert not work as intended? Is it broken in a certain environment?\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n## Environment\n\n* Operating system (including version):\n* mkcert version (from `mkcert -version`):\n* Server (where the certificate is loaded):\n* Client (e.g. browser, CLI tool, or script):\n\n## What you did\n\n<!-- Including the `mkcert -install` step and how the certificate was generated and installed. -->\n\n## What went wrong\n\n<!-- Please include the precise error, like a terminal transcript or a browser screenshot. -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Question 🙋\n    url: https://github.com/FiloSottile/mkcert/discussions/new?category=q-a\n    about: Have a question about how to use mkcert?\n  - name: Feature request or suggestion\n    url: https://github.com/FiloSottile/mkcert/discussions/new?category=ideas\n    about: Wish mkcert had a feature it doesn't currently have?\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "on:\n  release:\n    types: [published]\nname: Upload Release Asset\njobs:\n  release:\n    name: Upload Release Asset\n    runs-on: ubuntu-latest\n    steps:\n      - name: Install Go\n        uses: actions/setup-go@v2\n        with:\n          go-version: 1.x\n      - name: Checkout repository\n        uses: actions/checkout@v2\n      - name: Build binaries\n        run: |\n          CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o \"mkcert-$(git describe --tags)-linux-amd64\" -ldflags \"-X main.Version=$(git describe --tags)\"\n          CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o \"mkcert-$(git describe --tags)-linux-arm\" -ldflags \"-X main.Version=$(git describe --tags)\"\n          CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o \"mkcert-$(git describe --tags)-linux-arm64\" -ldflags \"-X main.Version=$(git describe --tags)\"\n          CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o \"mkcert-$(git describe --tags)-darwin-amd64\" -ldflags \"-X main.Version=$(git describe --tags)\"\n          CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o \"mkcert-$(git describe --tags)-darwin-arm64\" -ldflags \"-X main.Version=$(git describe --tags)\"\n          CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o \"mkcert-$(git describe --tags)-windows-amd64.exe\" -ldflags \"-X main.Version=$(git describe --tags)\"\n          CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o \"mkcert-$(git describe --tags)-windows-arm64.exe\" -ldflags \"-X main.Version=$(git describe --tags)\"\n      - name: Upload release artifacts\n        uses: actions/github-script@v3\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            const fs = require(\"fs\").promises;\n            const { repo: { owner, repo }, sha } = context;\n\n            const release = await github.repos.getReleaseByTag({\n              owner, repo,\n              tag: process.env.GITHUB_REF.replace(\"refs/tags/\", \"\"),\n            });\n            console.log(\"Release:\", { release });\n\n            for (let file of await fs.readdir(\".\")) {\n              if (!file.startsWith(\"mkcert-\")) continue;\n              console.log(\"Uploading\", file);\n              await github.repos.uploadReleaseAsset({\n                owner, repo,\n                release_id: release.data.id,\n                name: file,\n                data: await fs.readFile(file),\n              });\n            }\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "on: [push, pull_request]\nname: Test\njobs:\n  test:\n    name: Go tests\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Install Go ${{ matrix.go }}\n        uses: actions/setup-go@v2\n        with:\n          go-version: 1.x\n      - name: Checkout repository\n        uses: actions/checkout@v2\n      - name: Run staticcheck\n        run: |\n          go install honnef.co/go/tools/cmd/staticcheck@latest\n          staticcheck ./...\n      - name: Run tests\n        run: go test -race ./...\n"
  },
  {
    "path": "AUTHORS",
    "content": "# This is the list of mkcert authors for copyright purposes.\n#\n# This does not necessarily list everyone who has contributed code, since in\n# some cases, their employer may be the copyright holder.  To see the full list\n# of contributors, see the revision history in source control.\n\nGoogle LLC\nAdam Shannon\nChad Retz\nTravis Campbell\nCarl Henrik Lunde\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2018 The mkcert Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# mkcert\n\nmkcert is a simple tool for making locally-trusted development certificates. It requires no configuration.\n\n```\n$ mkcert -install\nCreated a new local CA 💥\nThe local CA is now installed in the system trust store! ⚡️\nThe local CA is now installed in the Firefox trust store (requires browser restart)! 🦊\n\n$ mkcert example.com \"*.example.com\" example.test localhost 127.0.0.1 ::1\n\nCreated a new certificate valid for the following names 📜\n - \"example.com\"\n - \"*.example.com\"\n - \"example.test\"\n - \"localhost\"\n - \"127.0.0.1\"\n - \"::1\"\n\nThe certificate is at \"./example.com+5.pem\" and the key at \"./example.com+5-key.pem\" ✅\n```\n\n<p align=\"center\"><img width=\"498\" alt=\"Chrome and Firefox screenshot\" src=\"https://user-images.githubusercontent.com/1225294/51066373-96d4aa80-15be-11e9-91e2-f4e44a3a4458.png\"></p>\n\nUsing certificates from real certificate authorities (CAs) for development can be dangerous or impossible (for hosts like `example.test`, `localhost` or `127.0.0.1`), but self-signed certificates cause trust errors. Managing your own CA is the best solution, but usually involves arcane commands, specialized knowledge and manual steps.\n\nmkcert automatically creates and installs a local CA in the system root store, and generates locally-trusted certificates. mkcert does not automatically configure servers to use the certificates, though, that's up to you.\n\n## Installation\n\n> **Warning**: the `rootCA-key.pem` file that mkcert automatically generates gives complete power to intercept secure requests from your machine. Do not share it.\n\n### macOS\n\nOn macOS, use [Homebrew](https://brew.sh/)\n\n```\nbrew install mkcert\nbrew install nss # if you use Firefox\n```\n\nor [MacPorts](https://www.macports.org/).\n\n```\nsudo port selfupdate\nsudo port install mkcert\nsudo port install nss # if you use Firefox\n```\n\n### Linux\n\nOn Linux, first install `certutil`.\n\n```\nsudo apt install libnss3-tools\n    -or-\nsudo yum install nss-tools\n    -or-\nsudo pacman -S nss\n    -or-\nsudo zypper install mozilla-nss-tools\n```\n\nThen you can install using [Homebrew on Linux](https://docs.brew.sh/Homebrew-on-Linux)\n\n```\nbrew install mkcert\n```\n\nor build from source (requires Go 1.13+)\n\n```\ngit clone https://github.com/FiloSottile/mkcert && cd mkcert\ngo build -ldflags \"-X main.Version=$(git describe --tags)\"\n```\n\nor use [the pre-built binaries](https://github.com/FiloSottile/mkcert/releases).\n\n```\ncurl -JLO \"https://dl.filippo.io/mkcert/latest?for=linux/amd64\"\nchmod +x mkcert-v*-linux-amd64\nsudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert\n```\n\nFor Arch Linux users, [`mkcert`](https://archlinux.org/packages/extra/x86_64/mkcert/) is available on the official Arch Linux repository.\n\n```\nsudo pacman -Syu mkcert\n```\n\n### Windows\n\nOn Windows, use [Chocolatey](https://chocolatey.org)\n\n```\nchoco install mkcert\n```\n\nor use Scoop\n\n```\nscoop bucket add extras\nscoop install mkcert\n```\n\nor build from source (requires Go 1.10+), or use [the pre-built binaries](https://github.com/FiloSottile/mkcert/releases).\n\nIf you're running into permission problems try running `mkcert` as an Administrator.\n\n## Supported root stores\n\nmkcert supports the following root stores:\n\n* macOS system store\n* Windows system store\n* Linux variants that provide either\n    * `update-ca-trust` (Fedora, RHEL, CentOS) or\n    * `update-ca-certificates` (Ubuntu, Debian, OpenSUSE, SLES) or\n    * `trust` (Arch)\n* Firefox (macOS and Linux only)\n* Chrome and Chromium\n* Java (when `JAVA_HOME` is set)\n\nTo only install the local root CA into a subset of them, you can set the `TRUST_STORES` environment variable to a comma-separated list. Options are: \"system\", \"java\" and \"nss\" (includes Firefox).\n\n## Advanced topics\n\n### Advanced options\n\n```\n\t-cert-file FILE, -key-file FILE, -p12-file FILE\n\t    Customize the output paths.\n\n\t-client\n\t    Generate a certificate for client authentication.\n\n\t-ecdsa\n\t    Generate a certificate with an ECDSA key.\n\n\t-pkcs12\n\t    Generate a \".p12\" PKCS #12 file, also know as a \".pfx\" file,\n\t    containing certificate and key for legacy applications.\n\n\t-csr CSR\n\t    Generate a certificate based on the supplied CSR. Conflicts with\n\t    all other flags and arguments except -install and -cert-file.\n```\n\n> **Note:** You _must_ place these options before the domain names list.\n\n#### Example\n\n```\nmkcert -key-file key.pem -cert-file cert.pem example.com *.example.com\n```\n\n### S/MIME\n\nmkcert automatically generates an S/MIME certificate if one of the supplied names is an email address.\n\n```\nmkcert filippo@example.com\n```\n\n### Mobile devices\n\nFor the certificates to be trusted on mobile devices, you will have to install the root CA. It's the `rootCA.pem` file in the folder printed by `mkcert -CAROOT`.\n\nOn iOS, you can either use AirDrop, email the CA to yourself, or serve it from an HTTP server. After opening it, you need to [install the profile in Settings > Profile Downloaded](https://github.com/FiloSottile/mkcert/issues/233#issuecomment-690110809) and then [enable full trust in it](https://support.apple.com/en-nz/HT204477).\n\nFor Android, you will have to install the CA and then enable user roots in the development build of your app. See [this StackOverflow answer](https://stackoverflow.com/a/22040887/749014).\n\n### Using the root with Node.js\n\nNode does not use the system root store, so it won't accept mkcert certificates automatically. Instead, you will have to set the [`NODE_EXTRA_CA_CERTS`](https://nodejs.org/api/cli.html#cli_node_extra_ca_certs_file) environment variable.\n\n```\nexport NODE_EXTRA_CA_CERTS=\"$(mkcert -CAROOT)/rootCA.pem\"\n```\n\n### Changing the location of the CA files\n\nThe CA certificate and its key are stored in an application data folder in the user home. You usually don't have to worry about it, as installation is automated, but the location is printed by `mkcert -CAROOT`.\n\nIf you want to manage separate CAs, you can use the environment variable `$CAROOT` to set the folder where mkcert will place and look for the local CA files.\n\n### Installing the CA on other systems\n\nInstalling in the trust store does not require the CA key, so you can export the CA certificate and use mkcert to install it in other machines.\n\n* Look for the `rootCA.pem` file in `mkcert -CAROOT`\n* copy it to a different machine\n* set `$CAROOT` to its directory\n* run `mkcert -install`\n\nRemember that mkcert is meant for development purposes, not production, so it should not be used on end users' machines, and that you should *not* export or share `rootCA-key.pem`.\n"
  },
  {
    "path": "cert.go",
    "content": "// Copyright 2018 The mkcert Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/elliptic\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/sha1\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/asn1\"\n\t\"encoding/pem\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"math/big\"\n\t\"net\"\n\t\"net/mail\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\tpkcs12 \"software.sslmate.com/src/go-pkcs12\"\n)\n\nvar userAndHostname string\n\nfunc init() {\n\tu, err := user.Current()\n\tif err == nil {\n\t\tuserAndHostname = u.Username + \"@\"\n\t}\n\tif h, err := os.Hostname(); err == nil {\n\t\tuserAndHostname += h\n\t}\n\tif err == nil && u.Name != \"\" && u.Name != u.Username {\n\t\tuserAndHostname += \" (\" + u.Name + \")\"\n\t}\n}\n\nfunc (m *mkcert) makeCert(hosts []string) {\n\tif m.caKey == nil {\n\t\tlog.Fatalln(\"ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing\")\n\t}\n\n\tpriv, err := m.generateKey(false)\n\tfatalIfErr(err, \"failed to generate certificate key\")\n\tpub := priv.(crypto.Signer).Public()\n\n\t// Certificates last for 2 years and 3 months, which is always less than\n\t// 825 days, the limit that macOS/iOS apply to all certificates,\n\t// including custom roots. See https://support.apple.com/en-us/HT210176.\n\texpiration := time.Now().AddDate(2, 3, 0)\n\n\ttpl := &x509.Certificate{\n\t\tSerialNumber: randomSerialNumber(),\n\t\tSubject: pkix.Name{\n\t\t\tOrganization:       []string{\"mkcert development certificate\"},\n\t\t\tOrganizationalUnit: []string{userAndHostname},\n\t\t},\n\n\t\tNotBefore: time.Now(), NotAfter: expiration,\n\n\t\tKeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,\n\t}\n\n\tfor _, h := range hosts {\n\t\tif ip := net.ParseIP(h); ip != nil {\n\t\t\ttpl.IPAddresses = append(tpl.IPAddresses, ip)\n\t\t} else if email, err := mail.ParseAddress(h); err == nil && email.Address == h {\n\t\t\ttpl.EmailAddresses = append(tpl.EmailAddresses, h)\n\t\t} else if uriName, err := url.Parse(h); err == nil && uriName.Scheme != \"\" && uriName.Host != \"\" {\n\t\t\ttpl.URIs = append(tpl.URIs, uriName)\n\t\t} else {\n\t\t\ttpl.DNSNames = append(tpl.DNSNames, h)\n\t\t}\n\t}\n\n\tif m.client {\n\t\ttpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)\n\t}\n\tif len(tpl.IPAddresses) > 0 || len(tpl.DNSNames) > 0 || len(tpl.URIs) > 0 {\n\t\ttpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageServerAuth)\n\t}\n\tif len(tpl.EmailAddresses) > 0 {\n\t\ttpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)\n\t}\n\n\t// IIS (the main target of PKCS #12 files), only shows the deprecated\n\t// Common Name in the UI. See issue #115.\n\tif m.pkcs12 {\n\t\ttpl.Subject.CommonName = hosts[0]\n\t}\n\n\tcert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, pub, m.caKey)\n\tfatalIfErr(err, \"failed to generate certificate\")\n\n\tcertFile, keyFile, p12File := m.fileNames(hosts)\n\n\tif !m.pkcs12 {\n\t\tcertPEM := pem.EncodeToMemory(&pem.Block{Type: \"CERTIFICATE\", Bytes: cert})\n\t\tprivDER, err := x509.MarshalPKCS8PrivateKey(priv)\n\t\tfatalIfErr(err, \"failed to encode certificate key\")\n\t\tprivPEM := pem.EncodeToMemory(&pem.Block{Type: \"PRIVATE KEY\", Bytes: privDER})\n\n\t\tif certFile == keyFile {\n\t\t\terr = ioutil.WriteFile(keyFile, append(certPEM, privPEM...), 0600)\n\t\t\tfatalIfErr(err, \"failed to save certificate and key\")\n\t\t} else {\n\t\t\terr = ioutil.WriteFile(certFile, certPEM, 0644)\n\t\t\tfatalIfErr(err, \"failed to save certificate\")\n\t\t\terr = ioutil.WriteFile(keyFile, privPEM, 0600)\n\t\t\tfatalIfErr(err, \"failed to save certificate key\")\n\t\t}\n\t} else {\n\t\tdomainCert, _ := x509.ParseCertificate(cert)\n\t\tpfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, \"changeit\")\n\t\tfatalIfErr(err, \"failed to generate PKCS#12\")\n\t\terr = ioutil.WriteFile(p12File, pfxData, 0644)\n\t\tfatalIfErr(err, \"failed to save PKCS#12\")\n\t}\n\n\tm.printHosts(hosts)\n\n\tif !m.pkcs12 {\n\t\tif certFile == keyFile {\n\t\t\tlog.Printf(\"\\nThe certificate and key are at \\\"%s\\\" ✅\\n\\n\", certFile)\n\t\t} else {\n\t\t\tlog.Printf(\"\\nThe certificate is at \\\"%s\\\" and the key at \\\"%s\\\" ✅\\n\\n\", certFile, keyFile)\n\t\t}\n\t} else {\n\t\tlog.Printf(\"\\nThe PKCS#12 bundle is at \\\"%s\\\" ✅\\n\", p12File)\n\t\tlog.Printf(\"\\nThe legacy PKCS#12 encryption password is the often hardcoded default \\\"changeit\\\" ℹ️\\n\\n\")\n\t}\n\n\tlog.Printf(\"It will expire on %s 🗓\\n\\n\", expiration.Format(\"2 January 2006\"))\n}\n\nfunc (m *mkcert) printHosts(hosts []string) {\n\tsecondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\\*\\.[0-9a-z_-]+$`)\n\tlog.Printf(\"\\nCreated a new certificate valid for the following names 📜\")\n\tfor _, h := range hosts {\n\t\tlog.Printf(\" - %q\", h)\n\t\tif secondLvlWildcardRegexp.MatchString(h) {\n\t\t\tlog.Printf(\"   Warning: many browsers don't support second-level wildcards like %q ⚠️\", h)\n\t\t}\n\t}\n\n\tfor _, h := range hosts {\n\t\tif strings.HasPrefix(h, \"*.\") {\n\t\t\tlog.Printf(\"\\nReminder: X.509 wildcards only go one level deep, so this won't match a.b.%s ℹ️\", h[2:])\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc (m *mkcert) generateKey(rootCA bool) (crypto.PrivateKey, error) {\n\tif m.ecdsa {\n\t\treturn ecdsa.GenerateKey(elliptic.P256(), rand.Reader)\n\t}\n\tif rootCA {\n\t\treturn rsa.GenerateKey(rand.Reader, 3072)\n\t}\n\treturn rsa.GenerateKey(rand.Reader, 2048)\n}\n\nfunc (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {\n\tdefaultName := strings.Replace(hosts[0], \":\", \"_\", -1)\n\tdefaultName = strings.Replace(defaultName, \"*\", \"_wildcard\", -1)\n\tif len(hosts) > 1 {\n\t\tdefaultName += \"+\" + strconv.Itoa(len(hosts)-1)\n\t}\n\tif m.client {\n\t\tdefaultName += \"-client\"\n\t}\n\n\tcertFile = \"./\" + defaultName + \".pem\"\n\tif m.certFile != \"\" {\n\t\tcertFile = m.certFile\n\t}\n\tkeyFile = \"./\" + defaultName + \"-key.pem\"\n\tif m.keyFile != \"\" {\n\t\tkeyFile = m.keyFile\n\t}\n\tp12File = \"./\" + defaultName + \".p12\"\n\tif m.p12File != \"\" {\n\t\tp12File = m.p12File\n\t}\n\n\treturn\n}\n\nfunc randomSerialNumber() *big.Int {\n\tserialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)\n\tserialNumber, err := rand.Int(rand.Reader, serialNumberLimit)\n\tfatalIfErr(err, \"failed to generate serial number\")\n\treturn serialNumber\n}\n\nfunc (m *mkcert) makeCertFromCSR() {\n\tif m.caKey == nil {\n\t\tlog.Fatalln(\"ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing\")\n\t}\n\n\tcsrPEMBytes, err := ioutil.ReadFile(m.csrPath)\n\tfatalIfErr(err, \"failed to read the CSR\")\n\tcsrPEM, _ := pem.Decode(csrPEMBytes)\n\tif csrPEM == nil {\n\t\tlog.Fatalln(\"ERROR: failed to read the CSR: unexpected content\")\n\t}\n\tif csrPEM.Type != \"CERTIFICATE REQUEST\" &&\n\t\tcsrPEM.Type != \"NEW CERTIFICATE REQUEST\" {\n\t\tlog.Fatalln(\"ERROR: failed to read the CSR: expected CERTIFICATE REQUEST, got \" + csrPEM.Type)\n\t}\n\tcsr, err := x509.ParseCertificateRequest(csrPEM.Bytes)\n\tfatalIfErr(err, \"failed to parse the CSR\")\n\tfatalIfErr(csr.CheckSignature(), \"invalid CSR signature\")\n\n\texpiration := time.Now().AddDate(2, 3, 0)\n\ttpl := &x509.Certificate{\n\t\tSerialNumber:    randomSerialNumber(),\n\t\tSubject:         csr.Subject,\n\t\tExtraExtensions: csr.Extensions, // includes requested SANs, KUs and EKUs\n\n\t\tNotBefore: time.Now(), NotAfter: expiration,\n\n\t\t// If the CSR does not request a SAN extension, fix it up for them as\n\t\t// the Common Name field does not work in modern browsers. Otherwise,\n\t\t// this will get overridden.\n\t\tDNSNames: []string{csr.Subject.CommonName},\n\n\t\t// Likewise, if the CSR does not set KUs and EKUs, fix it up as Apple\n\t\t// platforms require serverAuth for TLS.\n\t\tKeyUsage:    x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,\n\t\tExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},\n\t}\n\n\tif m.client {\n\t\ttpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)\n\t}\n\tif len(csr.EmailAddresses) > 0 {\n\t\ttpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)\n\t}\n\n\tcert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, csr.PublicKey, m.caKey)\n\tfatalIfErr(err, \"failed to generate certificate\")\n\tc, err := x509.ParseCertificate(cert)\n\tfatalIfErr(err, \"failed to parse generated certificate\")\n\n\tvar hosts []string\n\thosts = append(hosts, c.DNSNames...)\n\thosts = append(hosts, c.EmailAddresses...)\n\tfor _, ip := range c.IPAddresses {\n\t\thosts = append(hosts, ip.String())\n\t}\n\tfor _, uri := range c.URIs {\n\t\thosts = append(hosts, uri.String())\n\t}\n\tcertFile, _, _ := m.fileNames(hosts)\n\n\terr = ioutil.WriteFile(certFile, pem.EncodeToMemory(\n\t\t&pem.Block{Type: \"CERTIFICATE\", Bytes: cert}), 0644)\n\tfatalIfErr(err, \"failed to save certificate\")\n\n\tm.printHosts(hosts)\n\n\tlog.Printf(\"\\nThe certificate is at \\\"%s\\\" ✅\\n\\n\", certFile)\n\n\tlog.Printf(\"It will expire on %s 🗓\\n\\n\", expiration.Format(\"2 January 2006\"))\n}\n\n// loadCA will load or create the CA at CAROOT.\nfunc (m *mkcert) loadCA() {\n\tif !pathExists(filepath.Join(m.CAROOT, rootName)) {\n\t\tm.newCA()\n\t}\n\n\tcertPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))\n\tfatalIfErr(err, \"failed to read the CA certificate\")\n\tcertDERBlock, _ := pem.Decode(certPEMBlock)\n\tif certDERBlock == nil || certDERBlock.Type != \"CERTIFICATE\" {\n\t\tlog.Fatalln(\"ERROR: failed to read the CA certificate: unexpected content\")\n\t}\n\tm.caCert, err = x509.ParseCertificate(certDERBlock.Bytes)\n\tfatalIfErr(err, \"failed to parse the CA certificate\")\n\n\tif !pathExists(filepath.Join(m.CAROOT, rootKeyName)) {\n\t\treturn // keyless mode, where only -install works\n\t}\n\n\tkeyPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootKeyName))\n\tfatalIfErr(err, \"failed to read the CA key\")\n\tkeyDERBlock, _ := pem.Decode(keyPEMBlock)\n\tif keyDERBlock == nil || keyDERBlock.Type != \"PRIVATE KEY\" {\n\t\tlog.Fatalln(\"ERROR: failed to read the CA key: unexpected content\")\n\t}\n\tm.caKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes)\n\tfatalIfErr(err, \"failed to parse the CA key\")\n}\n\nfunc (m *mkcert) newCA() {\n\tpriv, err := m.generateKey(true)\n\tfatalIfErr(err, \"failed to generate the CA key\")\n\tpub := priv.(crypto.Signer).Public()\n\n\tspkiASN1, err := x509.MarshalPKIXPublicKey(pub)\n\tfatalIfErr(err, \"failed to encode public key\")\n\n\tvar spki struct {\n\t\tAlgorithm        pkix.AlgorithmIdentifier\n\t\tSubjectPublicKey asn1.BitString\n\t}\n\t_, err = asn1.Unmarshal(spkiASN1, &spki)\n\tfatalIfErr(err, \"failed to decode public key\")\n\n\tskid := sha1.Sum(spki.SubjectPublicKey.Bytes)\n\n\ttpl := &x509.Certificate{\n\t\tSerialNumber: randomSerialNumber(),\n\t\tSubject: pkix.Name{\n\t\t\tOrganization:       []string{\"mkcert development CA\"},\n\t\t\tOrganizationalUnit: []string{userAndHostname},\n\n\t\t\t// The CommonName is required by iOS to show the certificate in the\n\t\t\t// \"Certificate Trust Settings\" menu.\n\t\t\t// https://github.com/FiloSottile/mkcert/issues/47\n\t\t\tCommonName: \"mkcert \" + userAndHostname,\n\t\t},\n\t\tSubjectKeyId: skid[:],\n\n\t\tNotAfter:  time.Now().AddDate(10, 0, 0),\n\t\tNotBefore: time.Now(),\n\n\t\tKeyUsage: x509.KeyUsageCertSign,\n\n\t\tBasicConstraintsValid: true,\n\t\tIsCA:                  true,\n\t\tMaxPathLenZero:        true,\n\t}\n\n\tcert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, pub, priv)\n\tfatalIfErr(err, \"failed to generate CA certificate\")\n\n\tprivDER, err := x509.MarshalPKCS8PrivateKey(priv)\n\tfatalIfErr(err, \"failed to encode CA key\")\n\terr = ioutil.WriteFile(filepath.Join(m.CAROOT, rootKeyName), pem.EncodeToMemory(\n\t\t&pem.Block{Type: \"PRIVATE KEY\", Bytes: privDER}), 0400)\n\tfatalIfErr(err, \"failed to save CA key\")\n\n\terr = ioutil.WriteFile(filepath.Join(m.CAROOT, rootName), pem.EncodeToMemory(\n\t\t&pem.Block{Type: \"CERTIFICATE\", Bytes: cert}), 0644)\n\tfatalIfErr(err, \"failed to save CA certificate\")\n\n\tlog.Printf(\"Created a new local CA 💥\\n\")\n}\n\nfunc (m *mkcert) caUniqueName() string {\n\treturn \"mkcert development CA \" + m.caCert.SerialNumber.String()\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module filippo.io/mkcert\n\ngo 1.18\n\nrequire (\n\tgolang.org/x/net v0.0.0-20220421235706-1d1ef9303861\n\thowett.net/plist v1.0.0\n\tsoftware.sslmate.com/src/go-pkcs12 v0.2.0\n)\n\nrequire (\n\tgolang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngolang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=\ngolang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8=\ngolang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=\nhowett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=\nhowett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=\nsoftware.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=\nsoftware.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=\n"
  },
  {
    "path": "main.go",
    "content": "// Copyright 2018 The mkcert Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Command mkcert is a simple zero-config tool to make development certificates.\npackage main\n\nimport (\n\t\"crypto\"\n\t\"crypto/x509\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"net/mail\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"golang.org/x/net/idna\"\n)\n\nconst shortUsage = `Usage of mkcert:\n\n\t$ mkcert -install\n\tInstall the local CA in the system trust store.\n\n\t$ mkcert example.org\n\tGenerate \"example.org.pem\" and \"example.org-key.pem\".\n\n\t$ mkcert example.com myapp.dev localhost 127.0.0.1 ::1\n\tGenerate \"example.com+4.pem\" and \"example.com+4-key.pem\".\n\n\t$ mkcert \"*.example.it\"\n\tGenerate \"_wildcard.example.it.pem\" and \"_wildcard.example.it-key.pem\".\n\n\t$ mkcert -uninstall\n\tUninstall the local CA (but do not delete it).\n\n`\n\nconst advancedUsage = `Advanced options:\n\n\t-cert-file FILE, -key-file FILE, -p12-file FILE\n\t    Customize the output paths.\n\n\t-client\n\t    Generate a certificate for client authentication.\n\n\t-ecdsa\n\t    Generate a certificate with an ECDSA key.\n\n\t-pkcs12\n\t    Generate a \".p12\" PKCS #12 file, also know as a \".pfx\" file,\n\t    containing certificate and key for legacy applications.\n\n\t-csr CSR\n\t    Generate a certificate based on the supplied CSR. Conflicts with\n\t    all other flags and arguments except -install and -cert-file.\n\n\t-CAROOT\n\t    Print the CA certificate and key storage location.\n\n\t$CAROOT (environment variable)\n\t    Set the CA certificate and key storage location. (This allows\n\t    maintaining multiple local CAs in parallel.)\n\n\t$TRUST_STORES (environment variable)\n\t    A comma-separated list of trust stores to install the local\n\t    root CA into. Options are: \"system\", \"java\" and \"nss\" (includes\n\t    Firefox). Autodetected by default.\n\n`\n\n// Version can be set at link time to override debug.BuildInfo.Main.Version,\n// which is \"(devel)\" when building from within the module. See\n// golang.org/issue/29814 and golang.org/issue/29228.\nvar Version string\n\nfunc main() {\n\tif len(os.Args) == 1 {\n\t\tfmt.Print(shortUsage)\n\t\treturn\n\t}\n\tlog.SetFlags(0)\n\tvar (\n\t\tinstallFlag   = flag.Bool(\"install\", false, \"\")\n\t\tuninstallFlag = flag.Bool(\"uninstall\", false, \"\")\n\t\tpkcs12Flag    = flag.Bool(\"pkcs12\", false, \"\")\n\t\tecdsaFlag     = flag.Bool(\"ecdsa\", false, \"\")\n\t\tclientFlag    = flag.Bool(\"client\", false, \"\")\n\t\thelpFlag      = flag.Bool(\"help\", false, \"\")\n\t\tcarootFlag    = flag.Bool(\"CAROOT\", false, \"\")\n\t\tcsrFlag       = flag.String(\"csr\", \"\", \"\")\n\t\tcertFileFlag  = flag.String(\"cert-file\", \"\", \"\")\n\t\tkeyFileFlag   = flag.String(\"key-file\", \"\", \"\")\n\t\tp12FileFlag   = flag.String(\"p12-file\", \"\", \"\")\n\t\tversionFlag   = flag.Bool(\"version\", false, \"\")\n\t)\n\tflag.Usage = func() {\n\t\tfmt.Fprint(flag.CommandLine.Output(), shortUsage)\n\t\tfmt.Fprintln(flag.CommandLine.Output(), `For more options, run \"mkcert -help\".`)\n\t}\n\tflag.Parse()\n\tif *helpFlag {\n\t\tfmt.Print(shortUsage)\n\t\tfmt.Print(advancedUsage)\n\t\treturn\n\t}\n\tif *versionFlag {\n\t\tif Version != \"\" {\n\t\t\tfmt.Println(Version)\n\t\t\treturn\n\t\t}\n\t\tif buildInfo, ok := debug.ReadBuildInfo(); ok {\n\t\t\tfmt.Println(buildInfo.Main.Version)\n\t\t\treturn\n\t\t}\n\t\tfmt.Println(\"(unknown)\")\n\t\treturn\n\t}\n\tif *carootFlag {\n\t\tif *installFlag || *uninstallFlag {\n\t\t\tlog.Fatalln(\"ERROR: you can't set -[un]install and -CAROOT at the same time\")\n\t\t}\n\t\tfmt.Println(getCAROOT())\n\t\treturn\n\t}\n\tif *installFlag && *uninstallFlag {\n\t\tlog.Fatalln(\"ERROR: you can't set -install and -uninstall at the same time\")\n\t}\n\tif *csrFlag != \"\" && (*pkcs12Flag || *ecdsaFlag || *clientFlag) {\n\t\tlog.Fatalln(\"ERROR: can only combine -csr with -install and -cert-file\")\n\t}\n\tif *csrFlag != \"\" && flag.NArg() != 0 {\n\t\tlog.Fatalln(\"ERROR: can't specify extra arguments when using -csr\")\n\t}\n\t(&mkcert{\n\t\tinstallMode: *installFlag, uninstallMode: *uninstallFlag, csrPath: *csrFlag,\n\t\tpkcs12: *pkcs12Flag, ecdsa: *ecdsaFlag, client: *clientFlag,\n\t\tcertFile: *certFileFlag, keyFile: *keyFileFlag, p12File: *p12FileFlag,\n\t}).Run(flag.Args())\n}\n\nconst rootName = \"rootCA.pem\"\nconst rootKeyName = \"rootCA-key.pem\"\n\ntype mkcert struct {\n\tinstallMode, uninstallMode bool\n\tpkcs12, ecdsa, client      bool\n\tkeyFile, certFile, p12File string\n\tcsrPath                    string\n\n\tCAROOT string\n\tcaCert *x509.Certificate\n\tcaKey  crypto.PrivateKey\n\n\t// The system cert pool is only loaded once. After installing the root, checks\n\t// will keep failing until the next execution. TODO: maybe execve?\n\t// https://github.com/golang/go/issues/24540 (thanks, myself)\n\tignoreCheckFailure bool\n}\n\nfunc (m *mkcert) Run(args []string) {\n\tm.CAROOT = getCAROOT()\n\tif m.CAROOT == \"\" {\n\t\tlog.Fatalln(\"ERROR: failed to find the default CA location, set one as the CAROOT env var\")\n\t}\n\tfatalIfErr(os.MkdirAll(m.CAROOT, 0755), \"failed to create the CAROOT\")\n\tm.loadCA()\n\n\tif m.installMode {\n\t\tm.install()\n\t\tif len(args) == 0 {\n\t\t\treturn\n\t\t}\n\t} else if m.uninstallMode {\n\t\tm.uninstall()\n\t\treturn\n\t} else {\n\t\tvar warning bool\n\t\tif storeEnabled(\"system\") && !m.checkPlatform() {\n\t\t\twarning = true\n\t\t\tlog.Println(\"Note: the local CA is not installed in the system trust store.\")\n\t\t}\n\t\tif storeEnabled(\"nss\") && hasNSS && CertutilInstallHelp != \"\" && !m.checkNSS() {\n\t\t\twarning = true\n\t\t\tlog.Printf(\"Note: the local CA is not installed in the %s trust store.\", NSSBrowsers)\n\t\t}\n\t\tif storeEnabled(\"java\") && hasJava && !m.checkJava() {\n\t\t\twarning = true\n\t\t\tlog.Println(\"Note: the local CA is not installed in the Java trust store.\")\n\t\t}\n\t\tif warning {\n\t\t\tlog.Println(\"Run \\\"mkcert -install\\\" for certificates to be trusted automatically ⚠️\")\n\t\t}\n\t}\n\n\tif m.csrPath != \"\" {\n\t\tm.makeCertFromCSR()\n\t\treturn\n\t}\n\n\tif len(args) == 0 {\n\t\tflag.Usage()\n\t\treturn\n\t}\n\n\thostnameRegexp := regexp.MustCompile(`(?i)^(\\*\\.)?[0-9a-z_-]([0-9a-z._-]*[0-9a-z_-])?$`)\n\tfor i, name := range args {\n\t\tif ip := net.ParseIP(name); ip != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif email, err := mail.ParseAddress(name); err == nil && email.Address == name {\n\t\t\tcontinue\n\t\t}\n\t\tif uriName, err := url.Parse(name); err == nil && uriName.Scheme != \"\" && uriName.Host != \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tpunycode, err := idna.ToASCII(name)\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"ERROR: %q is not a valid hostname, IP, URL or email: %s\", name, err)\n\t\t}\n\t\targs[i] = punycode\n\t\tif !hostnameRegexp.MatchString(punycode) {\n\t\t\tlog.Fatalf(\"ERROR: %q is not a valid hostname, IP, URL or email\", name)\n\t\t}\n\t}\n\n\tm.makeCert(args)\n}\n\nfunc getCAROOT() string {\n\tif env := os.Getenv(\"CAROOT\"); env != \"\" {\n\t\treturn env\n\t}\n\n\tvar dir string\n\tswitch {\n\tcase runtime.GOOS == \"windows\":\n\t\tdir = os.Getenv(\"LocalAppData\")\n\tcase os.Getenv(\"XDG_DATA_HOME\") != \"\":\n\t\tdir = os.Getenv(\"XDG_DATA_HOME\")\n\tcase runtime.GOOS == \"darwin\":\n\t\tdir = os.Getenv(\"HOME\")\n\t\tif dir == \"\" {\n\t\t\treturn \"\"\n\t\t}\n\t\tdir = filepath.Join(dir, \"Library\", \"Application Support\")\n\tdefault: // Unix\n\t\tdir = os.Getenv(\"HOME\")\n\t\tif dir == \"\" {\n\t\t\treturn \"\"\n\t\t}\n\t\tdir = filepath.Join(dir, \".local\", \"share\")\n\t}\n\treturn filepath.Join(dir, \"mkcert\")\n}\n\nfunc (m *mkcert) install() {\n\tif storeEnabled(\"system\") {\n\t\tif m.checkPlatform() {\n\t\t\tlog.Print(\"The local CA is already installed in the system trust store! 👍\")\n\t\t} else {\n\t\t\tif m.installPlatform() {\n\t\t\t\tlog.Print(\"The local CA is now installed in the system trust store! ⚡️\")\n\t\t\t}\n\t\t\tm.ignoreCheckFailure = true // TODO: replace with a check for a successful install\n\t\t}\n\t}\n\tif storeEnabled(\"nss\") && hasNSS {\n\t\tif m.checkNSS() {\n\t\t\tlog.Printf(\"The local CA is already installed in the %s trust store! 👍\", NSSBrowsers)\n\t\t} else {\n\t\t\tif hasCertutil && m.installNSS() {\n\t\t\t\tlog.Printf(\"The local CA is now installed in the %s trust store (requires browser restart)! 🦊\", NSSBrowsers)\n\t\t\t} else if CertutilInstallHelp == \"\" {\n\t\t\t\tlog.Printf(`Note: %s support is not available on your platform. ℹ️`, NSSBrowsers)\n\t\t\t} else if !hasCertutil {\n\t\t\t\tlog.Printf(`Warning: \"certutil\" is not available, so the CA can't be automatically installed in %s! ⚠️`, NSSBrowsers)\n\t\t\t\tlog.Printf(`Install \"certutil\" with \"%s\" and re-run \"mkcert -install\" 👈`, CertutilInstallHelp)\n\t\t\t}\n\t\t}\n\t}\n\tif storeEnabled(\"java\") && hasJava {\n\t\tif m.checkJava() {\n\t\t\tlog.Println(\"The local CA is already installed in Java's trust store! 👍\")\n\t\t} else {\n\t\t\tif hasKeytool {\n\t\t\t\tm.installJava()\n\t\t\t\tlog.Println(\"The local CA is now installed in Java's trust store! ☕️\")\n\t\t\t} else {\n\t\t\t\tlog.Println(`Warning: \"keytool\" is not available, so the CA can't be automatically installed in Java's trust store! ⚠️`)\n\t\t\t}\n\t\t}\n\t}\n\tlog.Print(\"\")\n}\n\nfunc (m *mkcert) uninstall() {\n\tif storeEnabled(\"nss\") && hasNSS {\n\t\tif hasCertutil {\n\t\t\tm.uninstallNSS()\n\t\t} else if CertutilInstallHelp != \"\" {\n\t\t\tlog.Print(\"\")\n\t\t\tlog.Printf(`Warning: \"certutil\" is not available, so the CA can't be automatically uninstalled from %s (if it was ever installed)! ⚠️`, NSSBrowsers)\n\t\t\tlog.Printf(`You can install \"certutil\" with \"%s\" and re-run \"mkcert -uninstall\" 👈`, CertutilInstallHelp)\n\t\t\tlog.Print(\"\")\n\t\t}\n\t}\n\tif storeEnabled(\"java\") && hasJava {\n\t\tif hasKeytool {\n\t\t\tm.uninstallJava()\n\t\t} else {\n\t\t\tlog.Print(\"\")\n\t\t\tlog.Println(`Warning: \"keytool\" is not available, so the CA can't be automatically uninstalled from Java's trust store (if it was ever installed)! ⚠️`)\n\t\t\tlog.Print(\"\")\n\t\t}\n\t}\n\tif storeEnabled(\"system\") && m.uninstallPlatform() {\n\t\tlog.Print(\"The local CA is now uninstalled from the system trust store(s)! 👋\")\n\t\tlog.Print(\"\")\n\t} else if storeEnabled(\"nss\") && hasCertutil {\n\t\tlog.Printf(\"The local CA is now uninstalled from the %s trust store(s)! 👋\", NSSBrowsers)\n\t\tlog.Print(\"\")\n\t}\n}\n\nfunc (m *mkcert) checkPlatform() bool {\n\tif m.ignoreCheckFailure {\n\t\treturn true\n\t}\n\n\t_, err := m.caCert.Verify(x509.VerifyOptions{})\n\treturn err == nil\n}\n\nfunc storeEnabled(name string) bool {\n\tstores := os.Getenv(\"TRUST_STORES\")\n\tif stores == \"\" {\n\t\treturn true\n\t}\n\tfor _, store := range strings.Split(stores, \",\") {\n\t\tif store == name {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc fatalIfErr(err error, msg string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"ERROR: %s: %s\", msg, err)\n\t}\n}\n\nfunc fatalIfCmdErr(err error, cmd string, out []byte) {\n\tif err != nil {\n\t\tlog.Fatalf(\"ERROR: failed to execute \\\"%s\\\": %s\\n\\n%s\\n\", cmd, err, out)\n\t}\n}\n\nfunc pathExists(path string) bool {\n\t_, err := os.Stat(path)\n\treturn err == nil\n}\n\nfunc binaryExists(name string) bool {\n\t_, err := exec.LookPath(name)\n\treturn err == nil\n}\n\nvar sudoWarningOnce sync.Once\n\nfunc commandWithSudo(cmd ...string) *exec.Cmd {\n\tif u, err := user.Current(); err == nil && u.Uid == \"0\" {\n\t\treturn exec.Command(cmd[0], cmd[1:]...)\n\t}\n\tif !binaryExists(\"sudo\") {\n\t\tsudoWarningOnce.Do(func() {\n\t\t\tlog.Println(`Warning: \"sudo\" is not available, and mkcert is not running as root. The (un)install operation might fail. ⚠️`)\n\t\t})\n\t\treturn exec.Command(cmd[0], cmd[1:]...)\n\t}\n\treturn exec.Command(\"sudo\", append([]string{\"--prompt=Sudo password:\", \"--\"}, cmd...)...)\n}\n"
  },
  {
    "path": "truststore_darwin.go",
    "content": "// Copyright 2018 The mkcert Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/asn1\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"howett.net/plist\"\n)\n\nvar (\n\tFirefoxProfiles     = []string{os.Getenv(\"HOME\") + \"/Library/Application Support/Firefox/Profiles/*\"}\n\tCertutilInstallHelp = \"brew install nss\"\n\tNSSBrowsers         = \"Firefox\"\n)\n\n// https://github.com/golang/go/issues/24652#issuecomment-399826583\nvar trustSettings []interface{}\nvar _, _ = plist.Unmarshal(trustSettingsData, &trustSettings)\nvar trustSettingsData = []byte(`\n<array>\n\t<dict>\n\t\t<key>kSecTrustSettingsPolicy</key>\n\t\t<data>\n\t\tKoZIhvdjZAED\n\t\t</data>\n\t\t<key>kSecTrustSettingsPolicyName</key>\n\t\t<string>sslServer</string>\n\t\t<key>kSecTrustSettingsResult</key>\n\t\t<integer>1</integer>\n\t</dict>\n\t<dict>\n\t\t<key>kSecTrustSettingsPolicy</key>\n\t\t<data>\n\t\tKoZIhvdjZAEC\n\t\t</data>\n\t\t<key>kSecTrustSettingsPolicyName</key>\n\t\t<string>basicX509</string>\n\t\t<key>kSecTrustSettingsResult</key>\n\t\t<integer>1</integer>\n\t</dict>\n</array>\n`)\n\nfunc (m *mkcert) installPlatform() bool {\n\tcmd := commandWithSudo(\"security\", \"add-trusted-cert\", \"-d\", \"-k\", \"/Library/Keychains/System.keychain\", filepath.Join(m.CAROOT, rootName))\n\tout, err := cmd.CombinedOutput()\n\tfatalIfCmdErr(err, \"security add-trusted-cert\", out)\n\n\t// Make trustSettings explicit, as older Go does not know the defaults.\n\t// https://github.com/golang/go/issues/24652\n\n\tplistFile, err := ioutil.TempFile(\"\", \"trust-settings\")\n\tfatalIfErr(err, \"failed to create temp file\")\n\tdefer os.Remove(plistFile.Name())\n\n\tcmd = commandWithSudo(\"security\", \"trust-settings-export\", \"-d\", plistFile.Name())\n\tout, err = cmd.CombinedOutput()\n\tfatalIfCmdErr(err, \"security trust-settings-export\", out)\n\n\tplistData, err := ioutil.ReadFile(plistFile.Name())\n\tfatalIfErr(err, \"failed to read trust settings\")\n\tvar plistRoot map[string]interface{}\n\t_, err = plist.Unmarshal(plistData, &plistRoot)\n\tfatalIfErr(err, \"failed to parse trust settings\")\n\n\trootSubjectASN1, _ := asn1.Marshal(m.caCert.Subject.ToRDNSequence())\n\n\tif plistRoot[\"trustVersion\"].(uint64) != 1 {\n\t\tlog.Fatalln(\"ERROR: unsupported trust settings version:\", plistRoot[\"trustVersion\"])\n\t}\n\ttrustList := plistRoot[\"trustList\"].(map[string]interface{})\n\tfor key := range trustList {\n\t\tentry := trustList[key].(map[string]interface{})\n\t\tif _, ok := entry[\"issuerName\"]; !ok {\n\t\t\tcontinue\n\t\t}\n\t\tissuerName := entry[\"issuerName\"].([]byte)\n\t\tif !bytes.Equal(rootSubjectASN1, issuerName) {\n\t\t\tcontinue\n\t\t}\n\t\tentry[\"trustSettings\"] = trustSettings\n\t\tbreak\n\t}\n\n\tplistData, err = plist.MarshalIndent(plistRoot, plist.XMLFormat, \"\\t\")\n\tfatalIfErr(err, \"failed to serialize trust settings\")\n\terr = ioutil.WriteFile(plistFile.Name(), plistData, 0600)\n\tfatalIfErr(err, \"failed to write trust settings\")\n\n\tcmd = commandWithSudo(\"security\", \"trust-settings-import\", \"-d\", plistFile.Name())\n\tout, err = cmd.CombinedOutput()\n\tfatalIfCmdErr(err, \"security trust-settings-import\", out)\n\n\treturn true\n}\n\nfunc (m *mkcert) uninstallPlatform() bool {\n\tcmd := commandWithSudo(\"security\", \"remove-trusted-cert\", \"-d\", filepath.Join(m.CAROOT, rootName))\n\tout, err := cmd.CombinedOutput()\n\tfatalIfCmdErr(err, \"security remove-trusted-cert\", out)\n\n\treturn true\n}\n"
  },
  {
    "path": "truststore_java.go",
    "content": "// Copyright 2018 The mkcert Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha1\"\n\t\"crypto/sha256\"\n\t\"crypto/x509\"\n\t\"encoding/hex\"\n\t\"hash\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nvar (\n\thasJava    bool\n\thasKeytool bool\n\n\tjavaHome    string\n\tcacertsPath string\n\tkeytoolPath string\n\tstorePass   string = \"changeit\"\n)\n\nfunc init() {\n\tif runtime.GOOS == \"windows\" {\n\t\tkeytoolPath = filepath.Join(\"bin\", \"keytool.exe\")\n\t} else {\n\t\tkeytoolPath = filepath.Join(\"bin\", \"keytool\")\n\t}\n\n\tif v := os.Getenv(\"JAVA_HOME\"); v != \"\" {\n\t\thasJava = true\n\t\tjavaHome = v\n\n\t\tif pathExists(filepath.Join(v, keytoolPath)) {\n\t\t\thasKeytool = true\n\t\t\tkeytoolPath = filepath.Join(v, keytoolPath)\n\t\t}\n\n\t\tif pathExists(filepath.Join(v, \"lib\", \"security\", \"cacerts\")) {\n\t\t\tcacertsPath = filepath.Join(v, \"lib\", \"security\", \"cacerts\")\n\t\t}\n\n\t\tif pathExists(filepath.Join(v, \"jre\", \"lib\", \"security\", \"cacerts\")) {\n\t\t\tcacertsPath = filepath.Join(v, \"jre\", \"lib\", \"security\", \"cacerts\")\n\t\t}\n\t}\n}\n\nfunc (m *mkcert) checkJava() bool {\n\tif !hasKeytool {\n\t\treturn false\n\t}\n\n\t// exists returns true if the given x509.Certificate's fingerprint\n\t// is in the keytool -list output\n\texists := func(c *x509.Certificate, h hash.Hash, keytoolOutput []byte) bool {\n\t\th.Write(c.Raw)\n\t\tfp := strings.ToUpper(hex.EncodeToString(h.Sum(nil)))\n\t\treturn bytes.Contains(keytoolOutput, []byte(fp))\n\t}\n\n\tkeytoolOutput, err := exec.Command(keytoolPath, \"-list\", \"-keystore\", cacertsPath, \"-storepass\", storePass).CombinedOutput()\n\tfatalIfCmdErr(err, \"keytool -list\", keytoolOutput)\n\t// keytool outputs SHA1 and SHA256 (Java 9+) certificates in uppercase hex\n\t// with each octet pair delimitated by \":\". Drop them from the keytool output\n\tkeytoolOutput = bytes.Replace(keytoolOutput, []byte(\":\"), nil, -1)\n\n\t// pre-Java 9 uses SHA1 fingerprints\n\ts1, s256 := sha1.New(), sha256.New()\n\treturn exists(m.caCert, s1, keytoolOutput) || exists(m.caCert, s256, keytoolOutput)\n}\n\nfunc (m *mkcert) installJava() {\n\targs := []string{\n\t\t\"-importcert\", \"-noprompt\",\n\t\t\"-keystore\", cacertsPath,\n\t\t\"-storepass\", storePass,\n\t\t\"-file\", filepath.Join(m.CAROOT, rootName),\n\t\t\"-alias\", m.caUniqueName(),\n\t}\n\n\tout, err := execKeytool(exec.Command(keytoolPath, args...))\n\tfatalIfCmdErr(err, \"keytool -importcert\", out)\n}\n\nfunc (m *mkcert) uninstallJava() {\n\targs := []string{\n\t\t\"-delete\",\n\t\t\"-alias\", m.caUniqueName(),\n\t\t\"-keystore\", cacertsPath,\n\t\t\"-storepass\", storePass,\n\t}\n\tout, err := execKeytool(exec.Command(keytoolPath, args...))\n\tif bytes.Contains(out, []byte(\"does not exist\")) {\n\t\treturn // cert didn't exist\n\t}\n\tfatalIfCmdErr(err, \"keytool -delete\", out)\n}\n\n// execKeytool will execute a \"keytool\" command and if needed re-execute\n// the command with commandWithSudo to work around file permissions.\nfunc execKeytool(cmd *exec.Cmd) ([]byte, error) {\n\tout, err := cmd.CombinedOutput()\n\tif err != nil && bytes.Contains(out, []byte(\"java.io.FileNotFoundException\")) && runtime.GOOS != \"windows\" {\n\t\torigArgs := cmd.Args[1:]\n\t\tcmd = commandWithSudo(cmd.Path)\n\t\tcmd.Args = append(cmd.Args, origArgs...)\n\t\tcmd.Env = []string{\n\t\t\t\"JAVA_HOME=\" + javaHome,\n\t\t}\n\t\tout, err = cmd.CombinedOutput()\n\t}\n\treturn out, err\n}\n"
  },
  {
    "path": "truststore_linux.go",
    "content": "// Copyright 2018 The mkcert Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nvar (\n\tFirefoxProfiles = []string{os.Getenv(\"HOME\") + \"/.mozilla/firefox/*\",\n\t\tos.Getenv(\"HOME\") + \"/snap/firefox/common/.mozilla/firefox/*\"}\n\tNSSBrowsers = \"Firefox and/or Chrome/Chromium\"\n\n\tSystemTrustFilename string\n\tSystemTrustCommand  []string\n\tCertutilInstallHelp string\n)\n\nfunc init() {\n\tswitch {\n\tcase binaryExists(\"apt\"):\n\t\tCertutilInstallHelp = \"apt install libnss3-tools\"\n\tcase binaryExists(\"yum\"):\n\t\tCertutilInstallHelp = \"yum install nss-tools\"\n\tcase binaryExists(\"zypper\"):\n\t\tCertutilInstallHelp = \"zypper install mozilla-nss-tools\"\n\t}\n\tif pathExists(\"/etc/pki/ca-trust/source/anchors/\") {\n\t\tSystemTrustFilename = \"/etc/pki/ca-trust/source/anchors/%s.pem\"\n\t\tSystemTrustCommand = []string{\"update-ca-trust\", \"extract\"}\n\t} else if pathExists(\"/usr/local/share/ca-certificates/\") {\n\t\tSystemTrustFilename = \"/usr/local/share/ca-certificates/%s.crt\"\n\t\tSystemTrustCommand = []string{\"update-ca-certificates\"}\n\t} else if pathExists(\"/etc/ca-certificates/trust-source/anchors/\") {\n\t\tSystemTrustFilename = \"/etc/ca-certificates/trust-source/anchors/%s.crt\"\n\t\tSystemTrustCommand = []string{\"trust\", \"extract-compat\"}\n\t} else if pathExists(\"/usr/share/pki/trust/anchors\") {\n\t\tSystemTrustFilename = \"/usr/share/pki/trust/anchors/%s.pem\"\n\t\tSystemTrustCommand = []string{\"update-ca-certificates\"}\n\t}\n}\n\nfunc (m *mkcert) systemTrustFilename() string {\n\treturn fmt.Sprintf(SystemTrustFilename, strings.Replace(m.caUniqueName(), \" \", \"_\", -1))\n}\n\nfunc (m *mkcert) installPlatform() bool {\n\tif SystemTrustCommand == nil {\n\t\tlog.Printf(\"Installing to the system store is not yet supported on this Linux 😣 but %s will still work.\", NSSBrowsers)\n\t\tlog.Printf(\"You can also manually install the root certificate at %q.\", filepath.Join(m.CAROOT, rootName))\n\t\treturn false\n\t}\n\n\tcert, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))\n\tfatalIfErr(err, \"failed to read root certificate\")\n\n\tcmd := commandWithSudo(\"tee\", m.systemTrustFilename())\n\tcmd.Stdin = bytes.NewReader(cert)\n\tout, err := cmd.CombinedOutput()\n\tfatalIfCmdErr(err, \"tee\", out)\n\n\tcmd = commandWithSudo(SystemTrustCommand...)\n\tout, err = cmd.CombinedOutput()\n\tfatalIfCmdErr(err, strings.Join(SystemTrustCommand, \" \"), out)\n\n\treturn true\n}\n\nfunc (m *mkcert) uninstallPlatform() bool {\n\tif SystemTrustCommand == nil {\n\t\treturn false\n\t}\n\n\tcmd := commandWithSudo(\"rm\", \"-f\", m.systemTrustFilename())\n\tout, err := cmd.CombinedOutput()\n\tfatalIfCmdErr(err, \"rm\", out)\n\n\t// We used to install under non-unique filenames.\n\tlegacyFilename := fmt.Sprintf(SystemTrustFilename, \"mkcert-rootCA\")\n\tif pathExists(legacyFilename) {\n\t\tcmd := commandWithSudo(\"rm\", \"-f\", legacyFilename)\n\t\tout, err := cmd.CombinedOutput()\n\t\tfatalIfCmdErr(err, \"rm (legacy filename)\", out)\n\t}\n\n\tcmd = commandWithSudo(SystemTrustCommand...)\n\tout, err = cmd.CombinedOutput()\n\tfatalIfCmdErr(err, strings.Join(SystemTrustCommand, \" \"), out)\n\n\treturn true\n}\n"
  },
  {
    "path": "truststore_nss.go",
    "content": "// Copyright 2018 The mkcert Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nvar (\n\thasNSS       bool\n\thasCertutil  bool\n\tcertutilPath string\n\tnssDBs       = []string{\n\t\tfilepath.Join(os.Getenv(\"HOME\"), \".pki/nssdb\"),\n\t\tfilepath.Join(os.Getenv(\"HOME\"), \"snap/chromium/current/.pki/nssdb\"), // Snapcraft\n\t\t\"/etc/pki/nssdb\", // CentOS 7\n\t}\n\tfirefoxPaths = []string{\n\t\t\"/usr/bin/firefox\",\n\t\t\"/usr/bin/firefox-nightly\",\n\t\t\"/usr/bin/firefox-developer-edition\",\n\t\t\"/snap/firefox\",\n\t\t\"/Applications/Firefox.app\",\n\t\t\"/Applications/FirefoxDeveloperEdition.app\",\n\t\t\"/Applications/Firefox Developer Edition.app\",\n\t\t\"/Applications/Firefox Nightly.app\",\n\t\t\"C:\\\\Program Files\\\\Mozilla Firefox\",\n\t}\n)\n\nfunc init() {\n\tallPaths := append(append([]string{}, nssDBs...), firefoxPaths...)\n\tfor _, path := range allPaths {\n\t\tif pathExists(path) {\n\t\t\thasNSS = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tswitch runtime.GOOS {\n\tcase \"darwin\":\n\t\tswitch {\n\t\tcase binaryExists(\"certutil\"):\n\t\t\tcertutilPath, _ = exec.LookPath(\"certutil\")\n\t\t\thasCertutil = true\n\t\tcase binaryExists(\"/usr/local/opt/nss/bin/certutil\"):\n\t\t\t// Check the default Homebrew path, to save executing Ruby. #135\n\t\t\tcertutilPath = \"/usr/local/opt/nss/bin/certutil\"\n\t\t\thasCertutil = true\n\t\tdefault:\n\t\t\tout, err := exec.Command(\"brew\", \"--prefix\", \"nss\").Output()\n\t\t\tif err == nil {\n\t\t\t\tcertutilPath = filepath.Join(strings.TrimSpace(string(out)), \"bin\", \"certutil\")\n\t\t\t\thasCertutil = pathExists(certutilPath)\n\t\t\t}\n\t\t}\n\n\tcase \"linux\":\n\t\tif hasCertutil = binaryExists(\"certutil\"); hasCertutil {\n\t\t\tcertutilPath, _ = exec.LookPath(\"certutil\")\n\t\t}\n\t}\n}\n\nfunc (m *mkcert) checkNSS() bool {\n\tif !hasCertutil {\n\t\treturn false\n\t}\n\tsuccess := true\n\tif m.forEachNSSProfile(func(profile string) {\n\t\terr := exec.Command(certutilPath, \"-V\", \"-d\", profile, \"-u\", \"L\", \"-n\", m.caUniqueName()).Run()\n\t\tif err != nil {\n\t\t\tsuccess = false\n\t\t}\n\t}) == 0 {\n\t\tsuccess = false\n\t}\n\treturn success\n}\n\nfunc (m *mkcert) installNSS() bool {\n\tif m.forEachNSSProfile(func(profile string) {\n\t\tcmd := exec.Command(certutilPath, \"-A\", \"-d\", profile, \"-t\", \"C,,\", \"-n\", m.caUniqueName(), \"-i\", filepath.Join(m.CAROOT, rootName))\n\t\tout, err := execCertutil(cmd)\n\t\tfatalIfCmdErr(err, \"certutil -A -d \"+profile, out)\n\t}) == 0 {\n\t\tlog.Printf(\"ERROR: no %s security databases found\", NSSBrowsers)\n\t\treturn false\n\t}\n\tif !m.checkNSS() {\n\t\tlog.Printf(\"Installing in %s failed. Please report the issue with details about your environment at https://github.com/FiloSottile/mkcert/issues/new 👎\", NSSBrowsers)\n\t\tlog.Printf(\"Note that if you never started %s, you need to do that at least once.\", NSSBrowsers)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (m *mkcert) uninstallNSS() {\n\tm.forEachNSSProfile(func(profile string) {\n\t\terr := exec.Command(certutilPath, \"-V\", \"-d\", profile, \"-u\", \"L\", \"-n\", m.caUniqueName()).Run()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tcmd := exec.Command(certutilPath, \"-D\", \"-d\", profile, \"-n\", m.caUniqueName())\n\t\tout, err := execCertutil(cmd)\n\t\tfatalIfCmdErr(err, \"certutil -D -d \"+profile, out)\n\t})\n}\n\n// execCertutil will execute a \"certutil\" command and if needed re-execute\n// the command with commandWithSudo to work around file permissions.\nfunc execCertutil(cmd *exec.Cmd) ([]byte, error) {\n\tout, err := cmd.CombinedOutput()\n\tif err != nil && bytes.Contains(out, []byte(\"SEC_ERROR_READ_ONLY\")) && runtime.GOOS != \"windows\" {\n\t\torigArgs := cmd.Args[1:]\n\t\tcmd = commandWithSudo(cmd.Path)\n\t\tcmd.Args = append(cmd.Args, origArgs...)\n\t\tout, err = cmd.CombinedOutput()\n\t}\n\treturn out, err\n}\n\nfunc (m *mkcert) forEachNSSProfile(f func(profile string)) (found int) {\n\tvar profiles []string\n\tprofiles = append(profiles, nssDBs...)\n\tfor _, ff := range FirefoxProfiles {\n\t\tpp, _ := filepath.Glob(ff)\n\t\tprofiles = append(profiles, pp...)\n\t}\n\tfor _, profile := range profiles {\n\t\tif stat, err := os.Stat(profile); err != nil || !stat.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tif pathExists(filepath.Join(profile, \"cert9.db\")) {\n\t\t\tf(\"sql:\" + profile)\n\t\t\tfound++\n\t\t} else if pathExists(filepath.Join(profile, \"cert8.db\")) {\n\t\t\tf(\"dbm:\" + profile)\n\t\t\tfound++\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "truststore_windows.go",
    "content": "// Copyright 2018 The mkcert Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"math/big\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nvar (\n\tFirefoxProfiles     = []string{os.Getenv(\"USERPROFILE\") + \"\\\\AppData\\\\Roaming\\\\Mozilla\\\\Firefox\\\\Profiles\"}\n\tCertutilInstallHelp = \"\" // certutil unsupported on Windows\n\tNSSBrowsers         = \"Firefox\"\n)\n\nvar (\n\tmodcrypt32                           = syscall.NewLazyDLL(\"crypt32.dll\")\n\tprocCertAddEncodedCertificateToStore = modcrypt32.NewProc(\"CertAddEncodedCertificateToStore\")\n\tprocCertCloseStore                   = modcrypt32.NewProc(\"CertCloseStore\")\n\tprocCertDeleteCertificateFromStore   = modcrypt32.NewProc(\"CertDeleteCertificateFromStore\")\n\tprocCertDuplicateCertificateContext  = modcrypt32.NewProc(\"CertDuplicateCertificateContext\")\n\tprocCertEnumCertificatesInStore      = modcrypt32.NewProc(\"CertEnumCertificatesInStore\")\n\tprocCertOpenSystemStoreW             = modcrypt32.NewProc(\"CertOpenSystemStoreW\")\n)\n\nfunc (m *mkcert) installPlatform() bool {\n\t// Load cert\n\tcert, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))\n\tfatalIfErr(err, \"failed to read root certificate\")\n\t// Decode PEM\n\tif certBlock, _ := pem.Decode(cert); certBlock == nil || certBlock.Type != \"CERTIFICATE\" {\n\t\tfatalIfErr(fmt.Errorf(\"invalid PEM data\"), \"decode pem\")\n\t} else {\n\t\tcert = certBlock.Bytes\n\t}\n\t// Open root store\n\tstore, err := openWindowsRootStore()\n\tfatalIfErr(err, \"open root store\")\n\tdefer store.close()\n\t// Add cert\n\tfatalIfErr(store.addCert(cert), \"add cert\")\n\treturn true\n}\n\nfunc (m *mkcert) uninstallPlatform() bool {\n\t// We'll just remove all certs with the same serial number\n\t// Open root store\n\tstore, err := openWindowsRootStore()\n\tfatalIfErr(err, \"open root store\")\n\tdefer store.close()\n\t// Do the deletion\n\tdeletedAny, err := store.deleteCertsWithSerial(m.caCert.SerialNumber)\n\tif err == nil && !deletedAny {\n\t\terr = fmt.Errorf(\"no certs found\")\n\t}\n\tfatalIfErr(err, \"delete cert\")\n\treturn true\n}\n\ntype windowsRootStore uintptr\n\nfunc openWindowsRootStore() (windowsRootStore, error) {\n\trootStr, err := syscall.UTF16PtrFromString(\"ROOT\")\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tstore, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(rootStr)))\n\tif store != 0 {\n\t\treturn windowsRootStore(store), nil\n\t}\n\treturn 0, fmt.Errorf(\"failed to open windows root store: %v\", err)\n}\n\nfunc (w windowsRootStore) close() error {\n\tret, _, err := procCertCloseStore.Call(uintptr(w), 0)\n\tif ret != 0 {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"failed to close windows root store: %v\", err)\n}\n\nfunc (w windowsRootStore) addCert(cert []byte) error {\n\t// TODO: ok to always overwrite?\n\tret, _, err := procCertAddEncodedCertificateToStore.Call(\n\t\tuintptr(w), // HCERTSTORE hCertStore\n\t\tuintptr(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING), // DWORD dwCertEncodingType\n\t\tuintptr(unsafe.Pointer(&cert[0])),                              // const BYTE *pbCertEncoded\n\t\tuintptr(len(cert)),                                             // DWORD cbCertEncoded\n\t\t3,                                                              // DWORD dwAddDisposition (CERT_STORE_ADD_REPLACE_EXISTING is 3)\n\t\t0,                                                              // PCCERT_CONTEXT *ppCertContext\n\t)\n\tif ret != 0 {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"failed adding cert: %v\", err)\n}\n\nfunc (w windowsRootStore) deleteCertsWithSerial(serial *big.Int) (bool, error) {\n\t// Go over each, deleting the ones we find\n\tvar cert *syscall.CertContext\n\tdeletedAny := false\n\tfor {\n\t\t// Next enum\n\t\tcertPtr, _, err := procCertEnumCertificatesInStore.Call(uintptr(w), uintptr(unsafe.Pointer(cert)))\n\t\tif cert = (*syscall.CertContext)(unsafe.Pointer(certPtr)); cert == nil {\n\t\t\tif errno, ok := err.(syscall.Errno); ok && errno == 0x80092004 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn deletedAny, fmt.Errorf(\"failed enumerating certs: %v\", err)\n\t\t}\n\t\t// Parse cert\n\t\tcertBytes := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length]\n\t\tparsedCert, err := x509.ParseCertificate(certBytes)\n\t\t// We'll just ignore parse failures for now\n\t\tif err == nil && parsedCert.SerialNumber != nil && parsedCert.SerialNumber.Cmp(serial) == 0 {\n\t\t\t// Duplicate the context so it doesn't stop the enum when we delete it\n\t\t\tdupCertPtr, _, err := procCertDuplicateCertificateContext.Call(uintptr(unsafe.Pointer(cert)))\n\t\t\tif dupCertPtr == 0 {\n\t\t\t\treturn deletedAny, fmt.Errorf(\"failed duplicating context: %v\", err)\n\t\t\t}\n\t\t\tif ret, _, err := procCertDeleteCertificateFromStore.Call(dupCertPtr); ret == 0 {\n\t\t\t\treturn deletedAny, fmt.Errorf(\"failed deleting certificate: %v\", err)\n\t\t\t}\n\t\t\tdeletedAny = true\n\t\t}\n\t}\n\treturn deletedAny, nil\n}\n"
  }
]