37 Commits

Author SHA1 Message Date
JohanGallardo
1c1dc4ed27 README: fixed broken readme link to arch linux package (#580) 2024-04-18 04:53:27 -04:00
Filippo Valsorda
2a46726ceb Drop "go vet" from CI due to false positives
See golang/go#41205
2022-04-26 19:37:25 +02:00
Filippo Valsorda
7abefc9067 Add dl.filippo.io instructions to the README
Fixes #367
2022-04-26 19:26:59 +02:00
Filippo Valsorda
d7ab78de71 Only print help when called without arguments
Fixes #401
2022-04-26 18:12:14 +02:00
Pei-Tang Huang
abde85bb26 Correct failed to save CA certificate message (#399) 2022-04-26 11:51:39 -04:00
Filippo Valsorda
e4df8ab302 Print the right hosts when a CSR doesn't have SANs
Close #344
Fixes #318
2022-04-25 20:05:46 +02:00
Filippo Valsorda
789f1b1c70 Add support for Firefox in a Snap for Ubuntu 22.04
Closes #327
Fixes #325
2022-04-25 19:56:19 +02:00
Dennis Ameling (he/him)
255b8304cd Add Windows arm64 build to CI pipeline (#396) 2022-04-25 12:58:55 -04:00
Shady Sharaf
bf27d05026 Build binaries for darwin/arm64 (#433)
Adds a binary build for macOS on ARM arch, ie: M1 processors.
2022-04-25 12:15:20 -04:00
Filippo Valsorda
4b065acd87 Update dependencies 2022-04-25 17:54:16 +02:00
Filippo Valsorda
6343246720 Update bug-report.md 2022-04-25 11:37:56 -04:00
Filippo Valsorda
61ccb55b20 .github: configure issue templates 2022-04-25 11:36:40 -04:00
Filippo Valsorda
999ca39d71 .github: add bug report ISSUE_TEMPLATE 2022-04-25 11:24:51 -04:00
Mikael Forsgren
0a3190b165 README: update Linuxbrew to Homebrew on Linux (#340)
Homebrew was formerly referred to as Linuxbrew when running on Linux or WSL.
2021-02-13 03:34:52 +01:00
Filippo Valsorda
1a5aaff12e Fix and update CI analyzers 2020-11-25 14:09:45 +01:00
Filippo Valsorda
33ca7b5c46 Switch tests and releases to GitHub Actions (#311) 2020-11-20 20:48:10 +01:00
Filippo Valsorda
9c196b6cdb Make CSR code match standard certificate generation 2020-10-27 12:54:36 +01:00
Filippo Valsorda
e28ee7df01 Simplify EKU logic
With -client, you get clientAuth. With a non-email SAN, you also get
serverAuth. With an email SAN, you also get emailProtection.
2020-10-27 12:51:28 +01:00
Filippo Valsorda
9f583c670f Drop codeSigning EKU from certificates with email SANs
Unclear how those are correlated.
If it breaks something we can put it back.

Fixes #303
2020-10-27 12:34:41 +01:00
Martin Tournoij
c12e24244a Don't overwrite the -key-file if it's identical to -cert-file (#264)
Especially for testing I find it much more convenient to just store both
the key and certificate in a single file, which works with pretty much
all software I've used.

Currently, the -cert-file will overwrite the -key-file since it uses
ioutil.WriteFile(). This fixes it to *append* if the files are
identical.

Co-authored-by: Filippo Valsorda <github@filippo.io>
2020-10-27 12:34:17 +01:00
Martin Tournoij
6649e9d2e7 Make explicit "mkcert -help" print to stdout (#265)
Currently "mkcert -help" prints to stderr, which is rather annoying as:

	$ mkcert -help | less

Gives us a blank page, as it pipes only stdout. To get any results in
less I need to use:

	$ mkcert 2>&1 | less
	$ mkcert |& less     # Non-standard bash/ zsh

Since the user explicitly asked for help with -help, it doesn't make
much sense to output it to stderr IMHO.
2020-10-26 00:55:09 +01:00
Filippo Valsorda
3fa4d18f75 Update iOS installation docs
Fixes #233
2020-10-26 00:45:44 +01:00
Filippo Valsorda
167867a226 Drop Apple 825-days rule bypass, shorten and print expiration
Closes #299
Closes #271
2020-10-26 00:25:26 +01:00
Filippo Valsorda
1ad7f56df4 Tone down the uninstalled root warnings
Some people might want to use mkcert to generate certificates without
installing the root after all.

Fixes #290
2020-10-26 00:25:26 +01:00
Filippo Valsorda
e8068235db Hide the CA path in normal operation
Most users don't need to interact with it, and it's important they don't
share the rootCA-key.pem file, so let's not lead them there in the first
place. Advanced users can still use "mkcert -CAROOT".

Fixes #282
2020-10-26 00:25:26 +01:00
Filippo Valsorda
ea8260d0d9 Accept "NEW CERTIFICATE REQUEST" PEM headers
Fixes #301
2020-10-26 00:25:26 +01:00
Mikel Kew
8e71a281f9 Support latest FirefoxDeveloperEdition.app without spaces (#280)
Latest version of Firefox Developer Edition on macOS seem to use
upper camel case naming for the app. This ensures that the CA will
be added to the Firefox trust store if using recent versions of
FF Dev Edition.
2020-10-26 00:24:53 +01:00
Dennis Ameling
c34db08bed Build release binaries for linux/arm64 (#284) 2020-10-26 00:23:20 +01:00
Filippo Valsorda
a2b1208e9c mkcert-master: remove in favor of "brew gomod filippo.io/mkcert@master"
See https://blog.filippo.io/install-go-tools-from-modules-with-brew-gomod/
2020-03-21 22:12:54 -04:00
Filippo Valsorda
d58feefc73 Fix -version output not to print spurious lines 2020-03-21 22:10:16 -04:00
Filippo Valsorda
0603a13b79 Use buildInfo.Main.Version when Version is not set 2020-03-21 22:04:37 -04:00
Filippo Valsorda
243b819761 go.mod: update import path to filippo.io/mkcert 2020-03-21 21:38:34 -04:00
Christian Rebischke
a21de51acf README: mention official Arch Linux package (#226)
Signed-off-by: Christian Rebischke <chris@nullday.de>
Co-authored-by: Filippo Valsorda <1225294+FiloSottile@users.noreply.github.com>
2019-12-26 14:56:06 +01:00
Koen Vervloesem
42a6d00604 README: add "cd mkcert" to build from source instructions (#228)
Co-authored-by: Filippo Valsorda <1225294+FiloSottile@users.noreply.github.com>
2019-12-26 14:53:10 +01:00
Senan Kelly
cb6311cfbe truststore_nss: add firefox nightly and developer edition binary paths (#225)
on my system I have only Firefox Nightly installed, so `/usr/bin/firefox` doesn't exist and so `hasNSS` was false and CA wasn't installed.

on my arch based system, the binary was at `/usr/bin/firefox-nightly`
https://aur.archlinux.org/packages/firefox-nightly/
it could also be at `/usr/bin/firefox-developer-edition`
see "package contents"
https://www.archlinux.org/packages/community/x86_64/firefox-developer-edition/
2019-11-29 18:36:50 -04:00
Nelson Martell
d8d73fcb89 README: add note about advanced options (#218)
* 📝 Add note about advaced options in README

Clarify position of advanced options argumnts (they won’t work if are placed after domain names)

Add example.

* Update README.md
2019-11-26 18:35:01 -05:00
fREW Schmidt
9b04095804 README: fix git clone command (#224) 2019-11-26 18:31:43 -05:00
16 changed files with 261 additions and 232 deletions

23
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@@ -0,0 +1,23 @@
---
name: Bug report 🐞
about: Did mkcert not work as intended? Is it broken in a certain environment?
title: ''
labels: ''
assignees: ''
---
## Environment
* Operating system (including version):
* mkcert version (from `mkcert -version`):
* Server (where the certificate is loaded):
* Client (e.g. browser, CLI tool, or script):
## What you did
<!-- Including the `mkcert -install` step and how the certificate was generated and installed. -->
## What went wrong
<!-- Please include the precise error, like a terminal transcript or a browser screenshot. -->

7
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
contact_links:
- name: Question 🙋
url: https://github.com/FiloSottile/mkcert/discussions/new?category=q-a
about: Have a question about how to use mkcert?
- name: Feature request or suggestion
url: https://github.com/FiloSottile/mkcert/discussions/new?category=ideas
about: Wish mkcert had a feature it doesn't currently have?

48
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
on:
release:
types: [published]
name: Upload Release Asset
jobs:
release:
name: Upload Release Asset
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: 1.x
- name: Checkout repository
uses: actions/checkout@v2
- name: Build binaries
run: |
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-linux-amd64" -ldflags "-X main.Version=$(git describe --tags)"
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)"
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o "mkcert-$(git describe --tags)-linux-arm64" -ldflags "-X main.Version=$(git describe --tags)"
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-darwin-amd64" -ldflags "-X main.Version=$(git describe --tags)"
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o "mkcert-$(git describe --tags)-darwin-arm64" -ldflags "-X main.Version=$(git describe --tags)"
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-windows-amd64.exe" -ldflags "-X main.Version=$(git describe --tags)"
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o "mkcert-$(git describe --tags)-windows-arm64.exe" -ldflags "-X main.Version=$(git describe --tags)"
- name: Upload release artifacts
uses: actions/github-script@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require("fs").promises;
const { repo: { owner, repo }, sha } = context;
const release = await github.repos.getReleaseByTag({
owner, repo,
tag: process.env.GITHUB_REF.replace("refs/tags/", ""),
});
console.log("Release:", { release });
for (let file of await fs.readdir(".")) {
if (!file.startsWith("mkcert-")) continue;
console.log("Uploading", file);
await github.repos.uploadReleaseAsset({
owner, repo,
release_id: release.data.id,
name: file,
data: await fs.readFile(file),
});
}

23
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
on: [push, pull_request]
name: Test
jobs:
test:
name: Go tests
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go ${{ matrix.go }}
uses: actions/setup-go@v2
with:
go-version: 1.x
- name: Checkout repository
uses: actions/checkout@v2
- name: Run staticcheck
run: |
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...
- name: Run tests
run: go test -race ./...

View File

@@ -1,24 +0,0 @@
language: go
go: stable
script:
- go run analysis.go ./...
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-linux-amd64"
-ldflags "-X main.Version=$(git describe --tags)"
- 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)"
- CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-darwin-amd64"
-ldflags "-X main.Version=$(git describe --tags)"
- CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o "mkcert-$(git describe --tags)-windows-amd64.exe"
-ldflags "-X main.Version=$(git describe --tags)"
deploy:
provider: releases
skip_cleanup: true
file_glob: true
file: "mkcert-*"
api_key:
secure: "Vd1M3TelYxwpWDe7qawK23LvSttF6xuOjwz9BQOlevidONDx/Wf2JwM8ggSt/mlvYp/FH57PX2TOWqTnVD0Hkt7Ds7CPyLSRwpgQdB8qqYBIuGBmWRXSoHc46qauWiSEJiGOO1/aqfwdROQlu/JvQyNEzzZsLsmqDIqwIOdYcauq3tYjkL48VKensGg0d8JcLuCZ+niXMOYrJa/3EdQFAij1ZlwYZt76dS/XcZ2BKhGexPOrgUaMyl9GOsd7fft71hS0aN4xDHbut5psbe1MJaGan0DUWBiCXLNjFwgFPtEHes3ddq98VVCv7iSDvO2E2nQuwoAS+pQgpPHNpiOzoomdbr/d5OGW2s2xy8tozjoQTUfUQ0wkfTG4YzFKj66xZ4Ut8+/LdN5TZvPITqQqyXJ3a3u0KSivOkjH6M+BtTQjPNY2dlPWJcufWHKVjzhHN82mHbivQyFmJZQ99RtVSr/Nx+wkKZB4nImif+CtJ3hFBdIjGQoruvmrbcC+2U6JaNGmSxxlt1/2he5KCjxsjm6S01cCsNR8ivciAuHi1LAoOza0aWELOMPQz09mvgWX92R/Bj5swgpxZoQr3MmytlTncs9S77Fv6WeUh4Em/fIRAeN8ffzK9t87DbjHgfXvdRKcOWTQbhH9wNUf0VeA58NibFktIZbQwP5088G/o4A="
on:
repo: FiloSottile/mkcert
tags: true

View File

@@ -4,12 +4,11 @@ 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" 💥
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️
The local CA is now installed in the Firefox trust store (requires browser restart)! 🦊
$ 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"
@@ -63,7 +62,7 @@ sudo pacman -S nss
sudo zypper install mozilla-nss-tools
```
Then you can install using [Linuxbrew](http://linuxbrew.sh/)
Then you can install using [Homebrew on Linux](https://docs.brew.sh/Homebrew-on-Linux)
```
brew install mkcert
@@ -72,18 +71,22 @@ brew install mkcert
or build from source (requires Go 1.13+)
```
git clone github.com/FiloSottile/mkcert
git clone https://github.com/FiloSottile/mkcert && cd mkcert
go build -ldflags "-X main.Version=$(git describe --tags)"
```
or use [the pre-built binaries](https://github.com/FiloSottile/mkcert/releases).
For Arch Linux users, mkcert is available from AUR as [`mkcert`](https://aur.archlinux.org/packages/mkcert/) or [`mkcert-git`](https://aur.archlinux.org/packages/mkcert-git/).
```
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert
```
```bash
git clone https://aur.archlinux.org/mkcert.git
cd mkcert
makepkg -si
For Arch Linux users, [`mkcert`](https://archlinux.org/packages/extra/x86_64/mkcert/) is available on the official Arch Linux repository.
```
sudo pacman -Syu mkcert
```
### Windows
@@ -144,6 +147,14 @@ To only install the local root CA into a subset of them, you can set the `TRUST_
all other flags and arguments except -install and -cert-file.
```
> **Note:** You _must_ place these options before the domain names list.
#### Example
```
mkcert -key-file key.pem -cert-file cert.pem example.com *.example.com
```
### S/MIME
mkcert automatically generates an S/MIME certificate if one of the supplied names is an email address.
@@ -156,7 +167,7 @@ mkcert filippo@example.com
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`.
On iOS, you can either use AirDrop, email the CA to yourself, or serve it from an HTTP server. After installing it, you must [enable full trust in it](https://support.apple.com/en-nz/HT204477). **Note**: earlier versions of mkcert ran into [an iOS bug](https://forums.developer.apple.com/thread/89568), if you can't see the root in "Certificate Trust Settings" you might have to update mkcert and [regenerate the root](https://github.com/FiloSottile/mkcert/issues/47#issuecomment-408724149).
On 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).
For 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).

View File

@@ -1,80 +0,0 @@
// 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 analysis
package main
import (
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/multichecker"
"golang.org/x/tools/go/analysis/passes/asmdecl"
"golang.org/x/tools/go/analysis/passes/assign"
"golang.org/x/tools/go/analysis/passes/atomic"
"golang.org/x/tools/go/analysis/passes/bools"
"golang.org/x/tools/go/analysis/passes/buildtag"
"golang.org/x/tools/go/analysis/passes/cgocall"
"golang.org/x/tools/go/analysis/passes/composite"
"golang.org/x/tools/go/analysis/passes/copylock"
"golang.org/x/tools/go/analysis/passes/errorsas"
"golang.org/x/tools/go/analysis/passes/httpresponse"
"golang.org/x/tools/go/analysis/passes/loopclosure"
"golang.org/x/tools/go/analysis/passes/lostcancel"
"golang.org/x/tools/go/analysis/passes/nilfunc"
"golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/passes/shift"
"golang.org/x/tools/go/analysis/passes/stdmethods"
"golang.org/x/tools/go/analysis/passes/structtag"
"golang.org/x/tools/go/analysis/passes/tests"
"golang.org/x/tools/go/analysis/passes/unmarshal"
"golang.org/x/tools/go/analysis/passes/unreachable"
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
"honnef.co/go/tools/simple"
"honnef.co/go/tools/staticcheck"
"honnef.co/go/tools/stylecheck"
)
func main() {
var analyzers []*analysis.Analyzer
// Add all cmd/vet analyzers.
analyzers = append(analyzers,
asmdecl.Analyzer,
assign.Analyzer,
atomic.Analyzer,
bools.Analyzer,
buildtag.Analyzer,
cgocall.Analyzer,
composite.Analyzer,
copylock.Analyzer,
errorsas.Analyzer,
httpresponse.Analyzer,
loopclosure.Analyzer,
lostcancel.Analyzer,
nilfunc.Analyzer,
printf.Analyzer,
shift.Analyzer,
stdmethods.Analyzer,
structtag.Analyzer,
tests.Analyzer,
unmarshal.Analyzer,
unreachable.Analyzer,
unsafeptr.Analyzer,
unusedresult.Analyzer)
for _, v := range simple.Analyzers {
analyzers = append(analyzers, v)
}
for _, v := range staticcheck.Analyzers {
analyzers = append(analyzers, v)
}
for _, v := range stylecheck.Analyzers {
analyzers = append(analyzers, v)
}
multichecker.Main(analyzers...)
}

96
cert.go
View File

@@ -56,6 +56,11 @@ func (m *mkcert) makeCert(hosts []string) {
fatalIfErr(err, "failed to generate certificate key")
pub := priv.(crypto.Signer).Public()
// Certificates last for 2 years and 3 months, which is always less than
// 825 days, the limit that macOS/iOS apply to all certificates,
// including custom roots. See https://support.apple.com/en-us/HT210176.
expiration := time.Now().AddDate(2, 3, 0)
tpl := &x509.Certificate{
SerialNumber: randomSerialNumber(),
Subject: pkix.Name{
@@ -63,17 +68,9 @@ func (m *mkcert) makeCert(hosts []string) {
OrganizationalUnit: []string{userAndHostname},
},
NotAfter: time.Now().AddDate(10, 0, 0),
NotBefore: time.Now(), NotAfter: expiration,
// 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,
BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
}
for _, h := range hosts {
@@ -89,12 +86,13 @@ func (m *mkcert) makeCert(hosts []string) {
}
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}
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
}
if len(tpl.IPAddresses) > 0 || len(tpl.DNSNames) > 0 || len(tpl.URIs) > 0 {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
}
if len(tpl.EmailAddresses) > 0 {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageCodeSigning, x509.ExtKeyUsageEmailProtection)
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
}
// IIS (the main target of PKCS #12 files), only shows the deprecated
@@ -109,15 +107,20 @@ func (m *mkcert) makeCert(hosts []string) {
certFile, keyFile, p12File := m.fileNames(hosts)
if !m.pkcs12 {
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
privDER, err := x509.MarshalPKCS8PrivateKey(priv)
fatalIfErr(err, "failed to encode certificate key")
err = ioutil.WriteFile(keyFile, pem.EncodeToMemory(
&pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0600)
fatalIfErr(err, "failed to save certificate key")
privPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privDER})
err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
fatalIfErr(err, "failed to save certificate")
if certFile == keyFile {
err = ioutil.WriteFile(keyFile, append(certPEM, privPEM...), 0600)
fatalIfErr(err, "failed to save certificate and key")
} else {
err = ioutil.WriteFile(certFile, certPEM, 0644)
fatalIfErr(err, "failed to save certificate")
err = ioutil.WriteFile(keyFile, privPEM, 0600)
fatalIfErr(err, "failed to save certificate key")
}
} else {
domainCert, _ := x509.ParseCertificate(cert)
pfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, "changeit")
@@ -129,11 +132,17 @@ func (m *mkcert) makeCert(hosts []string) {
m.printHosts(hosts)
if !m.pkcs12 {
log.Printf("\nThe certificate is at \"%s\" and the key at \"%s\" ✅\n\n", certFile, keyFile)
if certFile == keyFile {
log.Printf("\nThe certificate and key are at \"%s\" ✅\n\n", certFile)
} else {
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")
}
log.Printf("It will expire on %s 🗓\n\n", expiration.Format("2 January 2006"))
}
func (m *mkcert) printHosts(hosts []string) {
@@ -208,42 +217,53 @@ func (m *mkcert) makeCertFromCSR() {
if csrPEM == nil {
log.Fatalln("ERROR: failed to read the CSR: unexpected content")
}
if csrPEM.Type != "CERTIFICATE REQUEST" {
if csrPEM.Type != "CERTIFICATE REQUEST" &&
csrPEM.Type != "NEW 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")
expiration := time.Now().AddDate(2, 3, 0)
tpl := &x509.Certificate{
SerialNumber: randomSerialNumber(),
Subject: csr.Subject,
ExtraExtensions: csr.Extensions, // includes requested SANs
ExtraExtensions: csr.Extensions, // includes requested SANs, KUs and EKUs
NotAfter: time.Now().AddDate(10, 0, 0),
NotBefore: time.Now(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
NotBefore: time.Now(), NotAfter: expiration,
// 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},
// Likewise, if the CSR does not set KUs and EKUs, fix it up as Apple
// platforms require serverAuth for TLS.
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
if m.client {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
}
if len(csr.EmailAddresses) > 0 {
tpl.ExtKeyUsage = append(tpl.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
}
cert, err := x509.CreateCertificate(rand.Reader, tpl, m.caCert, csr.PublicKey, m.caKey)
fatalIfErr(err, "failed to generate certificate")
c, err := x509.ParseCertificate(cert)
fatalIfErr(err, "failed to parse generated certificate")
var hosts []string
hosts = append(hosts, csr.DNSNames...)
hosts = append(hosts, csr.EmailAddresses...)
for _, ip := range csr.IPAddresses {
hosts = append(hosts, c.DNSNames...)
hosts = append(hosts, c.EmailAddresses...)
for _, ip := range c.IPAddresses {
hosts = append(hosts, ip.String())
}
if len(hosts) == 0 {
hosts = []string{csr.Subject.CommonName}
for _, uri := range c.URIs {
hosts = append(hosts, uri.String())
}
certFile, _, _ := m.fileNames(hosts)
@@ -254,14 +274,14 @@ func (m *mkcert) makeCertFromCSR() {
m.printHosts(hosts)
log.Printf("\nThe certificate is at \"%s\" ✅\n\n", certFile)
log.Printf("It will expire on %s 🗓\n\n", expiration.Format("2 January 2006"))
}
// loadCA will load or create the CA at CAROOT.
func (m *mkcert) loadCA() {
if !pathExists(filepath.Join(m.CAROOT, rootName)) {
m.newCA()
} else {
log.Printf("Using the local CA at \"%s\" ✨\n", m.CAROOT)
}
certPEMBlock, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))
@@ -338,9 +358,9 @@ func (m *mkcert) newCA() {
err = ioutil.WriteFile(filepath.Join(m.CAROOT, rootName), pem.EncodeToMemory(
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
fatalIfErr(err, "failed to save CA key")
fatalIfErr(err, "failed to save CA certificate")
log.Printf("Created a new local CA at \"%s\" 💥\n", m.CAROOT)
log.Printf("Created a new local CA 💥\n")
}
func (m *mkcert) caUniqueName() string {

17
go.mod
View File

@@ -1,11 +1,14 @@
module github.com/FiloSottile/mkcert
module filippo.io/mkcert
go 1.13
go 1.18
require (
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11
honnef.co/go/tools v0.0.0-20191107024926-a9480a3ec3bc
howett.net/plist v0.0.0-20181124034731-591f970eefbb
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861
howett.net/plist v1.0.0
software.sslmate.com/src/go-pkcs12 v0.2.0
)
require (
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect
golang.org/x/text v0.3.7 // indirect
)

50
go.sum
View File

@@ -1,34 +1,20 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191022074931-774d2ec196ee/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8=
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20191107024926-a9480a3ec3bc h1:G3KJU7T3tdNpGfKsED8OHHsQozNxEW0rDS785ks+feY=
honnef.co/go/tools v0.0.0-20191107024926-a9480a3ec3bc/go.mod h1:bskWClgaWw7pMntzj97vj6x8S0hIhRBiTMJkNmGWTLE=
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=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=

33
main.go
View File

@@ -20,6 +20,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"runtime/debug"
"strings"
"sync"
@@ -78,10 +79,16 @@ const advancedUsage = `Advanced options:
`
// Version is set more precisely at build time.
var Version = "v1.4.1-dev"
// Version can be set at link time to override debug.BuildInfo.Main.Version,
// which is "(devel)" when building from within the module. See
// golang.org/issue/29814 and golang.org/issue/29228.
var Version string
func main() {
if len(os.Args) == 1 {
fmt.Print(shortUsage)
return
}
log.SetFlags(0)
var (
installFlag = flag.Bool("install", false, "")
@@ -103,12 +110,20 @@ func main() {
}
flag.Parse()
if *helpFlag {
fmt.Fprint(flag.CommandLine.Output(), shortUsage)
fmt.Fprint(flag.CommandLine.Output(), advancedUsage)
fmt.Print(shortUsage)
fmt.Print(advancedUsage)
return
}
if *versionFlag {
fmt.Println(Version)
if Version != "" {
fmt.Println(Version)
return
}
if buildInfo, ok := debug.ReadBuildInfo(); ok {
fmt.Println(buildInfo.Main.Version)
return
}
fmt.Println("(unknown)")
return
}
if *carootFlag {
@@ -173,18 +188,18 @@ func (m *mkcert) Run(args []string) {
var warning bool
if storeEnabled("system") && !m.checkPlatform() {
warning = true
log.Println("Warning: the local CA is not installed in the system trust store! ⚠️")
log.Println("Note: the local CA is not installed in the system trust store.")
}
if storeEnabled("nss") && hasNSS && CertutilInstallHelp != "" && !m.checkNSS() {
warning = true
log.Printf("Warning: the local CA is not installed in the %s trust store! ⚠️", NSSBrowsers)
log.Printf("Note: the local CA is not installed in the %s trust store.", NSSBrowsers)
}
if storeEnabled("java") && hasJava && !m.checkJava() {
warning = true
log.Println("Warning: the local CA is not installed in the Java trust store! ⚠️")
log.Println("Note: the local CA is not installed in the Java trust store.")
}
if warning {
log.Println("Run \"mkcert -install\" to avoid verification errors ‼")
log.Println("Run \"mkcert -install\" for certificates to be trusted automatically ⚠")
}
}

View File

@@ -1,17 +0,0 @@
# 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.
class MkcertMaster < Formula
desc "Simple tool to make locally trusted development certificates"
homepage "https://github.com/FiloSottile/mkcert"
head "https://github.com/FiloSottile/mkcert.git"
depends_on "go" => :build
def install
ENV["GOPATH"] = HOMEBREW_CACHE/"go_cache"
system "go", "build", "-o", bin/"mkcert"
prefix.install_metafiles
end
end

View File

@@ -16,7 +16,7 @@ import (
)
var (
FirefoxProfile = os.Getenv("HOME") + "/Library/Application Support/Firefox/Profiles/*"
FirefoxProfiles = []string{os.Getenv("HOME") + "/Library/Application Support/Firefox/Profiles/*"}
CertutilInstallHelp = "brew install nss"
NSSBrowsers = "Firefox"
)

View File

@@ -15,8 +15,9 @@ import (
)
var (
FirefoxProfile = os.Getenv("HOME") + "/.mozilla/firefox/*"
NSSBrowsers = "Firefox and/or Chrome/Chromium"
FirefoxProfiles = []string{os.Getenv("HOME") + "/.mozilla/firefox/*",
os.Getenv("HOME") + "/snap/firefox/common/.mozilla/firefox/*"}
NSSBrowsers = "Firefox and/or Chrome/Chromium"
SystemTrustFilename string
SystemTrustCommand []string

View File

@@ -24,7 +24,12 @@ var (
"/etc/pki/nssdb", // CentOS 7
}
firefoxPaths = []string{
"/usr/bin/firefox", "/Applications/Firefox.app",
"/usr/bin/firefox",
"/usr/bin/firefox-nightly",
"/usr/bin/firefox-developer-edition",
"/snap/firefox",
"/Applications/Firefox.app",
"/Applications/FirefoxDeveloperEdition.app",
"/Applications/Firefox Developer Edition.app",
"/Applications/Firefox Nightly.app",
"C:\\Program Files\\Mozilla Firefox",
@@ -124,8 +129,12 @@ func execCertutil(cmd *exec.Cmd) ([]byte, error) {
}
func (m *mkcert) forEachNSSProfile(f func(profile string)) (found int) {
profiles, _ := filepath.Glob(FirefoxProfile)
var profiles []string
profiles = append(profiles, nssDBs...)
for _, ff := range FirefoxProfiles {
pp, _ := filepath.Glob(ff)
profiles = append(profiles, pp...)
}
for _, profile := range profiles {
if stat, err := os.Stat(profile); err != nil || !stat.IsDir() {
continue

View File

@@ -17,7 +17,7 @@ import (
)
var (
FirefoxProfile = os.Getenv("USERPROFILE") + "\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles"
FirefoxProfiles = []string{os.Getenv("USERPROFILE") + "\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles"}
CertutilInstallHelp = "" // certutil unsupported on Windows
NSSBrowsers = "Firefox"
)
@@ -38,7 +38,7 @@ func (m *mkcert) installPlatform() bool {
fatalIfErr(err, "failed to read root certificate")
// Decode PEM
if certBlock, _ := pem.Decode(cert); certBlock == nil || certBlock.Type != "CERTIFICATE" {
fatalIfErr(fmt.Errorf("Invalid PEM data"), "decode pem")
fatalIfErr(fmt.Errorf("invalid PEM data"), "decode pem")
} else {
cert = certBlock.Bytes
}
@@ -60,7 +60,7 @@ func (m *mkcert) uninstallPlatform() bool {
// Do the deletion
deletedAny, err := store.deleteCertsWithSerial(m.caCert.SerialNumber)
if err == nil && !deletedAny {
err = fmt.Errorf("No certs found")
err = fmt.Errorf("no certs found")
}
fatalIfErr(err, "delete cert")
return true
@@ -69,11 +69,15 @@ func (m *mkcert) uninstallPlatform() bool {
type windowsRootStore uintptr
func openWindowsRootStore() (windowsRootStore, error) {
store, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("ROOT"))))
rootStr, err := syscall.UTF16PtrFromString("ROOT")
if err != nil {
return 0, err
}
store, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(rootStr)))
if store != 0 {
return windowsRootStore(store), nil
}
return 0, fmt.Errorf("Failed to open windows root store: %v", err)
return 0, fmt.Errorf("failed to open windows root store: %v", err)
}
func (w windowsRootStore) close() error {
@@ -81,7 +85,7 @@ func (w windowsRootStore) close() error {
if ret != 0 {
return nil
}
return fmt.Errorf("Failed to close windows root store: %v", err)
return fmt.Errorf("failed to close windows root store: %v", err)
}
func (w windowsRootStore) addCert(cert []byte) error {
@@ -97,7 +101,7 @@ func (w windowsRootStore) addCert(cert []byte) error {
if ret != 0 {
return nil
}
return fmt.Errorf("Failed adding cert: %v", err)
return fmt.Errorf("failed adding cert: %v", err)
}
func (w windowsRootStore) deleteCertsWithSerial(serial *big.Int) (bool, error) {
@@ -111,7 +115,7 @@ func (w windowsRootStore) deleteCertsWithSerial(serial *big.Int) (bool, error) {
if errno, ok := err.(syscall.Errno); ok && errno == 0x80092004 {
break
}
return deletedAny, fmt.Errorf("Failed enumerating certs: %v", err)
return deletedAny, fmt.Errorf("failed enumerating certs: %v", err)
}
// Parse cert
certBytes := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length]
@@ -121,10 +125,10 @@ func (w windowsRootStore) deleteCertsWithSerial(serial *big.Int) (bool, error) {
// Duplicate the context so it doesn't stop the enum when we delete it
dupCertPtr, _, err := procCertDuplicateCertificateContext.Call(uintptr(unsafe.Pointer(cert)))
if dupCertPtr == 0 {
return deletedAny, fmt.Errorf("Failed duplicating context: %v", err)
return deletedAny, fmt.Errorf("failed duplicating context: %v", err)
}
if ret, _, err := procCertDeleteCertificateFromStore.Call(dupCertPtr); ret == 0 {
return deletedAny, fmt.Errorf("Failed deleting certificate: %v", err)
return deletedAny, fmt.Errorf("failed deleting certificate: %v", err)
}
deletedAny = true
}