mirror of
https://github.com/FiloSottile/mkcert.git
synced 2025-10-14 17:01:41 +08:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
44af1b6556 | ||
![]() |
5d14daf07b | ||
![]() |
060fcce2db | ||
![]() |
6be76ae477 | ||
![]() |
53f1769ab5 | ||
![]() |
e5f9c16f8c | ||
![]() |
5fc72d92bc | ||
![]() |
561c99875b | ||
![]() |
61180c71ad | ||
![]() |
ba12bc5899 | ||
![]() |
62149df1a0 | ||
![]() |
ae768be874 | ||
![]() |
fd504a1868 | ||
![]() |
676d4cdf6b | ||
![]() |
9e258bad93 | ||
![]() |
c3bf865b0c | ||
![]() |
fdd8fe8e17 | ||
![]() |
1e7d221386 | ||
![]() |
5651e29aea | ||
![]() |
b0f7a80e95 | ||
![]() |
dbad5f86ec | ||
![]() |
779fa98126 |
21
.travis.yml
Normal file
21
.travis.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go: stable
|
||||
install: "# skip"
|
||||
|
||||
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"
|
||||
|
||||
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
|
28
CONTRIBUTING.md
Normal file
28
CONTRIBUTING.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# How to Contribute
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement. You (or your employer) retain the copyright to your contribution;
|
||||
this simply gives us permission to use and redistribute your contributions as
|
||||
part of the project. Head over to <https://cla.developers.google.com/> to see
|
||||
your current agreements on file or to sign a new one.
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Code reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. Consult
|
||||
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
|
||||
information on using pull requests.
|
||||
|
||||
## Community Guidelines
|
||||
|
||||
This project follows [Google's Open Source Community
|
||||
Guidelines](https://opensource.google.com/conduct/).
|
11
Gopkg.lock
generated
11
Gopkg.lock
generated
@@ -34,9 +34,18 @@
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "software.sslmate.com/src/go-pkcs12"
|
||||
packages = [
|
||||
".",
|
||||
"internal/rc2"
|
||||
]
|
||||
revision = "2291e8f0f237e77e89ce233be7653ecca8cf391a"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "eb8c5336b6da0643bb04cf921e8e61c2966555c879bc20533b060724d71667c6"
|
||||
inputs-digest = "af41b15413cbd854c23022d16f6da65af1235c9510e4193a17efef737de71c70"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@@ -36,3 +36,7 @@
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
name = "software.sslmate.com/src/go-pkcs12"
|
||||
branch = "master"
|
25
README.md
25
README.md
@@ -52,9 +52,28 @@ go get -u github.com/FiloSottile/mkcert
|
||||
$(go env GOPATH)/bin/mkcert
|
||||
```
|
||||
|
||||
Windows will be supported next. (PRs welcome!)
|
||||
On Arch Linux you can use your [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers) to install mkcert from the [PKGBUILD](https://aur.archlinux.org/packages/mkcert/).
|
||||
|
||||
Warning: the `rootCA-key.pem` file that mkcert automatically generates gives complete power to intercept secure requests from your machine. Do not share it.
|
||||
```
|
||||
yaourt -S mkcert
|
||||
```
|
||||
|
||||
On Windows build from source (requires Go 1.10+), or use [the pre-built binaries](https://github.com/FiloSottile/mkcert/releases).
|
||||
|
||||
> **Warning**: the `rootCA-key.pem` file that mkcert automatically generates gives complete power to intercept secure requests from your machine. Do not share it.
|
||||
|
||||
## Supported root stores
|
||||
|
||||
mkcert supports the following root stores:
|
||||
|
||||
* macOS system store
|
||||
* Windows system store
|
||||
* Linux variants that provide either
|
||||
* `update-ca-trust` (Fedora, RHEL, CentOS) or
|
||||
* `update-ca-certificates` (Ubuntu, Debian)
|
||||
* Firefox (macOS and Linux only)
|
||||
* Chrome and Chromium
|
||||
* Java (when `JAVA_HOME` is set)
|
||||
|
||||
## Advanced topics
|
||||
|
||||
@@ -62,7 +81,7 @@ Warning: the `rootCA-key.pem` file that mkcert automatically generates gives 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 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).
|
||||
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).
|
||||
|
||||
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).
|
||||
|
||||
|
55
cert.go
55
cert.go
@@ -7,8 +7,10 @@ package main
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -22,6 +24,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
)
|
||||
|
||||
var userAndHostname string
|
||||
@@ -79,15 +83,23 @@ func (m *mkcert) makeCert(hosts []string) {
|
||||
filename += "+" + strconv.Itoa(len(hosts)-1)
|
||||
}
|
||||
|
||||
privDER, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
fatalIfErr(err, "failed to encode certificate key")
|
||||
err = ioutil.WriteFile(filename+"-key.pem", pem.EncodeToMemory(
|
||||
&pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0600)
|
||||
fatalIfErr(err, "failed to save certificate key")
|
||||
if !m.pkcs12 {
|
||||
privDER, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
fatalIfErr(err, "failed to encode certificate key")
|
||||
err = ioutil.WriteFile(filename+"-key.pem", pem.EncodeToMemory(
|
||||
&pem.Block{Type: "PRIVATE KEY", Bytes: privDER}), 0600)
|
||||
fatalIfErr(err, "failed to save certificate key")
|
||||
|
||||
err = ioutil.WriteFile(filename+".pem", pem.EncodeToMemory(
|
||||
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
|
||||
fatalIfErr(err, "failed to save certificate key")
|
||||
err = ioutil.WriteFile(filename+".pem", pem.EncodeToMemory(
|
||||
&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0644)
|
||||
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")
|
||||
fatalIfErr(err, "failed to generate PKCS#12")
|
||||
err = ioutil.WriteFile(filename+".p12", pfxData, 0644)
|
||||
fatalIfErr(err, "failed to save PKCS#12")
|
||||
}
|
||||
|
||||
secondLvlWildcardRegexp := regexp.MustCompile(`(?i)^\*\.[0-9a-z_-]+$`)
|
||||
log.Printf("\nCreated a new certificate valid for the following names 📜")
|
||||
@@ -97,7 +109,12 @@ func (m *mkcert) makeCert(hosts []string) {
|
||||
log.Printf(" Warning: many browsers don't support second-level wildcards like %q ⚠️", h)
|
||||
}
|
||||
}
|
||||
log.Printf("\nThe certificate is at \"./%s.pem\" and the key at \"./%s-key.pem\" ✅\n\n", filename, filename)
|
||||
|
||||
if !m.pkcs12 {
|
||||
log.Printf("\nThe certificate is at \"./%s.pem\" and the key at \"./%s-key.pem\" ✅\n\n", filename, filename)
|
||||
} else {
|
||||
log.Printf("\nThe PKCS#12 bundle is at \"./%s.p12\" ✅\n\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// loadCA will load or create the CA at CAROOT.
|
||||
@@ -134,17 +151,36 @@ func (m *mkcert) loadCA() {
|
||||
func (m *mkcert) newCA() {
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 3072)
|
||||
fatalIfErr(err, "failed to generate the CA key")
|
||||
pub := priv.PublicKey
|
||||
|
||||
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)
|
||||
fatalIfErr(err, "failed to encode public key")
|
||||
|
||||
var spki struct {
|
||||
Algorithm pkix.AlgorithmIdentifier
|
||||
SubjectPublicKey asn1.BitString
|
||||
}
|
||||
_, err = asn1.Unmarshal(spkiASN1, &spki)
|
||||
fatalIfErr(err, "failed to decode public key")
|
||||
|
||||
skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
|
||||
|
||||
tpl := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"mkcert development CA"},
|
||||
OrganizationalUnit: []string{userAndHostname},
|
||||
|
||||
// The CommonName is required by iOS to show the certificate in the
|
||||
// "Certificate Trust Settings" menu.
|
||||
// https://github.com/FiloSottile/mkcert/issues/47
|
||||
CommonName: "mkcert " + userAndHostname,
|
||||
},
|
||||
SubjectKeyId: skid[:],
|
||||
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
NotBefore: time.Now(),
|
||||
@@ -156,7 +192,6 @@ func (m *mkcert) newCA() {
|
||||
MaxPathLenZero: true,
|
||||
}
|
||||
|
||||
pub := priv.PublicKey
|
||||
cert, err := x509.CreateCertificate(rand.Reader, tpl, tpl, &pub, priv)
|
||||
fatalIfErr(err, "failed to generate CA certificate")
|
||||
|
||||
|
101
main.go
101
main.go
@@ -20,11 +20,37 @@ import (
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
const usage = `Usage of mkcert:
|
||||
|
||||
$ mkcert -install
|
||||
Install the local CA in the system trust store.
|
||||
|
||||
$ mkcert example.org
|
||||
Generate "example.org.pem" and "example.org-key.pem".
|
||||
|
||||
$ 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 -uninstall
|
||||
Uninstall the local CA (but do not delete it).
|
||||
|
||||
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")
|
||||
flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), usage) }
|
||||
flag.Parse()
|
||||
if *carootFlag {
|
||||
if *installFlag || *uninstallFlag {
|
||||
@@ -37,7 +63,7 @@ func main() {
|
||||
log.Fatalln("ERROR: you can't set -install and -uninstall at the same time")
|
||||
}
|
||||
(&mkcert{
|
||||
installMode: *installFlag, uninstallMode: *uninstallFlag,
|
||||
installMode: *installFlag, uninstallMode: *uninstallFlag, pkcs12: *pkcs12Flag,
|
||||
}).Run(flag.Args())
|
||||
}
|
||||
|
||||
@@ -46,6 +72,7 @@ const keyName = "rootCA-key.pem"
|
||||
|
||||
type mkcert struct {
|
||||
installMode, uninstallMode bool
|
||||
pkcs12 bool
|
||||
|
||||
CAROOT string
|
||||
caCert *x509.Certificate
|
||||
@@ -79,37 +106,21 @@ func (m *mkcert) Run(args []string) {
|
||||
warning = true
|
||||
log.Println("Warning: the local CA is not installed in the system trust store! ⚠️")
|
||||
}
|
||||
if hasNSS && !m.checkNSS() {
|
||||
if 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() {
|
||||
warning = true
|
||||
log.Println("Warning: the local CA is not installed in the Java trust store! ⚠️")
|
||||
}
|
||||
if warning {
|
||||
log.Println("Run \"mkcert -install\" to avoid verification errors ‼️")
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
log.Printf(`
|
||||
Usage:
|
||||
|
||||
$ mkcert -install
|
||||
Install the local CA in the system trust store.
|
||||
|
||||
$ mkcert example.org
|
||||
Generate "example.org.pem" and "example.org-key.pem".
|
||||
|
||||
$ 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 -uninstall
|
||||
Uninstall the local CA (but do not delete it).
|
||||
|
||||
Change the CA certificate and key storage location by setting $CAROOT,
|
||||
print it with "mkcert -CAROOT".
|
||||
`)
|
||||
log.Printf("\n%s", usage)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,24 +148,23 @@ func getCAROOT() string {
|
||||
}
|
||||
|
||||
var dir string
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
switch {
|
||||
case runtime.GOOS == "windows":
|
||||
dir = os.Getenv("LocalAppData")
|
||||
case "darwin":
|
||||
case os.Getenv("XDG_DATA_HOME") != "":
|
||||
dir = os.Getenv("XDG_DATA_HOME")
|
||||
case runtime.GOOS == "darwin":
|
||||
dir = os.Getenv("HOME")
|
||||
if dir == "" {
|
||||
return ""
|
||||
}
|
||||
dir = filepath.Join(dir, "Library", "Application Support")
|
||||
default: // Unix
|
||||
dir = os.Getenv("XDG_DATA_HOME")
|
||||
dir = os.Getenv("HOME")
|
||||
if dir == "" {
|
||||
dir = os.Getenv("HOME")
|
||||
if dir == "" {
|
||||
return ""
|
||||
}
|
||||
dir = filepath.Join(dir, ".local", "share")
|
||||
return ""
|
||||
}
|
||||
dir = filepath.Join(dir, ".local", "share")
|
||||
}
|
||||
return filepath.Join(dir, "mkcert")
|
||||
}
|
||||
@@ -169,15 +179,25 @@ func (m *mkcert) install() {
|
||||
printed = true
|
||||
}
|
||||
if hasNSS && !m.checkNSS() {
|
||||
if hasCertutil {
|
||||
m.installNSS()
|
||||
if hasCertutil && m.installNSS() {
|
||||
log.Printf("The local CA is now installed in the %s trust store (requires browser restart)! 🦊", NSSBrowsers)
|
||||
} else {
|
||||
} else if CertutilInstallHelp == "" {
|
||||
log.Printf(`Note: %s support is not available on your platform. ℹ️`, NSSBrowsers)
|
||||
} else if !hasCertutil {
|
||||
log.Printf(`Warning: "certutil" is not available, so the CA can't be automatically installed in %s! ⚠️`, NSSBrowsers)
|
||||
log.Printf(`Install "certutil" with "%s" and re-run "mkcert -install" 👈`, CertutilInstallHelp)
|
||||
}
|
||||
printed = true
|
||||
}
|
||||
if hasJava && !m.checkJava() {
|
||||
if hasKeytool {
|
||||
m.installJava()
|
||||
log.Println("The local CA is now installed in Java's trust store! ☕️")
|
||||
} else {
|
||||
log.Println(`Warning: "keytool" is not available, so the CA can't be automatically installed in Java's trust store! ⚠️`)
|
||||
}
|
||||
printed = true
|
||||
}
|
||||
if printed {
|
||||
log.Print("")
|
||||
}
|
||||
@@ -187,13 +207,22 @@ func (m *mkcert) uninstall() {
|
||||
if hasNSS {
|
||||
if hasCertutil {
|
||||
m.uninstallNSS()
|
||||
} else {
|
||||
} else if CertutilInstallHelp != "" {
|
||||
log.Print("")
|
||||
log.Printf(`Warning: "certutil" is not available, so the CA can't be automatically uninstalled from %s (if it was ever installed)! ⚠️`, NSSBrowsers)
|
||||
log.Printf(`You can install "certutil" with "%s" and re-run "mkcert -uninstall" 👈`, CertutilInstallHelp)
|
||||
log.Print("")
|
||||
}
|
||||
}
|
||||
if hasJava {
|
||||
if hasKeytool {
|
||||
m.uninstallJava()
|
||||
} else {
|
||||
log.Print("")
|
||||
log.Println(`Warning: "keytool" is not available, so the CA can't be automatically uninstalled from Java's trust store (if it was ever installed)! ⚠️`)
|
||||
log.Print("")
|
||||
}
|
||||
}
|
||||
if m.uninstallPlatform() {
|
||||
log.Print("The local CA is now uninstalled from the system trust store(s)! 👋")
|
||||
log.Print("")
|
||||
|
@@ -17,7 +17,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
FirefoxPath = "/Applications/Firefox.app"
|
||||
FirefoxProfile = os.Getenv("HOME") + "/Library/Application Support/Firefox/Profiles/*"
|
||||
CertutilInstallHelp = "brew install nss"
|
||||
NSSBrowsers = "Firefox"
|
||||
|
107
truststore_java.go
Normal file
107
truststore_java.go
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2018 The Go 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 (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
hasJava bool
|
||||
hasKeytool bool
|
||||
|
||||
javaHome string
|
||||
cacertsPath string
|
||||
keytoolPath string
|
||||
storePass string = "changeit"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if v := os.Getenv("JAVA_HOME"); v != "" {
|
||||
hasJava = true
|
||||
javaHome = v
|
||||
|
||||
_, err := os.Stat(path.Join(v, "bin/keytool"))
|
||||
if err == nil {
|
||||
hasKeytool = true
|
||||
keytoolPath = path.Join(v, "bin/keytool")
|
||||
}
|
||||
|
||||
cacertsPath = path.Join(v, "jre/lib/security/cacerts")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mkcert) checkJava() bool {
|
||||
// exists returns true if the given x509.Certificate's fingerprint
|
||||
// is in the keytool -list output
|
||||
exists := func(c *x509.Certificate, h hash.Hash, keytoolOutput []byte) bool {
|
||||
h.Write(c.Raw)
|
||||
fp := strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
|
||||
return bytes.Contains(keytoolOutput, []byte(fp))
|
||||
}
|
||||
|
||||
keytoolOutput, err := exec.Command(keytoolPath, "-list", "-keystore", cacertsPath, "-storepass", storePass).CombinedOutput()
|
||||
fatalIfCmdErr(err, "keytool -list", keytoolOutput)
|
||||
// keytool outputs SHA1 and SHA256 (Java 9+) certificates in uppercase hex
|
||||
// with each octet pair delimitated by ":". Drop them from the keytool output
|
||||
keytoolOutput = bytes.Replace(keytoolOutput, []byte(":"), nil, -1)
|
||||
|
||||
// pre-Java 9 uses SHA1 fingerprints
|
||||
s1, s256 := sha1.New(), sha256.New()
|
||||
return exists(m.caCert, s1, keytoolOutput) || exists(m.caCert, s256, keytoolOutput)
|
||||
}
|
||||
|
||||
func (m *mkcert) installJava() {
|
||||
args := []string{
|
||||
"-importcert", "-noprompt",
|
||||
"-keystore", cacertsPath,
|
||||
"-storepass", storePass,
|
||||
"-file", filepath.Join(m.CAROOT, rootName),
|
||||
"-alias", m.caUniqueName(),
|
||||
}
|
||||
|
||||
out, err := m.execKeytool(exec.Command(keytoolPath, args...))
|
||||
fatalIfCmdErr(err, "keytool -importcert", out)
|
||||
}
|
||||
|
||||
func (m *mkcert) uninstallJava() {
|
||||
args := []string{
|
||||
"-delete",
|
||||
"-alias", m.caUniqueName(),
|
||||
"-keystore", cacertsPath,
|
||||
"-storepass", storePass,
|
||||
}
|
||||
out, err := m.execKeytool(exec.Command(keytoolPath, args...))
|
||||
if bytes.Contains(out, []byte("does not exist")) {
|
||||
return // cert didn't exist
|
||||
}
|
||||
fatalIfCmdErr(err, "keytool -delete", out)
|
||||
}
|
||||
|
||||
// execKeytool will execute a "keytool" command and if needed re-execute
|
||||
// the command wrapped in 'sudo' to work around file permissions.
|
||||
func (m *mkcert) execKeytool(cmd *exec.Cmd) ([]byte, error) {
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil && bytes.Contains(out, []byte("java.io.FileNotFoundException")) {
|
||||
origArgs := cmd.Args[1:]
|
||||
cmd = exec.Command("sudo", keytoolPath)
|
||||
cmd.Args = append(cmd.Args, origArgs...)
|
||||
cmd.Env = []string{
|
||||
"JAVA_HOME=" + javaHome,
|
||||
}
|
||||
out, err = cmd.CombinedOutput()
|
||||
}
|
||||
return out, err
|
||||
}
|
@@ -15,7 +15,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
FirefoxPath = "/usr/bin/firefox"
|
||||
FirefoxProfile = os.Getenv("HOME") + "/.mozilla/firefox/*"
|
||||
CertutilInstallHelp = `apt install libnss3-tools" or "yum install nss-tools`
|
||||
NSSBrowsers = "Firefox and/or Chrome/Chromium"
|
||||
@@ -26,12 +25,12 @@ var (
|
||||
|
||||
func init() {
|
||||
_, err := os.Stat("/etc/pki/ca-trust/source/anchors/")
|
||||
if !os.IsNotExist(err) {
|
||||
if err == nil {
|
||||
SystemTrustFilename = "/etc/pki/ca-trust/source/anchors/mkcert-rootCA.pem"
|
||||
SystemTrustCommand = []string{"update-ca-trust", "extract"}
|
||||
} else {
|
||||
_, err = os.Stat("/usr/local/share/ca-certificates/")
|
||||
if !os.IsNotExist(err) {
|
||||
if err == nil {
|
||||
SystemTrustFilename = "/usr/local/share/ca-certificates/mkcert-rootCA.crt"
|
||||
SystemTrustCommand = []string{"update-ca-certificates"}
|
||||
}
|
||||
|
@@ -17,8 +17,14 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
_, err := os.Stat(FirefoxPath)
|
||||
hasNSS = !os.IsNotExist(err)
|
||||
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
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
@@ -29,12 +35,10 @@ func init() {
|
||||
certutilPath = filepath.Join(strings.TrimSpace(string(out)), "bin", "certutil")
|
||||
|
||||
_, err = os.Stat(certutilPath)
|
||||
hasCertutil = !os.IsNotExist(err)
|
||||
hasCertutil = err == nil
|
||||
|
||||
case "linux":
|
||||
_, err := os.Stat(nssDB)
|
||||
hasNSS = hasNSS && !os.IsNotExist(err)
|
||||
|
||||
var err error
|
||||
certutilPath, err = exec.LookPath("certutil")
|
||||
hasCertutil = err == nil
|
||||
}
|
||||
@@ -56,24 +60,21 @@ func (m *mkcert) checkNSS() bool {
|
||||
return success
|
||||
}
|
||||
|
||||
func (m *mkcert) installNSS() {
|
||||
func (m *mkcert) installNSS() bool {
|
||||
if m.forEachNSSProfile(func(profile string) {
|
||||
cmd := exec.Command(certutilPath, "-A", "-d", profile, "-t", "C,,", "-n", m.caUniqueName(), "-i", filepath.Join(m.CAROOT, rootName))
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Printf("!!! You've hit a known issue. Please report the entire command output at https://github.com/FiloSottile/mkcert/issues/12\nProfile path: %s\nOS: %s/%s\ncertutil: %s\n", profile, runtime.GOOS, runtime.GOARCH, certutilPath)
|
||||
cmd := exec.Command("ls", "-l", profile[4:])
|
||||
cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
|
||||
cmd.Run()
|
||||
}
|
||||
fatalIfCmdErr(err, "certutil -A", out)
|
||||
}) == 0 {
|
||||
log.Printf("ERROR: no %s security databases found", NSSBrowsers)
|
||||
return false
|
||||
}
|
||||
if !m.checkNSS() {
|
||||
log.Printf("Installing in %s failed. Please report the issue with details about your environment at https://github.com/FiloSottile/mkcert/issues/new 👎", NSSBrowsers)
|
||||
log.Printf("Note that if you never started %s, you need to do that at least once.", NSSBrowsers)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *mkcert) uninstallNSS() {
|
||||
@@ -90,7 +91,7 @@ func (m *mkcert) uninstallNSS() {
|
||||
|
||||
func (m *mkcert) forEachNSSProfile(f func(profile string)) (found int) {
|
||||
profiles, _ := filepath.Glob(FirefoxProfile)
|
||||
if _, err := os.Stat(nssDB); !os.IsNotExist(err) {
|
||||
if _, err := os.Stat(nssDB); err == nil {
|
||||
profiles = append(profiles, nssDB)
|
||||
}
|
||||
if len(profiles) == 0 {
|
||||
@@ -100,13 +101,14 @@ func (m *mkcert) forEachNSSProfile(f func(profile string)) (found int) {
|
||||
if stat, err := os.Stat(profile); err != nil || !stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(profile, "cert8.db")); !os.IsNotExist(err) {
|
||||
f("dbm:" + profile)
|
||||
found++
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(profile, "cert9.db")); !os.IsNotExist(err) {
|
||||
if _, err := os.Stat(filepath.Join(profile, "cert9.db")); err == nil {
|
||||
f("sql:" + profile)
|
||||
found++
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(profile, "cert8.db")); err == nil {
|
||||
f("dbm:" + profile)
|
||||
found++
|
||||
}
|
||||
}
|
||||
return
|
||||
|
129
truststore_windows.go
Normal file
129
truststore_windows.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
FirefoxProfile = os.Getenv("USERPROFILE") + "\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles"
|
||||
CertutilInstallHelp = "" // certutil unsupported on Windows
|
||||
NSSBrowsers = "Firefox"
|
||||
)
|
||||
|
||||
var (
|
||||
modcrypt32 = syscall.NewLazyDLL("crypt32.dll")
|
||||
procCertAddEncodedCertificateToStore = modcrypt32.NewProc("CertAddEncodedCertificateToStore")
|
||||
procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
|
||||
procCertDeleteCertificateFromStore = modcrypt32.NewProc("CertDeleteCertificateFromStore")
|
||||
procCertDuplicateCertificateContext = modcrypt32.NewProc("CertDuplicateCertificateContext")
|
||||
procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore")
|
||||
procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
|
||||
)
|
||||
|
||||
func (m *mkcert) installPlatform() bool {
|
||||
// Load cert
|
||||
cert, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName))
|
||||
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")
|
||||
} else {
|
||||
cert = certBlock.Bytes
|
||||
}
|
||||
// Open root store
|
||||
store, err := openWindowsRootStore()
|
||||
fatalIfErr(err, "open root store")
|
||||
defer store.close()
|
||||
// Add cert
|
||||
fatalIfErr(store.addCert(cert), "add cert")
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *mkcert) uninstallPlatform() bool {
|
||||
// We'll just remove all certs with the same serial number
|
||||
// Open root store
|
||||
store, err := openWindowsRootStore()
|
||||
fatalIfErr(err, "open root store")
|
||||
defer store.close()
|
||||
// Do the deletion
|
||||
deletedAny, err := store.deleteCertsWithSerial(m.caCert.SerialNumber)
|
||||
if err == nil && !deletedAny {
|
||||
err = fmt.Errorf("No certs found")
|
||||
}
|
||||
fatalIfErr(err, "delete cert")
|
||||
return true
|
||||
}
|
||||
|
||||
type windowsRootStore uintptr
|
||||
|
||||
func openWindowsRootStore() (windowsRootStore, error) {
|
||||
store, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("ROOT"))))
|
||||
if store != 0 {
|
||||
return windowsRootStore(store), nil
|
||||
}
|
||||
return 0, fmt.Errorf("Failed to open windows root store: %v", err)
|
||||
}
|
||||
|
||||
func (w windowsRootStore) close() error {
|
||||
ret, _, err := procCertCloseStore.Call(uintptr(w), 0)
|
||||
if ret != 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Failed to close windows root store: %v", err)
|
||||
}
|
||||
|
||||
func (w windowsRootStore) addCert(cert []byte) error {
|
||||
// TODO: ok to always overwrite?
|
||||
ret, _, err := procCertAddEncodedCertificateToStore.Call(
|
||||
uintptr(w), // HCERTSTORE hCertStore
|
||||
uintptr(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING), // DWORD dwCertEncodingType
|
||||
uintptr(unsafe.Pointer(&cert[0])), // const BYTE *pbCertEncoded
|
||||
uintptr(len(cert)), // DWORD cbCertEncoded
|
||||
3, // DWORD dwAddDisposition (CERT_STORE_ADD_REPLACE_EXISTING is 3)
|
||||
0, // PCCERT_CONTEXT *ppCertContext
|
||||
)
|
||||
if ret != 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Failed adding cert: %v", err)
|
||||
}
|
||||
|
||||
func (w windowsRootStore) deleteCertsWithSerial(serial *big.Int) (bool, error) {
|
||||
// Go over each, deleting the ones we find
|
||||
var cert *syscall.CertContext
|
||||
deletedAny := false
|
||||
for {
|
||||
// Next enum
|
||||
certPtr, _, err := procCertEnumCertificatesInStore.Call(uintptr(w), uintptr(unsafe.Pointer(cert)))
|
||||
if cert = (*syscall.CertContext)(unsafe.Pointer(certPtr)); cert == nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == 0x80092004 {
|
||||
break
|
||||
}
|
||||
return deletedAny, fmt.Errorf("Failed enumerating certs: %v", err)
|
||||
}
|
||||
// Parse cert
|
||||
certBytes := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length]
|
||||
parsedCert, err := x509.ParseCertificate(certBytes)
|
||||
// We'll just ignore parse failures for now
|
||||
if err == nil && parsedCert.SerialNumber != nil && parsedCert.SerialNumber.Cmp(serial) == 0 {
|
||||
// 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)
|
||||
}
|
||||
if ret, _, err := procCertDeleteCertificateFromStore.Call(dupCertPtr); ret == 0 {
|
||||
return deletedAny, fmt.Errorf("Failed deleting certificate: %v", err)
|
||||
}
|
||||
deletedAny = true
|
||||
}
|
||||
}
|
||||
return deletedAny, nil
|
||||
}
|
10
vendor/software.sslmate.com/src/go-pkcs12/.gitattributes
generated
vendored
Normal file
10
vendor/software.sslmate.com/src/go-pkcs12/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Treat all files in this repo as binary, with no git magic updating
|
||||
# line endings. Windows users contributing to Go will need to use a
|
||||
# modern version of git and editors capable of LF line endings.
|
||||
#
|
||||
# We'll prevent accidental CRLF line endings from entering the repo
|
||||
# via the git-review gofmt checks.
|
||||
#
|
||||
# See golang.org/issue/9281
|
||||
|
||||
* -text
|
2
vendor/software.sslmate.com/src/go-pkcs12/.gitignore
generated
vendored
Normal file
2
vendor/software.sslmate.com/src/go-pkcs12/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Add no patterns to .hgignore except for files generated by the build.
|
||||
last-change
|
28
vendor/software.sslmate.com/src/go-pkcs12/LICENSE
generated
vendored
Normal file
28
vendor/software.sslmate.com/src/go-pkcs12/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2015, 2018 Opsmate, Inc.
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
50
vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go
generated
vendored
Normal file
50
vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2015 The Go 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 pkcs12
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// bmpString returns s encoded in UCS-2 with a zero terminator.
|
||||
func bmpString(s string) ([]byte, error) {
|
||||
// References:
|
||||
// https://tools.ietf.org/html/rfc7292#appendix-B.1
|
||||
// https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
|
||||
// - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes
|
||||
// EncodeRune returns 0xfffd if the rune does not need special encoding
|
||||
// - the above RFC provides the info that BMPStrings are NULL terminated.
|
||||
|
||||
ret := make([]byte, 0, 2*len(s)+2)
|
||||
|
||||
for _, r := range s {
|
||||
if t, _ := utf16.EncodeRune(r); t != 0xfffd {
|
||||
return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2")
|
||||
}
|
||||
ret = append(ret, byte(r/256), byte(r%256))
|
||||
}
|
||||
|
||||
return append(ret, 0, 0), nil
|
||||
}
|
||||
|
||||
func decodeBMPString(bmpString []byte) (string, error) {
|
||||
if len(bmpString)%2 != 0 {
|
||||
return "", errors.New("pkcs12: odd-length BMP string")
|
||||
}
|
||||
|
||||
// strip terminator if present
|
||||
if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 {
|
||||
bmpString = bmpString[:l-2]
|
||||
}
|
||||
|
||||
s := make([]uint16, 0, len(bmpString)/2)
|
||||
for len(bmpString) > 0 {
|
||||
s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1]))
|
||||
bmpString = bmpString[2:]
|
||||
}
|
||||
|
||||
return string(utf16.Decode(s)), nil
|
||||
}
|
172
vendor/software.sslmate.com/src/go-pkcs12/crypto.go
generated
vendored
Normal file
172
vendor/software.sslmate.com/src/go-pkcs12/crypto.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2015 The Go 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 pkcs12
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
|
||||
"software.sslmate.com/src/go-pkcs12/internal/rc2"
|
||||
)
|
||||
|
||||
var (
|
||||
oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
|
||||
oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6})
|
||||
)
|
||||
|
||||
// pbeCipher is an abstraction of a PKCS#12 cipher.
|
||||
type pbeCipher interface {
|
||||
// create returns a cipher.Block given a key.
|
||||
create(key []byte) (cipher.Block, error)
|
||||
// deriveKey returns a key derived from the given password and salt.
|
||||
deriveKey(salt, password []byte, iterations int) []byte
|
||||
// deriveKey returns an IV derived from the given password and salt.
|
||||
deriveIV(salt, password []byte, iterations int) []byte
|
||||
}
|
||||
|
||||
type shaWithTripleDESCBC struct{}
|
||||
|
||||
func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) {
|
||||
return des.NewTripleDESCipher(key)
|
||||
}
|
||||
|
||||
func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte {
|
||||
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24)
|
||||
}
|
||||
|
||||
func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte {
|
||||
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8)
|
||||
}
|
||||
|
||||
type shaWith40BitRC2CBC struct{}
|
||||
|
||||
func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) {
|
||||
return rc2.New(key, len(key)*8)
|
||||
}
|
||||
|
||||
func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte {
|
||||
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5)
|
||||
}
|
||||
|
||||
func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte {
|
||||
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8)
|
||||
}
|
||||
|
||||
type pbeParams struct {
|
||||
Salt []byte
|
||||
Iterations int
|
||||
}
|
||||
|
||||
func pbeCipherFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.Block, []byte, error) {
|
||||
var cipherType pbeCipher
|
||||
|
||||
switch {
|
||||
case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC):
|
||||
cipherType = shaWithTripleDESCBC{}
|
||||
case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC):
|
||||
cipherType = shaWith40BitRC2CBC{}
|
||||
default:
|
||||
return nil, nil, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported")
|
||||
}
|
||||
|
||||
var params pbeParams
|
||||
if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key := cipherType.deriveKey(params.Salt, password, params.Iterations)
|
||||
iv := cipherType.deriveIV(params.Salt, password, params.Iterations)
|
||||
|
||||
block, err := cipherType.create(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return block, iv, nil
|
||||
}
|
||||
|
||||
func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) {
|
||||
block, iv, err := pbeCipherFor(algorithm, password)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil
|
||||
}
|
||||
|
||||
func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) {
|
||||
cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encrypted := info.Data()
|
||||
if len(encrypted) == 0 {
|
||||
return nil, errors.New("pkcs12: empty encrypted data")
|
||||
}
|
||||
if len(encrypted)%blockSize != 0 {
|
||||
return nil, errors.New("pkcs12: input is not a multiple of the block size")
|
||||
}
|
||||
decrypted = make([]byte, len(encrypted))
|
||||
cbc.CryptBlocks(decrypted, encrypted)
|
||||
|
||||
psLen := int(decrypted[len(decrypted)-1])
|
||||
if psLen == 0 || psLen > blockSize {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
|
||||
if len(decrypted) < psLen {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
ps := decrypted[len(decrypted)-psLen:]
|
||||
decrypted = decrypted[:len(decrypted)-psLen]
|
||||
if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// decryptable abstracts an object that contains ciphertext.
|
||||
type decryptable interface {
|
||||
Algorithm() pkix.AlgorithmIdentifier
|
||||
Data() []byte
|
||||
}
|
||||
|
||||
func pbEncrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) {
|
||||
block, iv, err := pbeCipherFor(algorithm, password)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return cipher.NewCBCEncrypter(block, iv), block.BlockSize(), nil
|
||||
}
|
||||
|
||||
func pbEncrypt(info encryptable, decrypted []byte, password []byte) error {
|
||||
cbc, blockSize, err := pbEncrypterFor(info.Algorithm(), password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
psLen := blockSize - len(decrypted)%blockSize
|
||||
encrypted := make([]byte, len(decrypted)+psLen)
|
||||
copy(encrypted[:len(decrypted)], decrypted)
|
||||
copy(encrypted[len(decrypted):], bytes.Repeat([]byte{byte(psLen)}, psLen))
|
||||
cbc.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
info.SetData(encrypted)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// encryptable abstracts a object that contains ciphertext.
|
||||
type encryptable interface {
|
||||
Algorithm() pkix.AlgorithmIdentifier
|
||||
SetData([]byte)
|
||||
}
|
23
vendor/software.sslmate.com/src/go-pkcs12/errors.go
generated
vendored
Normal file
23
vendor/software.sslmate.com/src/go-pkcs12/errors.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2015 The Go 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 pkcs12
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrDecryption represents a failure to decrypt the input.
|
||||
ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding")
|
||||
|
||||
// ErrIncorrectPassword is returned when an incorrect password is detected.
|
||||
// Usually, P12/PFX data is signed to be able to verify the password.
|
||||
ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect")
|
||||
)
|
||||
|
||||
// NotImplementedError indicates that the input is not currently supported.
|
||||
type NotImplementedError string
|
||||
|
||||
func (e NotImplementedError) Error() string {
|
||||
return "pkcs12: " + string(e)
|
||||
}
|
271
vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go
generated
vendored
Normal file
271
vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright 2015 The Go 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 rc2 implements the RC2 cipher
|
||||
/*
|
||||
https://www.ietf.org/rfc/rfc2268.txt
|
||||
http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf
|
||||
|
||||
This code is licensed under the MIT license.
|
||||
*/
|
||||
package rc2
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// The rc2 block size in bytes
|
||||
const BlockSize = 8
|
||||
|
||||
type rc2Cipher struct {
|
||||
k [64]uint16
|
||||
}
|
||||
|
||||
// New returns a new rc2 cipher with the given key and effective key length t1
|
||||
func New(key []byte, t1 int) (cipher.Block, error) {
|
||||
// TODO(dgryski): error checking for key length
|
||||
return &rc2Cipher{
|
||||
k: expandKey(key, t1),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (*rc2Cipher) BlockSize() int { return BlockSize }
|
||||
|
||||
var piTable = [256]byte{
|
||||
0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
|
||||
0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
|
||||
0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
|
||||
0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
|
||||
0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
|
||||
0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
|
||||
0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
|
||||
0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
|
||||
0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
|
||||
0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
|
||||
0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
|
||||
0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
|
||||
0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
|
||||
0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
|
||||
0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
|
||||
0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad,
|
||||
}
|
||||
|
||||
func expandKey(key []byte, t1 int) [64]uint16 {
|
||||
|
||||
l := make([]byte, 128)
|
||||
copy(l, key)
|
||||
|
||||
var t = len(key)
|
||||
var t8 = (t1 + 7) / 8
|
||||
var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8))))
|
||||
|
||||
for i := len(key); i < 128; i++ {
|
||||
l[i] = piTable[l[i-1]+l[uint8(i-t)]]
|
||||
}
|
||||
|
||||
l[128-t8] = piTable[l[128-t8]&tm]
|
||||
|
||||
for i := 127 - t8; i >= 0; i-- {
|
||||
l[i] = piTable[l[i+1]^l[i+t8]]
|
||||
}
|
||||
|
||||
var k [64]uint16
|
||||
|
||||
for i := range k {
|
||||
k[i] = uint16(l[2*i]) + uint16(l[2*i+1])*256
|
||||
}
|
||||
|
||||
return k
|
||||
}
|
||||
|
||||
func rotl16(x uint16, b uint) uint16 {
|
||||
return (x >> (16 - b)) | (x << b)
|
||||
}
|
||||
|
||||
func (c *rc2Cipher) Encrypt(dst, src []byte) {
|
||||
|
||||
r0 := binary.LittleEndian.Uint16(src[0:])
|
||||
r1 := binary.LittleEndian.Uint16(src[2:])
|
||||
r2 := binary.LittleEndian.Uint16(src[4:])
|
||||
r3 := binary.LittleEndian.Uint16(src[6:])
|
||||
|
||||
var j int
|
||||
|
||||
for j <= 16 {
|
||||
// mix r0
|
||||
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
|
||||
r0 = rotl16(r0, 1)
|
||||
j++
|
||||
|
||||
// mix r1
|
||||
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
|
||||
r1 = rotl16(r1, 2)
|
||||
j++
|
||||
|
||||
// mix r2
|
||||
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
|
||||
r2 = rotl16(r2, 3)
|
||||
j++
|
||||
|
||||
// mix r3
|
||||
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
|
||||
r3 = rotl16(r3, 5)
|
||||
j++
|
||||
|
||||
}
|
||||
|
||||
r0 = r0 + c.k[r3&63]
|
||||
r1 = r1 + c.k[r0&63]
|
||||
r2 = r2 + c.k[r1&63]
|
||||
r3 = r3 + c.k[r2&63]
|
||||
|
||||
for j <= 40 {
|
||||
// mix r0
|
||||
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
|
||||
r0 = rotl16(r0, 1)
|
||||
j++
|
||||
|
||||
// mix r1
|
||||
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
|
||||
r1 = rotl16(r1, 2)
|
||||
j++
|
||||
|
||||
// mix r2
|
||||
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
|
||||
r2 = rotl16(r2, 3)
|
||||
j++
|
||||
|
||||
// mix r3
|
||||
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
|
||||
r3 = rotl16(r3, 5)
|
||||
j++
|
||||
|
||||
}
|
||||
|
||||
r0 = r0 + c.k[r3&63]
|
||||
r1 = r1 + c.k[r0&63]
|
||||
r2 = r2 + c.k[r1&63]
|
||||
r3 = r3 + c.k[r2&63]
|
||||
|
||||
for j <= 60 {
|
||||
// mix r0
|
||||
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
|
||||
r0 = rotl16(r0, 1)
|
||||
j++
|
||||
|
||||
// mix r1
|
||||
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
|
||||
r1 = rotl16(r1, 2)
|
||||
j++
|
||||
|
||||
// mix r2
|
||||
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
|
||||
r2 = rotl16(r2, 3)
|
||||
j++
|
||||
|
||||
// mix r3
|
||||
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
|
||||
r3 = rotl16(r3, 5)
|
||||
j++
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint16(dst[0:], r0)
|
||||
binary.LittleEndian.PutUint16(dst[2:], r1)
|
||||
binary.LittleEndian.PutUint16(dst[4:], r2)
|
||||
binary.LittleEndian.PutUint16(dst[6:], r3)
|
||||
}
|
||||
|
||||
func (c *rc2Cipher) Decrypt(dst, src []byte) {
|
||||
|
||||
r0 := binary.LittleEndian.Uint16(src[0:])
|
||||
r1 := binary.LittleEndian.Uint16(src[2:])
|
||||
r2 := binary.LittleEndian.Uint16(src[4:])
|
||||
r3 := binary.LittleEndian.Uint16(src[6:])
|
||||
|
||||
j := 63
|
||||
|
||||
for j >= 44 {
|
||||
// unmix r3
|
||||
r3 = rotl16(r3, 16-5)
|
||||
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
|
||||
j--
|
||||
|
||||
// unmix r2
|
||||
r2 = rotl16(r2, 16-3)
|
||||
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
|
||||
j--
|
||||
|
||||
// unmix r1
|
||||
r1 = rotl16(r1, 16-2)
|
||||
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
|
||||
j--
|
||||
|
||||
// unmix r0
|
||||
r0 = rotl16(r0, 16-1)
|
||||
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
|
||||
j--
|
||||
}
|
||||
|
||||
r3 = r3 - c.k[r2&63]
|
||||
r2 = r2 - c.k[r1&63]
|
||||
r1 = r1 - c.k[r0&63]
|
||||
r0 = r0 - c.k[r3&63]
|
||||
|
||||
for j >= 20 {
|
||||
// unmix r3
|
||||
r3 = rotl16(r3, 16-5)
|
||||
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
|
||||
j--
|
||||
|
||||
// unmix r2
|
||||
r2 = rotl16(r2, 16-3)
|
||||
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
|
||||
j--
|
||||
|
||||
// unmix r1
|
||||
r1 = rotl16(r1, 16-2)
|
||||
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
|
||||
j--
|
||||
|
||||
// unmix r0
|
||||
r0 = rotl16(r0, 16-1)
|
||||
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
|
||||
j--
|
||||
|
||||
}
|
||||
|
||||
r3 = r3 - c.k[r2&63]
|
||||
r2 = r2 - c.k[r1&63]
|
||||
r1 = r1 - c.k[r0&63]
|
||||
r0 = r0 - c.k[r3&63]
|
||||
|
||||
for j >= 0 {
|
||||
// unmix r3
|
||||
r3 = rotl16(r3, 16-5)
|
||||
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
|
||||
j--
|
||||
|
||||
// unmix r2
|
||||
r2 = rotl16(r2, 16-3)
|
||||
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
|
||||
j--
|
||||
|
||||
// unmix r1
|
||||
r1 = rotl16(r1, 16-2)
|
||||
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
|
||||
j--
|
||||
|
||||
// unmix r0
|
||||
r0 = rotl16(r0, 16-1)
|
||||
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
|
||||
j--
|
||||
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint16(dst[0:], r0)
|
||||
binary.LittleEndian.PutUint16(dst[2:], r1)
|
||||
binary.LittleEndian.PutUint16(dst[4:], r2)
|
||||
binary.LittleEndian.PutUint16(dst[6:], r3)
|
||||
}
|
59
vendor/software.sslmate.com/src/go-pkcs12/mac.go
generated
vendored
Normal file
59
vendor/software.sslmate.com/src/go-pkcs12/mac.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2015 The Go 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 pkcs12
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
)
|
||||
|
||||
type macData struct {
|
||||
Mac digestInfo
|
||||
MacSalt []byte
|
||||
Iterations int `asn1:"optional,default:1"`
|
||||
}
|
||||
|
||||
// from PKCS#7:
|
||||
type digestInfo struct {
|
||||
Algorithm pkix.AlgorithmIdentifier
|
||||
Digest []byte
|
||||
}
|
||||
|
||||
var (
|
||||
oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26})
|
||||
)
|
||||
|
||||
func verifyMac(macData *macData, message, password []byte) error {
|
||||
if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) {
|
||||
return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String())
|
||||
}
|
||||
|
||||
key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20)
|
||||
|
||||
mac := hmac.New(sha1.New, key)
|
||||
mac.Write(message)
|
||||
expectedMAC := mac.Sum(nil)
|
||||
|
||||
if !hmac.Equal(macData.Mac.Digest, expectedMAC) {
|
||||
return ErrIncorrectPassword
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeMac(macData *macData, message, password []byte) error {
|
||||
if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) {
|
||||
return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String())
|
||||
}
|
||||
|
||||
key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20)
|
||||
|
||||
mac := hmac.New(sha1.New, key)
|
||||
mac.Write(message)
|
||||
macData.Mac.Digest = mac.Sum(nil)
|
||||
|
||||
return nil
|
||||
}
|
170
vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go
generated
vendored
Normal file
170
vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
// Copyright 2015 The Go 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 pkcs12
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var (
|
||||
one = big.NewInt(1)
|
||||
)
|
||||
|
||||
// sha1Sum returns the SHA-1 hash of in.
|
||||
func sha1Sum(in []byte) []byte {
|
||||
sum := sha1.Sum(in)
|
||||
return sum[:]
|
||||
}
|
||||
|
||||
// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of
|
||||
// repeats of pattern.
|
||||
func fillWithRepeats(pattern []byte, v int) []byte {
|
||||
if len(pattern) == 0 {
|
||||
return nil
|
||||
}
|
||||
outputLen := v * ((len(pattern) + v - 1) / v)
|
||||
return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen]
|
||||
}
|
||||
|
||||
func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) {
|
||||
// implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments
|
||||
|
||||
// Let H be a hash function built around a compression function f:
|
||||
|
||||
// Z_2^u x Z_2^v -> Z_2^u
|
||||
|
||||
// (that is, H has a chaining variable and output of length u bits, and
|
||||
// the message input to the compression function of H is v bits). The
|
||||
// values for u and v are as follows:
|
||||
|
||||
// HASH FUNCTION VALUE u VALUE v
|
||||
// MD2, MD5 128 512
|
||||
// SHA-1 160 512
|
||||
// SHA-224 224 512
|
||||
// SHA-256 256 512
|
||||
// SHA-384 384 1024
|
||||
// SHA-512 512 1024
|
||||
// SHA-512/224 224 1024
|
||||
// SHA-512/256 256 1024
|
||||
|
||||
// Furthermore, let r be the iteration count.
|
||||
|
||||
// We assume here that u and v are both multiples of 8, as are the
|
||||
// lengths of the password and salt strings (which we denote by p and s,
|
||||
// respectively) and the number n of pseudorandom bits required. In
|
||||
// addition, u and v are of course non-zero.
|
||||
|
||||
// For information on security considerations for MD5 [19], see [25] and
|
||||
// [1], and on those for MD2, see [18].
|
||||
|
||||
// The following procedure can be used to produce pseudorandom bits for
|
||||
// a particular "purpose" that is identified by a byte called "ID".
|
||||
// This standard specifies 3 different values for the ID byte:
|
||||
|
||||
// 1. If ID=1, then the pseudorandom bits being produced are to be used
|
||||
// as key material for performing encryption or decryption.
|
||||
|
||||
// 2. If ID=2, then the pseudorandom bits being produced are to be used
|
||||
// as an IV (Initial Value) for encryption or decryption.
|
||||
|
||||
// 3. If ID=3, then the pseudorandom bits being produced are to be used
|
||||
// as an integrity key for MACing.
|
||||
|
||||
// 1. Construct a string, D (the "diversifier"), by concatenating v/8
|
||||
// copies of ID.
|
||||
var D []byte
|
||||
for i := 0; i < v; i++ {
|
||||
D = append(D, ID)
|
||||
}
|
||||
|
||||
// 2. Concatenate copies of the salt together to create a string S of
|
||||
// length v(ceiling(s/v)) bits (the final copy of the salt may be
|
||||
// truncated to create S). Note that if the salt is the empty
|
||||
// string, then so is S.
|
||||
|
||||
S := fillWithRepeats(salt, v)
|
||||
|
||||
// 3. Concatenate copies of the password together to create a string P
|
||||
// of length v(ceiling(p/v)) bits (the final copy of the password
|
||||
// may be truncated to create P). Note that if the password is the
|
||||
// empty string, then so is P.
|
||||
|
||||
P := fillWithRepeats(password, v)
|
||||
|
||||
// 4. Set I=S||P to be the concatenation of S and P.
|
||||
I := append(S, P...)
|
||||
|
||||
// 5. Set c=ceiling(n/u).
|
||||
c := (size + u - 1) / u
|
||||
|
||||
// 6. For i=1, 2, ..., c, do the following:
|
||||
A := make([]byte, c*20)
|
||||
var IjBuf []byte
|
||||
for i := 0; i < c; i++ {
|
||||
// A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1,
|
||||
// H(H(H(... H(D||I))))
|
||||
Ai := hash(append(D, I...))
|
||||
for j := 1; j < r; j++ {
|
||||
Ai = hash(Ai)
|
||||
}
|
||||
copy(A[i*20:], Ai[:])
|
||||
|
||||
if i < c-1 { // skip on last iteration
|
||||
// B. Concatenate copies of Ai to create a string B of length v
|
||||
// bits (the final copy of Ai may be truncated to create B).
|
||||
var B []byte
|
||||
for len(B) < v {
|
||||
B = append(B, Ai[:]...)
|
||||
}
|
||||
B = B[:v]
|
||||
|
||||
// C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit
|
||||
// blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by
|
||||
// setting I_j=(I_j+B+1) mod 2^v for each j.
|
||||
{
|
||||
Bbi := new(big.Int).SetBytes(B)
|
||||
Ij := new(big.Int)
|
||||
|
||||
for j := 0; j < len(I)/v; j++ {
|
||||
Ij.SetBytes(I[j*v : (j+1)*v])
|
||||
Ij.Add(Ij, Bbi)
|
||||
Ij.Add(Ij, one)
|
||||
Ijb := Ij.Bytes()
|
||||
// We expect Ijb to be exactly v bytes,
|
||||
// if it is longer or shorter we must
|
||||
// adjust it accordingly.
|
||||
if len(Ijb) > v {
|
||||
Ijb = Ijb[len(Ijb)-v:]
|
||||
}
|
||||
if len(Ijb) < v {
|
||||
if IjBuf == nil {
|
||||
IjBuf = make([]byte, v)
|
||||
}
|
||||
bytesShort := v - len(Ijb)
|
||||
for i := 0; i < bytesShort; i++ {
|
||||
IjBuf[i] = 0
|
||||
}
|
||||
copy(IjBuf[bytesShort:], Ijb)
|
||||
Ijb = IjBuf
|
||||
}
|
||||
copy(I[j*v:(j+1)*v], Ijb)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom
|
||||
// bit string, A.
|
||||
|
||||
// 8. Use the first n bits of A as the output of this entire process.
|
||||
return A[:size]
|
||||
|
||||
// If the above process is being used to generate a DES key, the process
|
||||
// should be used to create 64 random bits, and the key's parity bits
|
||||
// should be set after the 64 bits have been produced. Similar concerns
|
||||
// hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any
|
||||
// similar keys with parity bits "built into them".
|
||||
}
|
501
vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go
generated
vendored
Normal file
501
vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go
generated
vendored
Normal file
@@ -0,0 +1,501 @@
|
||||
// Copyright 2015 The Go 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 pkcs12 implements some of PKCS#12.
|
||||
//
|
||||
// This implementation is distilled from https://tools.ietf.org/html/rfc7292
|
||||
// and referenced documents. It is intended for decoding P12/PFX-stored
|
||||
// certificates and keys for use with the crypto/tls package.
|
||||
package pkcs12
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1})
|
||||
oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6})
|
||||
|
||||
oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20})
|
||||
oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21})
|
||||
oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1})
|
||||
)
|
||||
|
||||
type pfxPdu struct {
|
||||
Version int
|
||||
AuthSafe contentInfo
|
||||
MacData macData `asn1:"optional"`
|
||||
}
|
||||
|
||||
type contentInfo struct {
|
||||
ContentType asn1.ObjectIdentifier
|
||||
Content asn1.RawValue `asn1:"tag:0,explicit,optional"`
|
||||
}
|
||||
|
||||
type encryptedData struct {
|
||||
Version int
|
||||
EncryptedContentInfo encryptedContentInfo
|
||||
}
|
||||
|
||||
type encryptedContentInfo struct {
|
||||
ContentType asn1.ObjectIdentifier
|
||||
ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedContent []byte `asn1:"tag:0,optional"`
|
||||
}
|
||||
|
||||
func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier {
|
||||
return i.ContentEncryptionAlgorithm
|
||||
}
|
||||
|
||||
func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent }
|
||||
|
||||
func (i *encryptedContentInfo) SetData(data []byte) { i.EncryptedContent = data }
|
||||
|
||||
type safeBag struct {
|
||||
Id asn1.ObjectIdentifier
|
||||
Value asn1.RawValue `asn1:"tag:0,explicit"`
|
||||
Attributes []pkcs12Attribute `asn1:"set,optional"`
|
||||
}
|
||||
|
||||
type pkcs12Attribute struct {
|
||||
Id asn1.ObjectIdentifier
|
||||
Value asn1.RawValue `asn1:"set"`
|
||||
}
|
||||
|
||||
type encryptedPrivateKeyInfo struct {
|
||||
AlgorithmIdentifier pkix.AlgorithmIdentifier
|
||||
EncryptedData []byte
|
||||
}
|
||||
|
||||
func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier {
|
||||
return i.AlgorithmIdentifier
|
||||
}
|
||||
|
||||
func (i encryptedPrivateKeyInfo) Data() []byte {
|
||||
return i.EncryptedData
|
||||
}
|
||||
|
||||
func (i *encryptedPrivateKeyInfo) SetData(data []byte) {
|
||||
i.EncryptedData = data
|
||||
}
|
||||
|
||||
// PEM block types
|
||||
const (
|
||||
certificateType = "CERTIFICATE"
|
||||
privateKeyType = "PRIVATE KEY"
|
||||
)
|
||||
|
||||
// unmarshal calls asn1.Unmarshal, but also returns an error if there is any
|
||||
// trailing data after unmarshaling.
|
||||
func unmarshal(in []byte, out interface{}) error {
|
||||
trailing, err := asn1.Unmarshal(in, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(trailing) != 0 {
|
||||
return errors.New("pkcs12: trailing data found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks.
|
||||
func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) {
|
||||
encodedPassword, err := bmpString(password)
|
||||
if err != nil {
|
||||
return nil, ErrIncorrectPassword
|
||||
}
|
||||
|
||||
bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocks := make([]*pem.Block, 0, len(bags))
|
||||
for _, bag := range bags {
|
||||
block, err := convertBag(&bag, encodedPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
func convertBag(bag *safeBag, password []byte) (*pem.Block, error) {
|
||||
block := &pem.Block{
|
||||
Headers: make(map[string]string),
|
||||
}
|
||||
|
||||
for _, attribute := range bag.Attributes {
|
||||
k, v, err := convertAttribute(&attribute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block.Headers[k] = v
|
||||
}
|
||||
|
||||
switch {
|
||||
case bag.Id.Equal(oidCertBag):
|
||||
block.Type = certificateType
|
||||
certsData, err := decodeCertBag(bag.Value.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block.Bytes = certsData
|
||||
case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
|
||||
block.Type = privateKeyType
|
||||
|
||||
key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch key := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
block.Bytes = x509.MarshalPKCS1PrivateKey(key)
|
||||
case *ecdsa.PrivateKey:
|
||||
block.Bytes, err = x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String())
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) {
|
||||
isString := false
|
||||
|
||||
switch {
|
||||
case attribute.Id.Equal(oidFriendlyName):
|
||||
key = "friendlyName"
|
||||
isString = true
|
||||
case attribute.Id.Equal(oidLocalKeyID):
|
||||
key = "localKeyId"
|
||||
case attribute.Id.Equal(oidMicrosoftCSPName):
|
||||
// This key is chosen to match OpenSSL.
|
||||
key = "Microsoft CSP Name"
|
||||
isString = true
|
||||
default:
|
||||
return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String())
|
||||
}
|
||||
|
||||
if isString {
|
||||
if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if value, err = decodeBMPString(attribute.Value.Bytes); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
} else {
|
||||
var id []byte
|
||||
if err := unmarshal(attribute.Value.Bytes, &id); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
value = hex.EncodeToString(id)
|
||||
}
|
||||
|
||||
return key, value, nil
|
||||
}
|
||||
|
||||
// Decode extracts a certificate and private key from pfxData. This function
|
||||
// assumes that there is only one certificate and only one private key in the
|
||||
// pfxData.
|
||||
func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) {
|
||||
encodedPassword, err := bmpString(password)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(bags) != 2 {
|
||||
err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU")
|
||||
return
|
||||
}
|
||||
|
||||
for _, bag := range bags {
|
||||
switch {
|
||||
case bag.Id.Equal(oidCertBag):
|
||||
if certificate != nil {
|
||||
err = errors.New("pkcs12: expected exactly one certificate bag")
|
||||
}
|
||||
|
||||
certsData, err := decodeCertBag(bag.Value.Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
certs, err := x509.ParseCertificates(certsData)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(certs) != 1 {
|
||||
err = errors.New("pkcs12: expected exactly one certificate in the certBag")
|
||||
return nil, nil, err
|
||||
}
|
||||
certificate = certs[0]
|
||||
|
||||
case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
|
||||
if privateKey != nil {
|
||||
err = errors.New("pkcs12: expected exactly one key bag")
|
||||
}
|
||||
|
||||
if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if certificate == nil {
|
||||
return nil, nil, errors.New("pkcs12: certificate missing")
|
||||
}
|
||||
if privateKey == nil {
|
||||
return nil, nil, errors.New("pkcs12: private key missing")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) {
|
||||
pfx := new(pfxPdu)
|
||||
if err := unmarshal(p12Data, pfx); err != nil {
|
||||
return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error())
|
||||
}
|
||||
|
||||
if pfx.Version != 3 {
|
||||
return nil, nil, NotImplementedError("can only decode v3 PFX PDU's")
|
||||
}
|
||||
|
||||
if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) {
|
||||
return nil, nil, NotImplementedError("only password-protected PFX is implemented")
|
||||
}
|
||||
|
||||
// unmarshal the explicit bytes in the content for type 'data'
|
||||
if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 {
|
||||
return nil, nil, errors.New("pkcs12: no MAC in data")
|
||||
}
|
||||
|
||||
if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil {
|
||||
if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 {
|
||||
// some implementations use an empty byte array
|
||||
// for the empty string password try one more
|
||||
// time with empty-empty password
|
||||
password = nil
|
||||
err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var authenticatedSafe []contentInfo
|
||||
if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(authenticatedSafe) != 2 {
|
||||
return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe")
|
||||
}
|
||||
|
||||
for _, ci := range authenticatedSafe {
|
||||
var data []byte
|
||||
|
||||
switch {
|
||||
case ci.ContentType.Equal(oidDataContentType):
|
||||
if err := unmarshal(ci.Content.Bytes, &data); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
case ci.ContentType.Equal(oidEncryptedDataContentType):
|
||||
var encryptedData encryptedData
|
||||
if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if encryptedData.Version != 0 {
|
||||
return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported")
|
||||
}
|
||||
if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe")
|
||||
}
|
||||
|
||||
var safeContents []safeBag
|
||||
if err := unmarshal(data, &safeContents); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bags = append(bags, safeContents...)
|
||||
}
|
||||
|
||||
return bags, password, nil
|
||||
}
|
||||
|
||||
// Encode produces pfxData containing one private key, an end-entity certificate, and any number of CA certificates.
|
||||
// It emulates the behavior of OpenSSL's PKCS12_create: it creates two SafeContents: one that's encrypted with RC2
|
||||
// and contains the certificates, and another that is unencrypted and contains the private key shrouded with 3DES.
|
||||
// The private key bag and the end-entity certificate bag have the LocalKeyId attribute set to the SHA-1 fingerprint
|
||||
// of the end-entity certificate.
|
||||
func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) {
|
||||
encodedPassword, err := bmpString(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pfx pfxPdu
|
||||
pfx.Version = 3
|
||||
|
||||
var certFingerprint = sha1.Sum(certificate.Raw)
|
||||
var localKeyIdAttr pkcs12Attribute
|
||||
localKeyIdAttr.Id = oidLocalKeyID
|
||||
localKeyIdAttr.Value.Class = 0
|
||||
localKeyIdAttr.Value.Tag = 17
|
||||
localKeyIdAttr.Value.IsCompound = true
|
||||
if localKeyIdAttr.Value.Bytes, err = asn1.Marshal(certFingerprint[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var certBags []safeBag
|
||||
var certBag *safeBag
|
||||
if certBag, err = makeCertBag(certificate.Raw, []pkcs12Attribute{localKeyIdAttr}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certBags = append(certBags, *certBag)
|
||||
|
||||
for _, cert := range caCerts {
|
||||
if certBag, err = makeCertBag(cert.Raw, []pkcs12Attribute{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certBags = append(certBags, *certBag)
|
||||
}
|
||||
|
||||
var keyBag safeBag
|
||||
keyBag.Id = oidPKCS8ShroundedKeyBag
|
||||
keyBag.Value.Class = 2
|
||||
keyBag.Value.Tag = 0
|
||||
keyBag.Value.IsCompound = true
|
||||
if keyBag.Value.Bytes, err = encodePkcs8ShroudedKeyBag(rand, privateKey, encodedPassword); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyBag.Attributes = append(keyBag.Attributes, localKeyIdAttr)
|
||||
|
||||
// Construct an authenticated safe with two SafeContents.
|
||||
// The first SafeContents is encrypted and contains the cert bags.
|
||||
// The second SafeContents is unencrypted and contains the shrouded key bag.
|
||||
var authenticatedSafe [2]contentInfo
|
||||
if authenticatedSafe[0], err = makeSafeContents(rand, certBags, encodedPassword); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if authenticatedSafe[1], err = makeSafeContents(rand, []safeBag{keyBag}, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var authenticatedSafeBytes []byte
|
||||
if authenticatedSafeBytes, err = asn1.Marshal(authenticatedSafe[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// compute the MAC
|
||||
pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1
|
||||
pfx.MacData.MacSalt = make([]byte, 8)
|
||||
if _, err = rand.Read(pfx.MacData.MacSalt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pfx.MacData.Iterations = 1
|
||||
if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pfx.AuthSafe.ContentType = oidDataContentType
|
||||
pfx.AuthSafe.Content.Class = 2
|
||||
pfx.AuthSafe.Content.Tag = 0
|
||||
pfx.AuthSafe.Content.IsCompound = true
|
||||
if pfx.AuthSafe.Content.Bytes, err = asn1.Marshal(authenticatedSafeBytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pfxData, err = asn1.Marshal(pfx); err != nil {
|
||||
return nil, errors.New("pkcs12: error writing P12 data: " + err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func makeCertBag(certBytes []byte, attributes []pkcs12Attribute) (certBag *safeBag, err error) {
|
||||
certBag = new(safeBag)
|
||||
certBag.Id = oidCertBag
|
||||
certBag.Value.Class = 2
|
||||
certBag.Value.Tag = 0
|
||||
certBag.Value.IsCompound = true
|
||||
if certBag.Value.Bytes, err = encodeCertBag(certBytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certBag.Attributes = attributes
|
||||
return
|
||||
}
|
||||
|
||||
func makeSafeContents(rand io.Reader, bags []safeBag, password []byte) (ci contentInfo, err error) {
|
||||
var data []byte
|
||||
if data, err = asn1.Marshal(bags); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if password == nil {
|
||||
ci.ContentType = oidDataContentType
|
||||
ci.Content.Class = 2
|
||||
ci.Content.Tag = 0
|
||||
ci.Content.IsCompound = true
|
||||
if ci.Content.Bytes, err = asn1.Marshal(data); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
randomSalt := make([]byte, 8)
|
||||
if _, err = rand.Read(randomSalt); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var algo pkix.AlgorithmIdentifier
|
||||
algo.Algorithm = oidPBEWithSHAAnd40BitRC2CBC
|
||||
if algo.Parameters.FullBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var encryptedData encryptedData
|
||||
encryptedData.Version = 0
|
||||
encryptedData.EncryptedContentInfo.ContentType = oidDataContentType
|
||||
encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm = algo
|
||||
if err = pbEncrypt(&encryptedData.EncryptedContentInfo, data, password); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ci.ContentType = oidEncryptedDataContentType
|
||||
ci.Content.Class = 2
|
||||
ci.Content.Tag = 0
|
||||
ci.Content.IsCompound = true
|
||||
if ci.Content.Bytes, err = asn1.Marshal(encryptedData); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
77
vendor/software.sslmate.com/src/go-pkcs12/pkcs8.go
generated
vendored
Normal file
77
vendor/software.sslmate.com/src/go-pkcs12/pkcs8.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2015 The Go 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 pkcs12
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type pkcs8 struct { // Duplicated from x509 package
|
||||
Version int
|
||||
Algo pkix.AlgorithmIdentifier
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
var ( // Duplicated from x509 package
|
||||
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
|
||||
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
||||
)
|
||||
|
||||
var ( // Duplicated from x509 package
|
||||
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
|
||||
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
|
||||
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
|
||||
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
|
||||
)
|
||||
|
||||
func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { // Duplicated from x509 package
|
||||
switch curve {
|
||||
case elliptic.P224():
|
||||
return oidNamedCurveP224, true
|
||||
case elliptic.P256():
|
||||
return oidNamedCurveP256, true
|
||||
case elliptic.P384():
|
||||
return oidNamedCurveP384, true
|
||||
case elliptic.P521():
|
||||
return oidNamedCurveP521, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func marshalPKCS8PrivateKey(key interface{}) (der []byte, err error) {
|
||||
var privKey pkcs8
|
||||
switch key := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
privKey.Algo.Algorithm = oidPublicKeyRSA
|
||||
// This is a NULL parameters value which is technically
|
||||
// superfluous, but most other code includes it.
|
||||
privKey.Algo.Parameters = asn1.RawValue{
|
||||
Tag: 5,
|
||||
}
|
||||
privKey.PrivateKey = x509.MarshalPKCS1PrivateKey(key)
|
||||
case *ecdsa.PrivateKey:
|
||||
privKey.Algo.Algorithm = oidPublicKeyECDSA
|
||||
namedCurveOID, ok := oidFromNamedCurve(key.Curve)
|
||||
if !ok {
|
||||
return nil, errors.New("pkcs12: unknown elliptic curve")
|
||||
}
|
||||
if privKey.Algo.Parameters.FullBytes, err = asn1.Marshal(namedCurveOID); err != nil {
|
||||
return nil, errors.New("pkcs12: failed to embed OID of named curve in PKCS#8: " + err.Error())
|
||||
}
|
||||
if privKey.PrivateKey, err = x509.MarshalECPrivateKey(key); err != nil {
|
||||
return nil, errors.New("pkcs12: failed to embed EC private key in PKCS#8: " + err.Error())
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("pkcs12: only RSA and ECDSA private keys supported")
|
||||
}
|
||||
return asn1.Marshal(privKey)
|
||||
}
|
98
vendor/software.sslmate.com/src/go-pkcs12/safebags.go
generated
vendored
Normal file
98
vendor/software.sslmate.com/src/go-pkcs12/safebags.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2015 The Go 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 pkcs12
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
// see https://tools.ietf.org/html/rfc7292#appendix-D
|
||||
oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1})
|
||||
oidPKCS8ShroundedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2})
|
||||
oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3})
|
||||
)
|
||||
|
||||
type certBag struct {
|
||||
Id asn1.ObjectIdentifier
|
||||
Data []byte `asn1:"tag:0,explicit"`
|
||||
}
|
||||
|
||||
func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) {
|
||||
pkinfo := new(encryptedPrivateKeyInfo)
|
||||
if err = unmarshal(asn1Data, pkinfo); err != nil {
|
||||
return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error())
|
||||
}
|
||||
|
||||
pkData, err := pbDecrypt(pkinfo, password)
|
||||
if err != nil {
|
||||
return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error())
|
||||
}
|
||||
|
||||
ret := new(asn1.RawValue)
|
||||
if err = unmarshal(pkData, ret); err != nil {
|
||||
return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error())
|
||||
}
|
||||
|
||||
if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil {
|
||||
return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error())
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
func encodePkcs8ShroudedKeyBag(rand io.Reader, privateKey interface{}, password []byte) (asn1Data []byte, err error) {
|
||||
var pkData []byte
|
||||
if pkData, err = marshalPKCS8PrivateKey(privateKey); err != nil {
|
||||
return nil, errors.New("pkcs12: error encoding PKCS#8 private key: " + err.Error())
|
||||
}
|
||||
|
||||
randomSalt := make([]byte, 8)
|
||||
if _, err = rand.Read(randomSalt); err != nil {
|
||||
return nil, errors.New("pkcs12: error reading random salt: " + err.Error())
|
||||
}
|
||||
var paramBytes []byte
|
||||
if paramBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil {
|
||||
return nil, errors.New("pkcs12: error encoding params: " + err.Error())
|
||||
}
|
||||
|
||||
var pkinfo encryptedPrivateKeyInfo
|
||||
pkinfo.AlgorithmIdentifier.Algorithm = oidPBEWithSHAAnd3KeyTripleDESCBC
|
||||
pkinfo.AlgorithmIdentifier.Parameters.FullBytes = paramBytes
|
||||
|
||||
if err = pbEncrypt(&pkinfo, pkData, password); err != nil {
|
||||
return nil, errors.New("pkcs12: error encrypting PKCS#8 shrouded key bag: " + err.Error())
|
||||
}
|
||||
|
||||
if asn1Data, err = asn1.Marshal(pkinfo); err != nil {
|
||||
return nil, errors.New("pkcs12: error encoding PKCS#8 shrouded key bag: " + err.Error())
|
||||
}
|
||||
|
||||
return asn1Data, nil
|
||||
}
|
||||
|
||||
func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) {
|
||||
bag := new(certBag)
|
||||
if err := unmarshal(asn1Data, bag); err != nil {
|
||||
return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error())
|
||||
}
|
||||
if !bag.Id.Equal(oidCertTypeX509Certificate) {
|
||||
return nil, NotImplementedError("only X509 certificates are supported")
|
||||
}
|
||||
return bag.Data, nil
|
||||
}
|
||||
|
||||
func encodeCertBag(x509Certificates []byte) (asn1Data []byte, err error) {
|
||||
var bag certBag
|
||||
bag.Id = oidCertTypeX509Certificate
|
||||
bag.Data = x509Certificates
|
||||
if asn1Data, err = asn1.Marshal(bag); err != nil {
|
||||
return nil, errors.New("pkcs12: error encoding cert bag: " + err.Error())
|
||||
}
|
||||
return asn1Data, nil
|
||||
}
|
Reference in New Issue
Block a user