mirror of
https://github.com/FiloSottile/mkcert.git
synced 2025-10-14 17:01:41 +08:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2d05f3b4d8 | ||
![]() |
a5bb69b629 | ||
![]() |
df15e0c1ef | ||
![]() |
1f0796c926 | ||
![]() |
4d2ab596e7 | ||
![]() |
c2b30c48f1 | ||
![]() |
0a679a8bcd | ||
![]() |
574ea52743 | ||
![]() |
ff17118210 | ||
![]() |
72ec55f07f | ||
![]() |
e9f8fbcdf4 | ||
![]() |
8d9e434520 | ||
![]() |
74ab68812e | ||
![]() |
245b2732c8 | ||
![]() |
b1564cfb0d | ||
![]() |
b90c9c60cb | ||
![]() |
bf08925790 | ||
![]() |
ad5c6ddbef | ||
![]() |
0d4cf75db8 | ||
![]() |
c7c85b20ae | ||
![]() |
c03e3ceaca | ||
![]() |
9e9563535e | ||
![]() |
fcdbc54cc3 | ||
![]() |
99e15e29f9 | ||
![]() |
592400aab0 | ||
![]() |
66af5a51f6 | ||
![]() |
5bb0c47df7 | ||
![]() |
821679b01f | ||
![]() |
50b8c9f09f | ||
![]() |
3bcdd3721c | ||
![]() |
a177c7e2ad | ||
![]() |
8dca36bc48 | ||
![]() |
610df05c5c |
10
.travis.yml
10
.travis.yml
@@ -1,13 +1,15 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go: stable
|
||||
install: "# skip"
|
||||
install: (cd && go get honnef.co/go/tools/cmd/staticcheck)
|
||||
|
||||
script:
|
||||
- go vet
|
||||
- GOOS=linux GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-linux-amd64"
|
||||
- GOOS=darwin GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-darwin-amd64"
|
||||
- GOOS=windows GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-windows-amd64.exe"
|
||||
- staticcheck ./...
|
||||
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-linux-amd64"
|
||||
- CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o "mkcert-$(git describe --tags)-linux-arm"
|
||||
- CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-darwin-amd64"
|
||||
- CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-windows-amd64.exe"
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
|
52
README.md
52
README.md
@@ -6,15 +6,15 @@ mkcert is a simple tool for making locally-trusted development certificates. It
|
||||
$ mkcert -install
|
||||
Created a new local CA at "/Users/filippo/Library/Application Support/mkcert" 💥
|
||||
The local CA is now installed in the system trust store! ⚡️
|
||||
The local CA is now installed in the Firefox trust store (requires restart)! 🦊
|
||||
The local CA is now installed in the Firefox trust store (requires browser restart)! 🦊
|
||||
|
||||
$ mkcert example.com "*.example.org" myapp.dev localhost 127.0.0.1 ::1
|
||||
$ mkcert example.com "*.example.com" example.test localhost 127.0.0.1 ::1
|
||||
Using the local CA at "/Users/filippo/Library/Application Support/mkcert" ✨
|
||||
|
||||
Created a new certificate valid for the following names 📜
|
||||
- "example.com"
|
||||
- "*.example.org"
|
||||
- "myapp.dev"
|
||||
- "*.example.com"
|
||||
- "example.test"
|
||||
- "localhost"
|
||||
- "127.0.0.1"
|
||||
- "::1"
|
||||
@@ -22,9 +22,9 @@ Created a new certificate valid for the following names 📜
|
||||
The certificate is at "./example.com+5.pem" and the key at "./example.com+5-key.pem" ✅
|
||||
```
|
||||
|
||||
<p align="center"><img width="444" alt="Chrome screenshot" src="https://user-images.githubusercontent.com/1225294/41887838-7acd55ca-78d0-11e8-8a81-139a54faaf87.png"></p>
|
||||
<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>
|
||||
|
||||
Using certificates from real certificate authorities (CAs) for development can be dangerous or impossible (for hosts like `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.
|
||||
Using 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.
|
||||
|
||||
mkcert 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.
|
||||
|
||||
@@ -59,13 +59,15 @@ sudo apt install libnss3-tools
|
||||
sudo yum install nss-tools
|
||||
-or-
|
||||
sudo pacman -S nss
|
||||
-or-
|
||||
sudo zypper install mozilla-nss-tools
|
||||
```
|
||||
|
||||
Then you can install using [Linuxbrew](http://linuxbrew.sh/)
|
||||
|
||||
```
|
||||
brew install mkcert
|
||||
````
|
||||
```
|
||||
|
||||
or build from source (requires Go 1.10+)
|
||||
|
||||
@@ -86,7 +88,7 @@ makepkg -si
|
||||
|
||||
### Windows
|
||||
|
||||
On Windows, use Chocolatey
|
||||
On Windows, use [Chocolatey](https://chocolatey.org)
|
||||
|
||||
```
|
||||
choco install mkcert
|
||||
@@ -95,6 +97,7 @@ choco install mkcert
|
||||
or use Scoop
|
||||
|
||||
```
|
||||
scoop bucket add extras
|
||||
scoop install mkcert
|
||||
```
|
||||
|
||||
@@ -110,14 +113,45 @@ mkcert supports the following root stores:
|
||||
* Windows system store
|
||||
* Linux variants that provide either
|
||||
* `update-ca-trust` (Fedora, RHEL, CentOS) or
|
||||
* `update-ca-certificates` (Ubuntu, Debian) or
|
||||
* `update-ca-certificates` (Ubuntu, Debian, OpenSUSE, SLES) or
|
||||
* `trust` (Arch)
|
||||
* Firefox (macOS and Linux only)
|
||||
* Chrome and Chromium
|
||||
* Java (when `JAVA_HOME` is set)
|
||||
|
||||
To 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).
|
||||
|
||||
## Advanced topics
|
||||
|
||||
### Advanced options
|
||||
|
||||
```
|
||||
-cert-file FILE, -key-file FILE, -p12-file FILE
|
||||
Customize the output paths.
|
||||
|
||||
-client
|
||||
Generate a certificate for client authentication.
|
||||
|
||||
-ecdsa
|
||||
Generate a certificate with an ECDSA key.
|
||||
|
||||
-pkcs12
|
||||
Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,
|
||||
containing certificate and key for legacy applications.
|
||||
|
||||
-csr CSR
|
||||
Generate a certificate based on the supplied CSR. Conflicts with
|
||||
all other flags and arguments except -install and -cert-file.
|
||||
```
|
||||
|
||||
### S/MIME
|
||||
|
||||
mkcert automatically generates an S/MIME certificate if one of the supplied names is an email address.
|
||||
|
||||
```
|
||||
mkcert filippo@example.com
|
||||
```
|
||||
|
||||
### Mobile devices
|
||||
|
||||
For 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`.
|
||||
|
176
cert.go
176
cert.go
@@ -1,10 +1,13 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Copyright 2018 The mkcert Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
@@ -16,8 +19,9 @@ import (
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -25,18 +29,22 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
pkcs12 "software.sslmate.com/src/go-pkcs12"
|
||||
)
|
||||
|
||||
var userAndHostname string
|
||||
|
||||
func init() {
|
||||
u, _ := user.Current()
|
||||
if u != nil {
|
||||
u, err := user.Current()
|
||||
if err == nil {
|
||||
userAndHostname = u.Username + "@"
|
||||
}
|
||||
out, _ := exec.Command("hostname").Output()
|
||||
userAndHostname += strings.TrimSpace(string(out))
|
||||
if h, err := os.Hostname(); err == nil {
|
||||
userAndHostname += h
|
||||
}
|
||||
if err == nil && u.Name != "" && u.Name != u.Username {
|
||||
userAndHostname += " (" + u.Name + ")"
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mkcert) makeCert(hosts []string) {
|
||||
@@ -44,37 +52,58 @@ func (m *mkcert) makeCert(hosts []string) {
|
||||
log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
|
||||
}
|
||||
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
priv, err := m.generateKey(false)
|
||||
fatalIfErr(err, "failed to generate certificate key")
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
fatalIfErr(err, "failed to generate serial number")
|
||||
pub := priv.(crypto.Signer).Public()
|
||||
|
||||
tpl := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
SerialNumber: randomSerialNumber(),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"mkcert development certificate"},
|
||||
OrganizationalUnit: []string{userAndHostname},
|
||||
},
|
||||
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
NotBefore: time.Now(),
|
||||
|
||||
// Fix the notBefore to temporarily bypass macOS Catalina's limit on
|
||||
// certificate lifespan. Once mkcert provides an ACME server, automation
|
||||
// will be the recommended way to guarantee uninterrupted functionality,
|
||||
// and the lifespan will be shortened to 825 days. See issue 174 and
|
||||
// https://support.apple.com/en-us/HT210176.
|
||||
NotBefore: time.Date(2019, time.June, 1, 0, 0, 0, 0, time.UTC),
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
tpl.IPAddresses = append(tpl.IPAddresses, ip)
|
||||
} else if email, err := mail.ParseAddress(h); err == nil && email.Address == h {
|
||||
tpl.EmailAddresses = append(tpl.EmailAddresses, h)
|
||||
} else if uriName, err := url.Parse(h); err == nil && uriName.Scheme != "" && uriName.Host != "" {
|
||||
tpl.URIs = append(tpl.URIs, uriName)
|
||||
} else {
|
||||
tpl.DNSNames = append(tpl.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
pub := priv.PublicKey
|
||||
cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, &pub, m.caKey)
|
||||
if m.client {
|
||||
tpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
|
||||
} else if len(tpl.IPAddresses) > 0 || len(tpl.DNSNames) > 0 {
|
||||
tpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
||||
}
|
||||
if len(tpl.EmailAddresses) > 0 {
|
||||
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageCodeSigning, x509.ExtKeyUsageEmailProtection)
|
||||
}
|
||||
|
||||
// IIS (the main target of PKCS #12 files), only shows the deprecated
|
||||
// Common Name in the UI. See issue #115.
|
||||
if m.pkcs12 {
|
||||
tpl.Subject.CommonName = hosts[0]
|
||||
}
|
||||
|
||||
cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, pub, m.caKey)
|
||||
fatalIfErr(err, "failed to generate certificate")
|
||||
|
||||
certFile, keyFile, p12File := m.fileNames(hosts)
|
||||
@@ -88,7 +117,7 @@ func (m *mkcert) makeCert(hosts []string) {
|
||||
|
||||
err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
|
||||
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
|
||||
fatalIfErr(err, "failed to save certificate key")
|
||||
fatalIfErr(err, "failed to save certificate")
|
||||
} else {
|
||||
domainCert, _ := x509.ParseCertificate(cert)
|
||||
pfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, "changeit")
|
||||
@@ -97,6 +126,17 @@ func (m *mkcert) makeCert(hosts []string) {
|
||||
fatalIfErr(err, "failed to save PKCS#12")
|
||||
}
|
||||
|
||||
m.printHosts(hosts)
|
||||
|
||||
if !m.pkcs12 {
|
||||
log.Printf("\nThe certificate is at \"%s\" and the key at \"%s\" ✅\n\n", certFile, keyFile)
|
||||
} else {
|
||||
log.Printf("\nThe PKCS#12 bundle is at \"%s\" ✅\n", p12File)
|
||||
log.Printf("\nThe legacy PKCS#12 encryption password is the often hardcoded default \"changeit\" ℹ️\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mkcert) printHosts(hosts []string) {
|
||||
secondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\*\.[0-9a-z_-]+$`)
|
||||
log.Printf("\nCreated a new certificate valid for the following names 📜")
|
||||
for _, h := range hosts {
|
||||
@@ -112,13 +152,16 @@ func (m *mkcert) makeCert(hosts []string) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !m.pkcs12 {
|
||||
log.Printf("\nThe certificate is at \"%s\" and the key at \"%s\" ✅\n\n", certFile, keyFile)
|
||||
} else {
|
||||
log.Printf("\nThe PKCS#12 bundle is at \"%s\" ✅\n", p12File)
|
||||
log.Printf("\nThe legacy PKCS#12 encryption password is the often hardcoded default \"changeit\" ℹ️\n\n")
|
||||
func (m *mkcert) generateKey(rootCA bool) (crypto.PrivateKey, error) {
|
||||
if m.ecdsa {
|
||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
}
|
||||
if rootCA {
|
||||
return rsa.GenerateKey(rand.Reader, 3072)
|
||||
}
|
||||
return rsa.GenerateKey(rand.Reader, 2048)
|
||||
}
|
||||
|
||||
func (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {
|
||||
@@ -127,6 +170,9 @@ func (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {
|
||||
if len(hosts) > 1 {
|
||||
defaultName += "+" + strconv.Itoa(len(hosts)-1)
|
||||
}
|
||||
if m.client {
|
||||
defaultName += "-client"
|
||||
}
|
||||
|
||||
certFile = "./" + defaultName + ".pem"
|
||||
if m.certFile != "" {
|
||||
@@ -144,9 +190,75 @@ func (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {
|
||||
return
|
||||
}
|
||||
|
||||
func randomSerialNumber() *big.Int {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
fatalIfErr(err, "failed to generate serial number")
|
||||
return serialNumber
|
||||
}
|
||||
|
||||
func (m *mkcert) makeCertFromCSR() {
|
||||
if m.caKey == nil {
|
||||
log.Fatalln("ERROR: can't create new certificates because the CA key (rootCA-key.pem) is missing")
|
||||
}
|
||||
|
||||
csrPEMBytes, err := ioutil.ReadFile(m.csrPath)
|
||||
fatalIfErr(err, "failed to read the CSR")
|
||||
csrPEM, _ := pem.Decode(csrPEMBytes)
|
||||
if csrPEM == nil {
|
||||
log.Fatalln("ERROR: failed to read the CSR: unexpected content")
|
||||
}
|
||||
if csrPEM.Type != "CERTIFICATE REQUEST" {
|
||||
log.Fatalln("ERROR: failed to read the CSR: expected CERTIFICATE REQUEST, got " + csrPEM.Type)
|
||||
}
|
||||
csr, err := x509.ParseCertificateRequest(csrPEM.Bytes)
|
||||
fatalIfErr(err, "failed to parse the CSR")
|
||||
fatalIfErr(csr.CheckSignature(), "invalid CSR signature")
|
||||
|
||||
tpl := &x509.Certificate{
|
||||
SerialNumber: randomSerialNumber(),
|
||||
Subject: csr.Subject,
|
||||
ExtraExtensions: csr.Extensions, // includes requested SANs
|
||||
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
NotBefore: time.Now(),
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
|
||||
// If the CSR does not request a SAN extension, fix it up for them as
|
||||
// the Common Name field does not work in modern browsers. Otherwise,
|
||||
// this will get overridden.
|
||||
DNSNames: []string{csr.Subject.CommonName},
|
||||
}
|
||||
|
||||
cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, csr.PublicKey, m.caKey)
|
||||
fatalIfErr(err, "failed to generate certificate")
|
||||
|
||||
var hosts []string
|
||||
hosts = append(hosts, csr.DNSNames...)
|
||||
hosts = append(hosts, csr.EmailAddresses...)
|
||||
for _, ip := range csr.IPAddresses {
|
||||
hosts = append(hosts, ip.String())
|
||||
}
|
||||
if len(hosts) == 0 {
|
||||
hosts = []string{csr.Subject.CommonName}
|
||||
}
|
||||
certFile, _, _ := m.fileNames(hosts)
|
||||
|
||||
err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
|
||||
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
|
||||
fatalIfErr(err, "failed to save certificate")
|
||||
|
||||
m.printHosts(hosts)
|
||||
|
||||
log.Printf("\nThe certificate is at \"%s\" ✅\n\n", certFile)
|
||||
}
|
||||
|
||||
// loadCA will load or create the CA at CAROOT.
|
||||
func (m *mkcert) loadCA() {
|
||||
if _, err := os.Stat(filepath.Join(m.CAROOT, rootName)); os.IsNotExist(err) {
|
||||
if !pathExists(filepath.Join(m.CAROOT, rootName)) {
|
||||
m.newCA()
|
||||
} else {
|
||||
log.Printf("Using the local CA at \"%s\" ✨\n", m.CAROOT)
|
||||
@@ -161,7 +273,7 @@ func (m *mkcert) loadCA() {
|
||||
m.caCert, err = x509.ParseCertificate(certDERBlock.Bytes)
|
||||
fatalIfErr(err, "failed to parse the CA certificate")
|
||||
|
||||
if _, err := os.Stat(filepath.Join(m.CAROOT, rootKeyName)); os.IsNotExist(err) {
|
||||
if !pathExists(filepath.Join(m.CAROOT, rootKeyName)) {
|
||||
return // keyless mode, where only -install works
|
||||
}
|
||||
|
||||
@@ -176,15 +288,11 @@ func (m *mkcert) loadCA() {
|
||||
}
|
||||
|
||||
func (m *mkcert) newCA() {
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 3072)
|
||||
priv, err := m.generateKey(true)
|
||||
fatalIfErr(err, "failed to generate the CA key")
|
||||
pub := priv.PublicKey
|
||||
pub := priv.(crypto.Signer).Public()
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
fatalIfErr(err, "failed to generate serial number")
|
||||
|
||||
spkiASN1, err := x509.MarshalPKIXPublicKey(&pub)
|
||||
spkiASN1, err := x509.MarshalPKIXPublicKey(pub)
|
||||
fatalIfErr(err, "failed to encode public key")
|
||||
|
||||
var spki struct {
|
||||
@@ -197,7 +305,7 @@ func (m *mkcert) newCA() {
|
||||
skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
|
||||
|
||||
tpl := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
SerialNumber: randomSerialNumber(),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"mkcert development CA"},
|
||||
OrganizationalUnit: []string{userAndHostname},
|
||||
@@ -219,7 +327,7 @@ func (m *mkcert) newCA() {
|
||||
MaxPathLenZero: true,
|
||||
}
|
||||
|
||||
cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, &pub, priv)
|
||||
cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, pub, priv)
|
||||
fatalIfErr(err, "failed to generate CA certificate")
|
||||
|
||||
privDER, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
|
2
go.mod
2
go.mod
@@ -1,8 +1,8 @@
|
||||
module github.com/FiloSottile/mkcert
|
||||
|
||||
require (
|
||||
github.com/DHowett/go-plist v0.0.0-20180609054337-500bd5b9081b
|
||||
golang.org/x/net v0.0.0-20180627171509-e514e69ffb8b
|
||||
golang.org/x/text v0.3.0 // indirect
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237
|
||||
)
|
||||
|
11
go.sum
11
go.sum
@@ -1,8 +1,15 @@
|
||||
github.com/DHowett/go-plist v0.0.0-20180609054337-500bd5b9081b h1:WFNhl1+1ofCWWdNFEhut77cmuMXjJYYvkEVloDdaUCI=
|
||||
github.com/DHowett/go-plist v0.0.0-20180609054337-500bd5b9081b/go.mod h1:5paT5ZDrOm8eAJPem2Bd+q3FTi3Gxm/U4tb2tH8YIUQ=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
golang.org/x/net v0.0.0-20180627171509-e514e69ffb8b h1:oXs/nlnyk1ue6g+mFGEHIuIaQIT28IgumdSIRMq2aJY=
|
||||
golang.org/x/net v0.0.0-20180627171509-e514e69ffb8b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237 h1:iAEkCBPbRaflBgZ7o9gjVUuWuvWeV4sytFWg9o+Pj2k=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ=
|
||||
|
@@ -1,3 +1,7 @@
|
||||
// Copyright 2018 The mkcert Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//+build !go1.10
|
||||
|
||||
package main
|
||||
|
152
main.go
152
main.go
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Copyright 2018 The mkcert Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -12,15 +12,19 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
const usage = `Usage of mkcert:
|
||||
const shortUsage = `Usage of mkcert:
|
||||
|
||||
$ mkcert -install
|
||||
Install the local CA in the system trust store.
|
||||
@@ -31,32 +35,72 @@ const usage = `Usage of mkcert:
|
||||
$ mkcert example.com myapp.dev localhost 127.0.0.1 ::1
|
||||
Generate "example.com+4.pem" and "example.com+4-key.pem".
|
||||
|
||||
$ mkcert "*.example.com"
|
||||
Generate "_wildcard.example.com.pem" and "_wildcard.example.com-key.pem".
|
||||
|
||||
$ mkcert -pkcs12 example.com
|
||||
Generate "example.com.p12" instead of a PEM file.
|
||||
$ mkcert "*.example.it"
|
||||
Generate "_wildcard.example.it.pem" and "_wildcard.example.it-key.pem".
|
||||
|
||||
$ mkcert -uninstall
|
||||
Uninstall the local CA (but do not delete it).
|
||||
|
||||
Use -cert-file, -key-file and -p12-file to customize the output paths.
|
||||
`
|
||||
|
||||
const advancedUsage = `Advanced options:
|
||||
|
||||
-cert-file FILE, -key-file FILE, -p12-file FILE
|
||||
Customize the output paths.
|
||||
|
||||
-client
|
||||
Generate a certificate for client authentication.
|
||||
|
||||
-ecdsa
|
||||
Generate a certificate with an ECDSA key.
|
||||
|
||||
-pkcs12
|
||||
Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,
|
||||
containing certificate and key for legacy applications.
|
||||
|
||||
-csr CSR
|
||||
Generate a certificate based on the supplied CSR. Conflicts with
|
||||
all other flags and arguments except -install and -cert-file.
|
||||
|
||||
-CAROOT
|
||||
Print the CA certificate and key storage location.
|
||||
|
||||
$CAROOT (environment variable)
|
||||
Set the CA certificate and key storage location. (This allows
|
||||
maintaining multiple local CAs in parallel.)
|
||||
|
||||
$TRUST_STORES (environment variable)
|
||||
A comma-separated list of trust stores to install the local
|
||||
root CA into. Options are: "system", "java" and "nss" (includes
|
||||
Firefox). Autodetected by default.
|
||||
|
||||
Change the CA certificate and key storage location by setting $CAROOT,
|
||||
print it with "mkcert -CAROOT".
|
||||
`
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
var installFlag = flag.Bool("install", false, "install the local root CA in the system trust store")
|
||||
var uninstallFlag = flag.Bool("uninstall", false, "uninstall the local root CA from the system trust store")
|
||||
var pkcs12Flag = flag.Bool("pkcs12", false, "generate PKCS#12 instead of PEM")
|
||||
var carootFlag = flag.Bool("CAROOT", false, "print the CAROOT path")
|
||||
var certFileFlag = flag.String("cert-file", "", "output certificate file path")
|
||||
var keyFileFlag = flag.String("key-file", "", "output key file path")
|
||||
var p12FileFlag = flag.String("p12-file", "", "output PKCS#12 file path")
|
||||
flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), usage) }
|
||||
var (
|
||||
installFlag = flag.Bool("install", false, "")
|
||||
uninstallFlag = flag.Bool("uninstall", false, "")
|
||||
pkcs12Flag = flag.Bool("pkcs12", false, "")
|
||||
ecdsaFlag = flag.Bool("ecdsa", false, "")
|
||||
clientFlag = flag.Bool("client", false, "")
|
||||
helpFlag = flag.Bool("help", false, "")
|
||||
carootFlag = flag.Bool("CAROOT", false, "")
|
||||
csrFlag = flag.String("csr", "", "")
|
||||
certFileFlag = flag.String("cert-file", "", "")
|
||||
keyFileFlag = flag.String("key-file", "", "")
|
||||
p12FileFlag = flag.String("p12-file", "", "")
|
||||
)
|
||||
flag.Usage = func() {
|
||||
fmt.Fprint(flag.CommandLine.Output(), shortUsage)
|
||||
fmt.Fprintln(flag.CommandLine.Output(), `For more options, run "mkcert -help".`)
|
||||
}
|
||||
flag.Parse()
|
||||
if *helpFlag {
|
||||
fmt.Fprint(flag.CommandLine.Output(), shortUsage)
|
||||
fmt.Fprint(flag.CommandLine.Output(), advancedUsage)
|
||||
return
|
||||
}
|
||||
if *carootFlag {
|
||||
if *installFlag || *uninstallFlag {
|
||||
log.Fatalln("ERROR: you can't set -[un]install and -CAROOT at the same time")
|
||||
@@ -67,8 +111,15 @@ func main() {
|
||||
if *installFlag && *uninstallFlag {
|
||||
log.Fatalln("ERROR: you can't set -install and -uninstall at the same time")
|
||||
}
|
||||
if *csrFlag != "" && (*pkcs12Flag || *ecdsaFlag || *clientFlag) {
|
||||
log.Fatalln("ERROR: can only combine -csr with -install and -cert-file")
|
||||
}
|
||||
if *csrFlag != "" && flag.NArg() != 0 {
|
||||
log.Fatalln("ERROR: can't specify extra arguments when using -csr")
|
||||
}
|
||||
(&mkcert{
|
||||
installMode: *installFlag, uninstallMode: *uninstallFlag, pkcs12: *pkcs12Flag,
|
||||
installMode: *installFlag, uninstallMode: *uninstallFlag, csrPath: *csrFlag,
|
||||
pkcs12: *pkcs12Flag, ecdsa: *ecdsaFlag, client: *clientFlag,
|
||||
certFile: *certFileFlag, keyFile: *keyFileFlag, p12File: *p12FileFlag,
|
||||
}).Run(flag.Args())
|
||||
}
|
||||
@@ -78,8 +129,9 @@ const rootKeyName = "rootCA-key.pem"
|
||||
|
||||
type mkcert struct {
|
||||
installMode, uninstallMode bool
|
||||
pkcs12 bool
|
||||
pkcs12, ecdsa, client bool
|
||||
keyFile, certFile, p12File string
|
||||
csrPath string
|
||||
|
||||
CAROOT string
|
||||
caCert *x509.Certificate
|
||||
@@ -109,15 +161,15 @@ func (m *mkcert) Run(args []string) {
|
||||
return
|
||||
} else {
|
||||
var warning bool
|
||||
if !m.checkPlatform() {
|
||||
if storeEnabled("system") && !m.checkPlatform() {
|
||||
warning = true
|
||||
log.Println("Warning: the local CA is not installed in the system trust store! ⚠️")
|
||||
}
|
||||
if hasNSS && CertutilInstallHelp != "" && !m.checkNSS() {
|
||||
if storeEnabled("nss") && hasNSS && CertutilInstallHelp != "" && !m.checkNSS() {
|
||||
warning = true
|
||||
log.Printf("Warning: the local CA is not installed in the %s trust store! ⚠️", NSSBrowsers)
|
||||
}
|
||||
if hasJava && !m.checkJava() {
|
||||
if storeEnabled("java") && hasJava && !m.checkJava() {
|
||||
warning = true
|
||||
log.Println("Warning: the local CA is not installed in the Java trust store! ⚠️")
|
||||
}
|
||||
@@ -126,8 +178,13 @@ func (m *mkcert) Run(args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
if m.csrPath != "" {
|
||||
m.makeCertFromCSR()
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
log.Printf("\n%s", usage)
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -136,13 +193,19 @@ func (m *mkcert) Run(args []string) {
|
||||
if ip := net.ParseIP(name); ip != nil {
|
||||
continue
|
||||
}
|
||||
if email, err := mail.ParseAddress(name); err == nil && email.Address == name {
|
||||
continue
|
||||
}
|
||||
if uriName, err := url.Parse(name); err == nil && uriName.Scheme != "" && uriName.Host != "" {
|
||||
continue
|
||||
}
|
||||
punycode, err := idna.ToASCII(name)
|
||||
if err != nil {
|
||||
log.Fatalf("ERROR: %q is not a valid hostname or IP: %s", name, err)
|
||||
log.Fatalf("ERROR: %q is not a valid hostname, IP, URL or email: %s", name, err)
|
||||
}
|
||||
args[i] = punycode
|
||||
if !hostnameRegexp.MatchString(punycode) {
|
||||
log.Fatalf("ERROR: %q is not a valid hostname or IP", name)
|
||||
log.Fatalf("ERROR: %q is not a valid hostname, IP, URL or email", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,14 +241,14 @@ func getCAROOT() string {
|
||||
|
||||
func (m *mkcert) install() {
|
||||
var printed bool
|
||||
if !m.checkPlatform() {
|
||||
if storeEnabled("system") && !m.checkPlatform() {
|
||||
if m.installPlatform() {
|
||||
log.Print("The local CA is now installed in the system trust store! ⚡️")
|
||||
}
|
||||
m.ignoreCheckFailure = true // TODO: replace with a check for a successful install
|
||||
printed = true
|
||||
}
|
||||
if hasNSS && !m.checkNSS() {
|
||||
if storeEnabled("nss") && hasNSS && !m.checkNSS() {
|
||||
if hasCertutil && m.installNSS() {
|
||||
log.Printf("The local CA is now installed in the %s trust store (requires browser restart)! 🦊", NSSBrowsers)
|
||||
} else if CertutilInstallHelp == "" {
|
||||
@@ -196,7 +259,7 @@ func (m *mkcert) install() {
|
||||
}
|
||||
printed = true
|
||||
}
|
||||
if hasJava && !m.checkJava() {
|
||||
if storeEnabled("java") && hasJava && !m.checkJava() {
|
||||
if hasKeytool {
|
||||
m.installJava()
|
||||
log.Println("The local CA is now installed in Java's trust store! ☕️")
|
||||
@@ -211,7 +274,7 @@ func (m *mkcert) install() {
|
||||
}
|
||||
|
||||
func (m *mkcert) uninstall() {
|
||||
if hasNSS {
|
||||
if storeEnabled("nss") && hasNSS {
|
||||
if hasCertutil {
|
||||
m.uninstallNSS()
|
||||
} else if CertutilInstallHelp != "" {
|
||||
@@ -221,7 +284,7 @@ func (m *mkcert) uninstall() {
|
||||
log.Print("")
|
||||
}
|
||||
}
|
||||
if hasJava {
|
||||
if storeEnabled("java") && hasJava {
|
||||
if hasKeytool {
|
||||
m.uninstallJava()
|
||||
} else {
|
||||
@@ -230,10 +293,10 @@ func (m *mkcert) uninstall() {
|
||||
log.Print("")
|
||||
}
|
||||
}
|
||||
if m.uninstallPlatform() {
|
||||
if storeEnabled("system") && m.uninstallPlatform() {
|
||||
log.Print("The local CA is now uninstalled from the system trust store(s)! 👋")
|
||||
log.Print("")
|
||||
} else if hasCertutil {
|
||||
} else if storeEnabled("nss") && hasCertutil {
|
||||
log.Printf("The local CA is now uninstalled from the %s trust store(s)! 👋", NSSBrowsers)
|
||||
log.Print("")
|
||||
}
|
||||
@@ -248,6 +311,19 @@ func (m *mkcert) checkPlatform() bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func storeEnabled(name string) bool {
|
||||
stores := os.Getenv("TRUST_STORES")
|
||||
if stores == "" {
|
||||
return true
|
||||
}
|
||||
for _, store := range strings.Split(stores, ",") {
|
||||
if store == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func fatalIfErr(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Fatalf("ERROR: %s: %s", msg, err)
|
||||
@@ -259,3 +335,13 @@ func fatalIfCmdErr(err error, cmd string, out []byte) {
|
||||
log.Fatalf("ERROR: failed to execute \"%s\": %s\n\n%s\n", cmd, err, out)
|
||||
}
|
||||
}
|
||||
|
||||
func pathExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func binaryExists(name string) bool {
|
||||
_, err := exec.LookPath(name)
|
||||
return err == nil
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Copyright 2018 The mkcert Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/DHowett/go-plist"
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Copyright 2018 The mkcert Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -39,19 +39,16 @@ func init() {
|
||||
hasJava = true
|
||||
javaHome = v
|
||||
|
||||
_, err := os.Stat(filepath.Join(v, keytoolPath))
|
||||
if err == nil {
|
||||
if pathExists(filepath.Join(v, keytoolPath)) {
|
||||
hasKeytool = true
|
||||
keytoolPath = filepath.Join(v, keytoolPath)
|
||||
}
|
||||
|
||||
_, err = os.Stat(filepath.Join(v, "lib", "security", "cacerts"))
|
||||
if err == nil {
|
||||
if pathExists(filepath.Join(v, "lib", "security", "cacerts")) {
|
||||
cacertsPath = filepath.Join(v, "lib", "security", "cacerts")
|
||||
}
|
||||
|
||||
_, err = os.Stat(filepath.Join(v, "jre", "lib", "security", "cacerts"))
|
||||
if err == nil {
|
||||
if pathExists(filepath.Join(v, "jre", "lib", "security", "cacerts")) {
|
||||
cacertsPath = filepath.Join(v, "jre", "lib", "security", "cacerts")
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Copyright 2018 The mkcert Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -16,15 +16,23 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
FirefoxProfile = os.Getenv("HOME") + "/.mozilla/firefox/*"
|
||||
CertutilInstallHelp = `apt install libnss3-tools" or "yum install nss-tools`
|
||||
NSSBrowsers = "Firefox and/or Chrome/Chromium"
|
||||
FirefoxProfile = os.Getenv("HOME") + "/.mozilla/firefox/*"
|
||||
NSSBrowsers = "Firefox and/or Chrome/Chromium"
|
||||
|
||||
SystemTrustFilename string
|
||||
SystemTrustCommand []string
|
||||
CertutilInstallHelp string
|
||||
)
|
||||
|
||||
func init() {
|
||||
switch {
|
||||
case binaryExists("apt"):
|
||||
CertutilInstallHelp = "apt install libnss3-tools"
|
||||
case binaryExists("yum"):
|
||||
CertutilInstallHelp = "yum install nss-tools"
|
||||
case binaryExists("zypper"):
|
||||
CertutilInstallHelp = "zypper install mozilla-nss-tools"
|
||||
}
|
||||
if pathExists("/etc/pki/ca-trust/source/anchors/") {
|
||||
SystemTrustFilename = "/etc/pki/ca-trust/source/anchors/%s.pem"
|
||||
SystemTrustCommand = []string{"update-ca-trust", "extract"}
|
||||
@@ -34,20 +42,15 @@ func init() {
|
||||
} else if pathExists("/etc/ca-certificates/trust-source/anchors/") {
|
||||
SystemTrustFilename = "/etc/ca-certificates/trust-source/anchors/%s.crt"
|
||||
SystemTrustCommand = []string{"trust", "extract-compat"}
|
||||
} else if pathExists("/usr/share/pki/trust/anchors") {
|
||||
SystemTrustFilename = "/usr/share/pki/trust/anchors/%s.pem"
|
||||
SystemTrustCommand = []string{"update-ca-certificates"}
|
||||
}
|
||||
if SystemTrustCommand != nil {
|
||||
_, err := exec.LookPath(SystemTrustCommand[0])
|
||||
if err != nil {
|
||||
SystemTrustCommand = nil
|
||||
}
|
||||
if SystemTrustCommand != nil && !binaryExists(SystemTrustCommand[0]) {
|
||||
SystemTrustCommand = nil
|
||||
}
|
||||
}
|
||||
|
||||
func pathExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (m *mkcert) systemTrustFilename() string {
|
||||
return fmt.Sprintf(SystemTrustFilename, strings.Replace(m.caUniqueName(), " ", "_", -1))
|
||||
}
|
||||
@@ -99,7 +102,7 @@ func (m *mkcert) uninstallPlatform() bool {
|
||||
}
|
||||
|
||||
func CommandWithSudo(cmd ...string) *exec.Cmd {
|
||||
if _, err := exec.LookPath("sudo"); err != nil {
|
||||
if !binaryExists("sudo") {
|
||||
return exec.Command(cmd[0], cmd[1:]...)
|
||||
}
|
||||
return exec.Command("sudo", append([]string{"--"}, cmd...)...)
|
||||
|
@@ -1,3 +1,7 @@
|
||||
// Copyright 2018 The mkcert Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -13,38 +17,44 @@ var (
|
||||
hasNSS bool
|
||||
hasCertutil bool
|
||||
certutilPath string
|
||||
nssDB = filepath.Join(os.Getenv("HOME"), ".pki/nssdb")
|
||||
nssDBs = []string{
|
||||
filepath.Join(os.Getenv("HOME"), ".pki/nssdb"),
|
||||
filepath.Join(os.Getenv("HOME"), "snap/chromium/current/.pki/nssdb"), // Snapcraft
|
||||
"/etc/pki/nssdb", // CentOS 7
|
||||
}
|
||||
firefoxPaths = []string{
|
||||
"/usr/bin/firefox", "/Applications/Firefox.app",
|
||||
"/Applications/Firefox Developer Edition.app",
|
||||
"/Applications/Firefox Nightly.app",
|
||||
"C:\\Program Files\\Mozilla Firefox",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, path := range []string{
|
||||
"/usr/bin/firefox", nssDB, "/Applications/Firefox.app",
|
||||
"/Applications/Firefox Developer Edition.app",
|
||||
"C:\\Program Files\\Mozilla Firefox",
|
||||
} {
|
||||
_, err := os.Stat(path)
|
||||
hasNSS = hasNSS || err == nil
|
||||
allPaths := append(append([]string{}, nssDBs...), firefoxPaths...)
|
||||
for _, path := range allPaths {
|
||||
if pathExists(path) {
|
||||
hasNSS = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
var err error
|
||||
certutilPath, err = exec.LookPath("certutil")
|
||||
if err != nil {
|
||||
var out []byte
|
||||
out, err = exec.Command("brew", "--prefix", "nss").Output()
|
||||
if err != nil {
|
||||
return
|
||||
if hasCertutil = binaryExists("certutil"); hasCertutil {
|
||||
certutilPath, _ = exec.LookPath("certutil")
|
||||
} else {
|
||||
out, err := exec.Command("brew", "--prefix", "nss").Output()
|
||||
if err == nil {
|
||||
certutilPath = filepath.Join(strings.TrimSpace(string(out)), "bin", "certutil")
|
||||
hasCertutil = pathExists(certutilPath)
|
||||
}
|
||||
certutilPath = filepath.Join(strings.TrimSpace(string(out)), "bin", "certutil")
|
||||
_, err = os.Stat(certutilPath)
|
||||
}
|
||||
hasCertutil = err == nil
|
||||
|
||||
case "linux":
|
||||
var err error
|
||||
certutilPath, err = exec.LookPath("certutil")
|
||||
hasCertutil = err == nil
|
||||
if hasCertutil = binaryExists("certutil"); hasCertutil {
|
||||
certutilPath, _ = exec.LookPath("certutil")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,22 +105,15 @@ func (m *mkcert) uninstallNSS() {
|
||||
|
||||
func (m *mkcert) forEachNSSProfile(f func(profile string)) (found int) {
|
||||
profiles, _ := filepath.Glob(FirefoxProfile)
|
||||
if _, err := os.Stat(nssDB); err == nil {
|
||||
profiles = append(profiles, nssDB)
|
||||
}
|
||||
if len(profiles) == 0 {
|
||||
return
|
||||
}
|
||||
profiles = append(profiles, nssDBs...)
|
||||
for _, profile := range profiles {
|
||||
if stat, err := os.Stat(profile); err != nil || !stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(profile, "cert9.db")); err == nil {
|
||||
if pathExists(filepath.Join(profile, "cert9.db")) {
|
||||
f("sql:" + profile)
|
||||
found++
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(profile, "cert8.db")); err == nil {
|
||||
} else if pathExists(filepath.Join(profile, "cert8.db")) {
|
||||
f("dbm:" + profile)
|
||||
found++
|
||||
}
|
||||
|
@@ -1,3 +1,7 @@
|
||||
// Copyright 2018 The mkcert Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
0
vendor/github.com/DHowett/go-plist/LICENSE → vendor/howett.net/plist/LICENSE
generated
vendored
0
vendor/github.com/DHowett/go-plist/LICENSE → vendor/howett.net/plist/LICENSE
generated
vendored
0
vendor/github.com/DHowett/go-plist/README.md → vendor/howett.net/plist/README.md
generated
vendored
0
vendor/github.com/DHowett/go-plist/README.md → vendor/howett.net/plist/README.md
generated
vendored
0
vendor/github.com/DHowett/go-plist/bplist.go → vendor/howett.net/plist/bplist.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/bplist.go → vendor/howett.net/plist/bplist.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/decode.go → vendor/howett.net/plist/decode.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/decode.go → vendor/howett.net/plist/decode.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/doc.go → vendor/howett.net/plist/doc.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/doc.go → vendor/howett.net/plist/doc.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/encode.go → vendor/howett.net/plist/encode.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/encode.go → vendor/howett.net/plist/encode.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/fuzz.go → vendor/howett.net/plist/fuzz.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/fuzz.go → vendor/howett.net/plist/fuzz.go
generated
vendored
9
vendor/howett.net/plist/go.mod
generated
vendored
Normal file
9
vendor/howett.net/plist/go.mod
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module howett.net/plist
|
||||
|
||||
require (
|
||||
// for cmd/ply
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.1
|
||||
)
|
0
vendor/github.com/DHowett/go-plist/marshal.go → vendor/howett.net/plist/marshal.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/marshal.go → vendor/howett.net/plist/marshal.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/must.go → vendor/howett.net/plist/must.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/must.go → vendor/howett.net/plist/must.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/plist.go → vendor/howett.net/plist/plist.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/plist.go → vendor/howett.net/plist/plist.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/typeinfo.go → vendor/howett.net/plist/typeinfo.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/typeinfo.go → vendor/howett.net/plist/typeinfo.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/util.go → vendor/howett.net/plist/util.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/util.go → vendor/howett.net/plist/util.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/zerocopy.go → vendor/howett.net/plist/zerocopy.go
generated
vendored
0
vendor/github.com/DHowett/go-plist/zerocopy.go → vendor/howett.net/plist/zerocopy.go
generated
vendored
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@@ -1,5 +1,3 @@
|
||||
# github.com/DHowett/go-plist v0.0.0-20180609054337-500bd5b9081b
|
||||
github.com/DHowett/go-plist
|
||||
# golang.org/x/net v0.0.0-20180627171509-e514e69ffb8b
|
||||
golang.org/x/net/idna
|
||||
# golang.org/x/text v0.3.0
|
||||
@@ -7,6 +5,8 @@ golang.org/x/text/secure/bidirule
|
||||
golang.org/x/text/unicode/bidi
|
||||
golang.org/x/text/unicode/norm
|
||||
golang.org/x/text/transform
|
||||
# howett.net/plist v0.0.0-20181124034731-591f970eefbb
|
||||
howett.net/plist
|
||||
# software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237
|
||||
software.sslmate.com/src/go-pkcs12
|
||||
software.sslmate.com/src/go-pkcs12/internal/rc2
|
||||
|
Reference in New Issue
Block a user