parent
592400aab0
commit
99e15e29f9
@ -136,6 +136,10 @@ To only install the local root CA into a subset of them, you can set the `TRUST_
|
|||||||
-pkcs12
|
-pkcs12
|
||||||
Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,
|
Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,
|
||||||
containing certificate and key for legacy applications.
|
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.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mobile devices
|
### Mobile devices
|
||||||
|
98
cert.go
98
cert.go
@ -51,12 +51,8 @@ func (m *mkcert) makeCert(hosts []string) {
|
|||||||
fatalIfErr(err, "failed to generate certificate key")
|
fatalIfErr(err, "failed to generate certificate key")
|
||||||
pub := priv.(crypto.Signer).Public()
|
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")
|
|
||||||
|
|
||||||
tpl := &x509.Certificate{
|
tpl := &x509.Certificate{
|
||||||
SerialNumber: serialNumber,
|
SerialNumber: randomSerialNumber(),
|
||||||
Subject: pkix.Name{
|
Subject: pkix.Name{
|
||||||
Organization: []string{"mkcert development certificate"},
|
Organization: []string{"mkcert development certificate"},
|
||||||
OrganizationalUnit: []string{userAndHostname},
|
OrganizationalUnit: []string{userAndHostname},
|
||||||
@ -100,7 +96,7 @@ func (m *mkcert) makeCert(hosts []string) {
|
|||||||
|
|
||||||
err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
|
err = ioutil.WriteFile(certFile, pem.EncodeToMemory(
|
||||||
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
|
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
|
||||||
fatalIfErr(err, "failed to save certificate key")
|
fatalIfErr(err, "failed to save certificate")
|
||||||
} else {
|
} else {
|
||||||
domainCert, _ := x509.ParseCertificate(cert)
|
domainCert, _ := x509.ParseCertificate(cert)
|
||||||
pfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, "changeit")
|
pfxData, err := pkcs12.Encode(rand.Reader, priv, domainCert, []*x509.Certificate{m.caCert}, "changeit")
|
||||||
@ -109,6 +105,17 @@ func (m *mkcert) makeCert(hosts []string) {
|
|||||||
fatalIfErr(err, "failed to save PKCS#12")
|
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_-]+$`)
|
secondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\*\.[0-9a-z_-]+$`)
|
||||||
log.Printf("\nCreated a new certificate valid for the following names 📜")
|
log.Printf("\nCreated a new certificate valid for the following names 📜")
|
||||||
for _, h := range hosts {
|
for _, h := range hosts {
|
||||||
@ -124,13 +131,6 @@ func (m *mkcert) makeCert(hosts []string) {
|
|||||||
break
|
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) {
|
func (m *mkcert) generateKey(rootCA bool) (crypto.PrivateKey, error) {
|
||||||
@ -166,6 +166,72 @@ func (m *mkcert) fileNames(hosts []string) (certFile, keyFile, p12File string) {
|
|||||||
return
|
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.
|
// loadCA will load or create the CA at CAROOT.
|
||||||
func (m *mkcert) loadCA() {
|
func (m *mkcert) loadCA() {
|
||||||
if _, err := os.Stat(filepath.Join(m.CAROOT, rootName)); os.IsNotExist(err) {
|
if _, err := os.Stat(filepath.Join(m.CAROOT, rootName)); os.IsNotExist(err) {
|
||||||
@ -202,10 +268,6 @@ func (m *mkcert) newCA() {
|
|||||||
fatalIfErr(err, "failed to generate the CA key")
|
fatalIfErr(err, "failed to generate the CA key")
|
||||||
pub := priv.(crypto.Signer).Public()
|
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")
|
fatalIfErr(err, "failed to encode public key")
|
||||||
|
|
||||||
@ -219,7 +281,7 @@ func (m *mkcert) newCA() {
|
|||||||
skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
|
skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
|
||||||
|
|
||||||
tpl := &x509.Certificate{
|
tpl := &x509.Certificate{
|
||||||
SerialNumber: serialNumber,
|
SerialNumber: randomSerialNumber(),
|
||||||
Subject: pkix.Name{
|
Subject: pkix.Name{
|
||||||
Organization: []string{"mkcert development CA"},
|
Organization: []string{"mkcert development CA"},
|
||||||
OrganizationalUnit: []string{userAndHostname},
|
OrganizationalUnit: []string{userAndHostname},
|
||||||
|
19
main.go
19
main.go
@ -55,6 +55,10 @@ const advancedUsage = `Advanced options:
|
|||||||
Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,
|
Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,
|
||||||
containing certificate and key for legacy applications.
|
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
|
-CAROOT
|
||||||
Print the CA certificate and key storage location.
|
Print the CA certificate and key storage location.
|
||||||
|
|
||||||
@ -79,6 +83,7 @@ func main() {
|
|||||||
clientFlag = flag.Bool("client", false, "")
|
clientFlag = flag.Bool("client", false, "")
|
||||||
helpFlag = flag.Bool("help", false, "")
|
helpFlag = flag.Bool("help", false, "")
|
||||||
carootFlag = flag.Bool("CAROOT", false, "")
|
carootFlag = flag.Bool("CAROOT", false, "")
|
||||||
|
csrFlag = flag.String("csr", "", "")
|
||||||
certFileFlag = flag.String("cert-file", "", "")
|
certFileFlag = flag.String("cert-file", "", "")
|
||||||
keyFileFlag = flag.String("key-file", "", "")
|
keyFileFlag = flag.String("key-file", "", "")
|
||||||
p12FileFlag = flag.String("p12-file", "", "")
|
p12FileFlag = flag.String("p12-file", "", "")
|
||||||
@ -103,8 +108,14 @@ func main() {
|
|||||||
if *installFlag && *uninstallFlag {
|
if *installFlag && *uninstallFlag {
|
||||||
log.Fatalln("ERROR: you can't set -install and -uninstall at the same time")
|
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{
|
(&mkcert{
|
||||||
installMode: *installFlag, uninstallMode: *uninstallFlag,
|
installMode: *installFlag, uninstallMode: *uninstallFlag, csrPath: *csrFlag,
|
||||||
pkcs12: *pkcs12Flag, ecdsa: *ecdsaFlag, client: *clientFlag,
|
pkcs12: *pkcs12Flag, ecdsa: *ecdsaFlag, client: *clientFlag,
|
||||||
certFile: *certFileFlag, keyFile: *keyFileFlag, p12File: *p12FileFlag,
|
certFile: *certFileFlag, keyFile: *keyFileFlag, p12File: *p12FileFlag,
|
||||||
}).Run(flag.Args())
|
}).Run(flag.Args())
|
||||||
@ -117,6 +128,7 @@ type mkcert struct {
|
|||||||
installMode, uninstallMode bool
|
installMode, uninstallMode bool
|
||||||
pkcs12, ecdsa, client bool
|
pkcs12, ecdsa, client bool
|
||||||
keyFile, certFile, p12File string
|
keyFile, certFile, p12File string
|
||||||
|
csrPath string
|
||||||
|
|
||||||
CAROOT string
|
CAROOT string
|
||||||
caCert *x509.Certificate
|
caCert *x509.Certificate
|
||||||
@ -163,6 +175,11 @@ func (m *mkcert) Run(args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.csrPath != "" {
|
||||||
|
m.makeCertFromCSR()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user