mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-28 03:30:59 +00:00
Add fork of Go 1.15-dev's crypto/x509
Snapshotted from Go commit 619c7a48a38b28b521591b490fd14ccb7ea5e821 (https://go-review.googlesource.com/c/go/+/229762, "crypto/x509: add x509omitbundledroots build tag to not embed roots") With 975c01342a25899962969833d8b2873dc8856a4f (https://go-review.googlesource.com/c/go/+/220721) removed, because it depends on other stuff in Go std that doesn't yet exist in a Go release. Also, add a subset fork of Go's internal/testenv, for use by x509's tests.
This commit is contained in:
parent
2dac4f2b24
commit
3bab226299
tempfork
internal/testenv
x509
cert_pool.goexample_test.goname_constraints_test.gopem_decrypt.gopem_decrypt_test.gopkcs1.gopkcs8.gopkcs8_test.goroot.goroot_aix.goroot_bsd.goroot_cgo_darwin.goroot_darwin.goroot_darwin_arm64.goroot_darwin_arm_gen.goroot_darwin_test.goroot_js.goroot_linux.goroot_nocgo_darwin.goroot_omit.goroot_omit_test.goroot_plan9.goroot_solaris.goroot_unix.goroot_unix_test.goroot_windows.gosec1.gosec1_test.gotest-file.crt
testdata
verify.goverify_test.gox509.gox509_test.gox509_test_import.go
160
tempfork/internal/testenv/testenv.go
Normal file
160
tempfork/internal/testenv/testenv.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// 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 testenv is Tailscale's fork/subset of Go's
|
||||||
|
// internal/testenv. It exists to satisfy the tests of our fork of
|
||||||
|
// crypto/x509.
|
||||||
|
package testenv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HasGoBuild reports whether the current system can build programs with ``go build''
|
||||||
|
// and then run them with os.StartProcess or exec.Command.
|
||||||
|
func HasGoBuild() bool {
|
||||||
|
if os.Getenv("GO_GCFLAGS") != "" {
|
||||||
|
// It's too much work to require every caller of the go command
|
||||||
|
// to pass along "-gcflags="+os.Getenv("GO_GCFLAGS").
|
||||||
|
// For now, if $GO_GCFLAGS is set, report that we simply can't
|
||||||
|
// run go build.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "android", "js":
|
||||||
|
return false
|
||||||
|
case "darwin":
|
||||||
|
if runtime.GOARCH == "arm64" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustHaveGoBuild checks that the current system can build programs with ``go build''
|
||||||
|
// and then run them with os.StartProcess or exec.Command.
|
||||||
|
// If not, MustHaveGoBuild calls t.Skip with an explanation.
|
||||||
|
func MustHaveGoBuild(t testing.TB) {
|
||||||
|
if os.Getenv("GO_GCFLAGS") != "" {
|
||||||
|
t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS")
|
||||||
|
}
|
||||||
|
if !HasGoBuild() {
|
||||||
|
t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasGoRun reports whether the current system can run programs with ``go run.''
|
||||||
|
func HasGoRun() bool {
|
||||||
|
// For now, having go run and having go build are the same.
|
||||||
|
return HasGoBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustHaveGoRun checks that the current system can run programs with ``go run.''
|
||||||
|
// If not, MustHaveGoRun calls t.Skip with an explanation.
|
||||||
|
func MustHaveGoRun(t testing.TB) {
|
||||||
|
if !HasGoRun() {
|
||||||
|
t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoTool reports the path to the Go tool.
|
||||||
|
func GoTool() (string, error) {
|
||||||
|
if !HasGoBuild() {
|
||||||
|
return "", errors.New("platform cannot run go tool")
|
||||||
|
}
|
||||||
|
var exeSuffix string
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
exeSuffix = ".exe"
|
||||||
|
}
|
||||||
|
path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
goBin, err := exec.LookPath("go" + exeSuffix)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("cannot find go tool: " + err.Error())
|
||||||
|
}
|
||||||
|
return goBin, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoToolPath reports the path to the Go tool.
|
||||||
|
// It is a convenience wrapper around GoTool.
|
||||||
|
// If the tool is unavailable GoToolPath calls t.Skip.
|
||||||
|
// If the tool should be available and isn't, GoToolPath calls t.Fatal.
|
||||||
|
func GoToolPath(t testing.TB) string {
|
||||||
|
MustHaveGoBuild(t)
|
||||||
|
path, err := GoTool()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Add all environment variables that affect the Go command to test metadata.
|
||||||
|
// Cached test results will be invalidate when these variables change.
|
||||||
|
// See golang.org/issue/32285.
|
||||||
|
for _, envVar := range strings.Fields(KnownEnv) {
|
||||||
|
os.Getenv(envVar)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// KnownEnv is a list of environment variables that affect the operation
|
||||||
|
// of the Go command.
|
||||||
|
const KnownEnv = `
|
||||||
|
AR
|
||||||
|
CC
|
||||||
|
CGO_CFLAGS
|
||||||
|
CGO_CFLAGS_ALLOW
|
||||||
|
CGO_CFLAGS_DISALLOW
|
||||||
|
CGO_CPPFLAGS
|
||||||
|
CGO_CPPFLAGS_ALLOW
|
||||||
|
CGO_CPPFLAGS_DISALLOW
|
||||||
|
CGO_CXXFLAGS
|
||||||
|
CGO_CXXFLAGS_ALLOW
|
||||||
|
CGO_CXXFLAGS_DISALLOW
|
||||||
|
CGO_ENABLED
|
||||||
|
CGO_FFLAGS
|
||||||
|
CGO_FFLAGS_ALLOW
|
||||||
|
CGO_FFLAGS_DISALLOW
|
||||||
|
CGO_LDFLAGS
|
||||||
|
CGO_LDFLAGS_ALLOW
|
||||||
|
CGO_LDFLAGS_DISALLOW
|
||||||
|
CXX
|
||||||
|
FC
|
||||||
|
GCCGO
|
||||||
|
GO111MODULE
|
||||||
|
GO386
|
||||||
|
GOARCH
|
||||||
|
GOARM
|
||||||
|
GOBIN
|
||||||
|
GOCACHE
|
||||||
|
GOENV
|
||||||
|
GOEXE
|
||||||
|
GOFLAGS
|
||||||
|
GOGCCFLAGS
|
||||||
|
GOHOSTARCH
|
||||||
|
GOHOSTOS
|
||||||
|
GOINSECURE
|
||||||
|
GOMIPS
|
||||||
|
GOMIPS64
|
||||||
|
GOMODCACHE
|
||||||
|
GONOPROXY
|
||||||
|
GONOSUMDB
|
||||||
|
GOOS
|
||||||
|
GOPATH
|
||||||
|
GOPPC64
|
||||||
|
GOPRIVATE
|
||||||
|
GOPROXY
|
||||||
|
GOROOT
|
||||||
|
GOSUMDB
|
||||||
|
GOTMPDIR
|
||||||
|
GOTOOLDIR
|
||||||
|
GOWASM
|
||||||
|
GO_EXTLINK_ENABLED
|
||||||
|
PKG_CONFIG
|
||||||
|
`
|
159
tempfork/x509/cert_pool.go
Normal file
159
tempfork/x509/cert_pool.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// Copyright 2011 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CertPool is a set of certificates.
|
||||||
|
type CertPool struct {
|
||||||
|
bySubjectKeyId map[string][]int
|
||||||
|
byName map[string][]int
|
||||||
|
certs []*Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCertPool returns a new, empty CertPool.
|
||||||
|
func NewCertPool() *CertPool {
|
||||||
|
return &CertPool{
|
||||||
|
bySubjectKeyId: make(map[string][]int),
|
||||||
|
byName: make(map[string][]int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CertPool) copy() *CertPool {
|
||||||
|
p := &CertPool{
|
||||||
|
bySubjectKeyId: make(map[string][]int, len(s.bySubjectKeyId)),
|
||||||
|
byName: make(map[string][]int, len(s.byName)),
|
||||||
|
certs: make([]*Certificate, len(s.certs)),
|
||||||
|
}
|
||||||
|
for k, v := range s.bySubjectKeyId {
|
||||||
|
indexes := make([]int, len(v))
|
||||||
|
copy(indexes, v)
|
||||||
|
p.bySubjectKeyId[k] = indexes
|
||||||
|
}
|
||||||
|
for k, v := range s.byName {
|
||||||
|
indexes := make([]int, len(v))
|
||||||
|
copy(indexes, v)
|
||||||
|
p.byName[k] = indexes
|
||||||
|
}
|
||||||
|
copy(p.certs, s.certs)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemCertPool returns a copy of the system cert pool.
|
||||||
|
//
|
||||||
|
// Any mutations to the returned pool are not written to disk and do
|
||||||
|
// not affect any other pool returned by SystemCertPool.
|
||||||
|
//
|
||||||
|
// New changes in the system cert pool might not be reflected
|
||||||
|
// in subsequent calls.
|
||||||
|
func SystemCertPool() (*CertPool, error) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// Issue 16736, 18609:
|
||||||
|
return nil, errors.New("crypto/x509: system root pool is not available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sysRoots := systemRootsPool(); sysRoots != nil {
|
||||||
|
return sysRoots.copy(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadSystemRoots()
|
||||||
|
}
|
||||||
|
|
||||||
|
// findPotentialParents returns the indexes of certificates in s which might
|
||||||
|
// have signed cert. The caller must not modify the returned slice.
|
||||||
|
func (s *CertPool) findPotentialParents(cert *Certificate) []int {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var candidates []int
|
||||||
|
if len(cert.AuthorityKeyId) > 0 {
|
||||||
|
candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
|
||||||
|
}
|
||||||
|
if len(candidates) == 0 {
|
||||||
|
candidates = s.byName[string(cert.RawIssuer)]
|
||||||
|
}
|
||||||
|
return candidates
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CertPool) contains(cert *Certificate) bool {
|
||||||
|
if s == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates := s.byName[string(cert.RawSubject)]
|
||||||
|
for _, c := range candidates {
|
||||||
|
if s.certs[c].Equal(cert) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCert adds a certificate to a pool.
|
||||||
|
func (s *CertPool) AddCert(cert *Certificate) {
|
||||||
|
if cert == nil {
|
||||||
|
panic("adding nil Certificate to CertPool")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the certificate isn't being added twice.
|
||||||
|
if s.contains(cert) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(s.certs)
|
||||||
|
s.certs = append(s.certs, cert)
|
||||||
|
|
||||||
|
if len(cert.SubjectKeyId) > 0 {
|
||||||
|
keyId := string(cert.SubjectKeyId)
|
||||||
|
s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n)
|
||||||
|
}
|
||||||
|
name := string(cert.RawSubject)
|
||||||
|
s.byName[name] = append(s.byName[name], n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
|
||||||
|
// It appends any certificates found to s and reports whether any certificates
|
||||||
|
// were successfully parsed.
|
||||||
|
//
|
||||||
|
// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set
|
||||||
|
// of root CAs in a format suitable for this function.
|
||||||
|
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
|
||||||
|
for len(pemCerts) > 0 {
|
||||||
|
var block *pem.Block
|
||||||
|
block, pemCerts = pem.Decode(pemCerts)
|
||||||
|
if block == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.AddCert(cert)
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subjects returns a list of the DER-encoded subjects of
|
||||||
|
// all of the certificates in the pool.
|
||||||
|
func (s *CertPool) Subjects() [][]byte {
|
||||||
|
res := make([][]byte, len(s.certs))
|
||||||
|
for i, c := range s.certs {
|
||||||
|
res[i] = c.RawSubject
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
137
tempfork/x509/example_test.go
Normal file
137
tempfork/x509/example_test.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// Copyright 2014 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 x509_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/dsa"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleCertificate_Verify() {
|
||||||
|
// Verifying with a custom list of root certificates.
|
||||||
|
|
||||||
|
const rootPEM = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
|
||||||
|
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
|
||||||
|
YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG
|
||||||
|
EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
|
||||||
|
bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||||
|
AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
|
||||||
|
VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
|
||||||
|
h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
|
||||||
|
ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
|
||||||
|
EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
|
||||||
|
DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7
|
||||||
|
qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD
|
||||||
|
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g
|
||||||
|
K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI
|
||||||
|
KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n
|
||||||
|
ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB
|
||||||
|
BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY
|
||||||
|
/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/
|
||||||
|
zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza
|
||||||
|
HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto
|
||||||
|
WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6
|
||||||
|
yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
const certPEM = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
|
||||||
|
BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
|
||||||
|
cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAw
|
||||||
|
WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
|
||||||
|
TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp
|
||||||
|
bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q
|
||||||
|
5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC
|
||||||
|
7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAa
|
||||||
|
BgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUF
|
||||||
|
BwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcy
|
||||||
|
LmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2Nz
|
||||||
|
cDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAf
|
||||||
|
BgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisG
|
||||||
|
AQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29t
|
||||||
|
L0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+
|
||||||
|
gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283
|
||||||
|
TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq
|
||||||
|
0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoW
|
||||||
|
RvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOh
|
||||||
|
yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
// First, create the set of root certificates. For this example we only
|
||||||
|
// have one. It's also possible to omit this in order to use the
|
||||||
|
// default root set of the current operating system.
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
|
||||||
|
if !ok {
|
||||||
|
panic("failed to parse root certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode([]byte(certPEM))
|
||||||
|
if block == nil {
|
||||||
|
panic("failed to parse certificate PEM")
|
||||||
|
}
|
||||||
|
cert, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to parse certificate: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := x509.VerifyOptions{
|
||||||
|
DNSName: "mail.google.com",
|
||||||
|
Roots: roots,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := cert.Verify(opts); err != nil {
|
||||||
|
panic("failed to verify certificate: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleParsePKIXPublicKey() {
|
||||||
|
const pubPEM = `
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlRuRnThUjU8/prwYxbty
|
||||||
|
WPT9pURI3lbsKMiB6Fn/VHOKE13p4D8xgOCADpdRagdT6n4etr9atzDKUSvpMtR3
|
||||||
|
CP5noNc97WiNCggBjVWhs7szEe8ugyqF23XwpHQ6uV1LKH50m92MbOWfCtjU9p/x
|
||||||
|
qhNpQQ1AZhqNy5Gevap5k8XzRmjSldNAFZMY7Yv3Gi+nyCwGwpVtBUwhuLzgNFK/
|
||||||
|
yDtw2WcWmUU7NuC8Q6MWvPebxVtCfVp/iQU6q60yyt6aGOBkhAX0LpKAEhKidixY
|
||||||
|
nP9PNVBvxgu3XZ4P36gZV6+ummKdBVnc3NqwBLu5+CcdRdusmHPHd5pHf4/38Z3/
|
||||||
|
6qU2a/fPvWzceVTEgZ47QjFMTCTmCwNt29cvi7zZeQzjtwQgn4ipN9NibRH/Ax/q
|
||||||
|
TbIzHfrJ1xa2RteWSdFjwtxi9C20HUkjXSeI4YlzQMH0fPX6KCE7aVePTOnB69I/
|
||||||
|
a9/q96DiXZajwlpq3wFctrs1oXqBp5DVrCIj8hU2wNgB7LtQ1mCtsYz//heai0K9
|
||||||
|
PhE4X6hiE0YmeAZjR0uHl8M/5aW9xCoJ72+12kKpWAa0SFRWLy6FejNYCYpkupVJ
|
||||||
|
yecLk/4L1W0l6jQQZnWErXZYe0PNFcmwGXy1Rep83kfBRNKRy5tvocalLlwXLdUk
|
||||||
|
AIU+2GKjyT3iMuzZxxFxPFMCAwEAAQ==
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
|
||||||
|
block, _ := pem.Decode([]byte(pubPEM))
|
||||||
|
if block == nil {
|
||||||
|
panic("failed to parse PEM block containing the public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to parse DER encoded public key: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pub := pub.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
fmt.Println("pub is of type RSA:", pub)
|
||||||
|
case *dsa.PublicKey:
|
||||||
|
fmt.Println("pub is of type DSA:", pub)
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
fmt.Println("pub is of type ECDSA:", pub)
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
fmt.Println("pub is of type Ed25519:", pub)
|
||||||
|
default:
|
||||||
|
panic("unknown type of public key")
|
||||||
|
}
|
||||||
|
}
|
2187
tempfork/x509/name_constraints_test.go
Normal file
2187
tempfork/x509/name_constraints_test.go
Normal file
File diff suppressed because it is too large
Load Diff
240
tempfork/x509/pem_decrypt.go
Normal file
240
tempfork/x509/pem_decrypt.go
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
// Copyright 2012 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 x509
|
||||||
|
|
||||||
|
// RFC 1423 describes the encryption of PEM blocks. The algorithm used to
|
||||||
|
// generate a key from the password was derived by looking at the OpenSSL
|
||||||
|
// implementation.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/des"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PEMCipher int
|
||||||
|
|
||||||
|
// Possible values for the EncryptPEMBlock encryption algorithm.
|
||||||
|
const (
|
||||||
|
_ PEMCipher = iota
|
||||||
|
PEMCipherDES
|
||||||
|
PEMCipher3DES
|
||||||
|
PEMCipherAES128
|
||||||
|
PEMCipherAES192
|
||||||
|
PEMCipherAES256
|
||||||
|
)
|
||||||
|
|
||||||
|
// rfc1423Algo holds a method for enciphering a PEM block.
|
||||||
|
type rfc1423Algo struct {
|
||||||
|
cipher PEMCipher
|
||||||
|
name string
|
||||||
|
cipherFunc func(key []byte) (cipher.Block, error)
|
||||||
|
keySize int
|
||||||
|
blockSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
// rfc1423Algos holds a slice of the possible ways to encrypt a PEM
|
||||||
|
// block. The ivSize numbers were taken from the OpenSSL source.
|
||||||
|
var rfc1423Algos = []rfc1423Algo{{
|
||||||
|
cipher: PEMCipherDES,
|
||||||
|
name: "DES-CBC",
|
||||||
|
cipherFunc: des.NewCipher,
|
||||||
|
keySize: 8,
|
||||||
|
blockSize: des.BlockSize,
|
||||||
|
}, {
|
||||||
|
cipher: PEMCipher3DES,
|
||||||
|
name: "DES-EDE3-CBC",
|
||||||
|
cipherFunc: des.NewTripleDESCipher,
|
||||||
|
keySize: 24,
|
||||||
|
blockSize: des.BlockSize,
|
||||||
|
}, {
|
||||||
|
cipher: PEMCipherAES128,
|
||||||
|
name: "AES-128-CBC",
|
||||||
|
cipherFunc: aes.NewCipher,
|
||||||
|
keySize: 16,
|
||||||
|
blockSize: aes.BlockSize,
|
||||||
|
}, {
|
||||||
|
cipher: PEMCipherAES192,
|
||||||
|
name: "AES-192-CBC",
|
||||||
|
cipherFunc: aes.NewCipher,
|
||||||
|
keySize: 24,
|
||||||
|
blockSize: aes.BlockSize,
|
||||||
|
}, {
|
||||||
|
cipher: PEMCipherAES256,
|
||||||
|
name: "AES-256-CBC",
|
||||||
|
cipherFunc: aes.NewCipher,
|
||||||
|
keySize: 32,
|
||||||
|
blockSize: aes.BlockSize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// deriveKey uses a key derivation function to stretch the password into a key
|
||||||
|
// with the number of bits our cipher requires. This algorithm was derived from
|
||||||
|
// the OpenSSL source.
|
||||||
|
func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
|
||||||
|
hash := md5.New()
|
||||||
|
out := make([]byte, c.keySize)
|
||||||
|
var digest []byte
|
||||||
|
|
||||||
|
for i := 0; i < len(out); i += len(digest) {
|
||||||
|
hash.Reset()
|
||||||
|
hash.Write(digest)
|
||||||
|
hash.Write(password)
|
||||||
|
hash.Write(salt)
|
||||||
|
digest = hash.Sum(digest[:0])
|
||||||
|
copy(out[i:], digest)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEncryptedPEMBlock returns if the PEM block is password encrypted.
|
||||||
|
func IsEncryptedPEMBlock(b *pem.Block) bool {
|
||||||
|
_, ok := b.Headers["DEK-Info"]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncorrectPasswordError is returned when an incorrect password is detected.
|
||||||
|
var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
|
||||||
|
|
||||||
|
// DecryptPEMBlock takes a password encrypted PEM block and the password used to
|
||||||
|
// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects
|
||||||
|
// the DEK-Info header to determine the algorithm used for decryption. If no
|
||||||
|
// DEK-Info header is present, an error is returned. If an incorrect password
|
||||||
|
// is detected an IncorrectPasswordError is returned. Because of deficiencies
|
||||||
|
// in the encrypted-PEM format, it's not always possible to detect an incorrect
|
||||||
|
// password. In these cases no error will be returned but the decrypted DER
|
||||||
|
// bytes will be random noise.
|
||||||
|
func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
|
||||||
|
dek, ok := b.Headers["DEK-Info"]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("x509: no DEK-Info header in block")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := strings.Index(dek, ",")
|
||||||
|
if idx == -1 {
|
||||||
|
return nil, errors.New("x509: malformed DEK-Info header")
|
||||||
|
}
|
||||||
|
|
||||||
|
mode, hexIV := dek[:idx], dek[idx+1:]
|
||||||
|
ciph := cipherByName(mode)
|
||||||
|
if ciph == nil {
|
||||||
|
return nil, errors.New("x509: unknown encryption mode")
|
||||||
|
}
|
||||||
|
iv, err := hex.DecodeString(hexIV)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(iv) != ciph.blockSize {
|
||||||
|
return nil, errors.New("x509: incorrect IV size")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on the OpenSSL implementation. The salt is the first 8 bytes
|
||||||
|
// of the initialization vector.
|
||||||
|
key := ciph.deriveKey(password, iv[:8])
|
||||||
|
block, err := ciph.cipherFunc(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b.Bytes)%block.BlockSize() != 0 {
|
||||||
|
return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size")
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, len(b.Bytes))
|
||||||
|
dec := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
dec.CryptBlocks(data, b.Bytes)
|
||||||
|
|
||||||
|
// Blocks are padded using a scheme where the last n bytes of padding are all
|
||||||
|
// equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
|
||||||
|
// For example:
|
||||||
|
// [x y z 2 2]
|
||||||
|
// [x y 7 7 7 7 7 7 7]
|
||||||
|
// If we detect a bad padding, we assume it is an invalid password.
|
||||||
|
dlen := len(data)
|
||||||
|
if dlen == 0 || dlen%ciph.blockSize != 0 {
|
||||||
|
return nil, errors.New("x509: invalid padding")
|
||||||
|
}
|
||||||
|
last := int(data[dlen-1])
|
||||||
|
if dlen < last {
|
||||||
|
return nil, IncorrectPasswordError
|
||||||
|
}
|
||||||
|
if last == 0 || last > ciph.blockSize {
|
||||||
|
return nil, IncorrectPasswordError
|
||||||
|
}
|
||||||
|
for _, val := range data[dlen-last:] {
|
||||||
|
if int(val) != last {
|
||||||
|
return nil, IncorrectPasswordError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data[:dlen-last], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptPEMBlock returns a PEM block of the specified type holding the
|
||||||
|
// given DER-encoded data encrypted with the specified algorithm and
|
||||||
|
// password.
|
||||||
|
func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
|
||||||
|
ciph := cipherByKey(alg)
|
||||||
|
if ciph == nil {
|
||||||
|
return nil, errors.New("x509: unknown encryption mode")
|
||||||
|
}
|
||||||
|
iv := make([]byte, ciph.blockSize)
|
||||||
|
if _, err := io.ReadFull(rand, iv); err != nil {
|
||||||
|
return nil, errors.New("x509: cannot generate IV: " + err.Error())
|
||||||
|
}
|
||||||
|
// The salt is the first 8 bytes of the initialization vector,
|
||||||
|
// matching the key derivation in DecryptPEMBlock.
|
||||||
|
key := ciph.deriveKey(password, iv[:8])
|
||||||
|
block, err := ciph.cipherFunc(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
enc := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
pad := ciph.blockSize - len(data)%ciph.blockSize
|
||||||
|
encrypted := make([]byte, len(data), len(data)+pad)
|
||||||
|
// We could save this copy by encrypting all the whole blocks in
|
||||||
|
// the data separately, but it doesn't seem worth the additional
|
||||||
|
// code.
|
||||||
|
copy(encrypted, data)
|
||||||
|
// See RFC 1423, Section 1.1.
|
||||||
|
for i := 0; i < pad; i++ {
|
||||||
|
encrypted = append(encrypted, byte(pad))
|
||||||
|
}
|
||||||
|
enc.CryptBlocks(encrypted, encrypted)
|
||||||
|
|
||||||
|
return &pem.Block{
|
||||||
|
Type: blockType,
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Proc-Type": "4,ENCRYPTED",
|
||||||
|
"DEK-Info": ciph.name + "," + hex.EncodeToString(iv),
|
||||||
|
},
|
||||||
|
Bytes: encrypted,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cipherByName(name string) *rfc1423Algo {
|
||||||
|
for i := range rfc1423Algos {
|
||||||
|
alg := &rfc1423Algos[i]
|
||||||
|
if alg.name == name {
|
||||||
|
return alg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cipherByKey(key PEMCipher) *rfc1423Algo {
|
||||||
|
for i := range rfc1423Algos {
|
||||||
|
alg := &rfc1423Algos[i]
|
||||||
|
if alg.cipher == key {
|
||||||
|
return alg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
249
tempfork/x509/pem_decrypt_test.go
Normal file
249
tempfork/x509/pem_decrypt_test.go
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
// Copyright 2012 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecrypt(t *testing.T) {
|
||||||
|
for i, data := range testData {
|
||||||
|
t.Logf("test %v. %v", i, data.kind)
|
||||||
|
block, rest := pem.Decode(data.pemData)
|
||||||
|
if len(rest) > 0 {
|
||||||
|
t.Error("extra data")
|
||||||
|
}
|
||||||
|
der, err := DecryptPEMBlock(block, data.password)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("decrypt failed: ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := ParsePKCS1PrivateKey(der); err != nil {
|
||||||
|
t.Error("invalid private key: ", err)
|
||||||
|
}
|
||||||
|
plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("cannot decode test DER data: ", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(der, plainDER) {
|
||||||
|
t.Error("data mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncrypt(t *testing.T) {
|
||||||
|
for i, data := range testData {
|
||||||
|
t.Logf("test %v. %v", i, data.kind)
|
||||||
|
plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("cannot decode test DER data: ", err)
|
||||||
|
}
|
||||||
|
password := []byte("kremvax1")
|
||||||
|
block, err := EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", plainDER, password, data.kind)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("encrypt: ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !IsEncryptedPEMBlock(block) {
|
||||||
|
t.Error("PEM block does not appear to be encrypted")
|
||||||
|
}
|
||||||
|
if block.Type != "RSA PRIVATE KEY" {
|
||||||
|
t.Errorf("unexpected block type; got %q want %q", block.Type, "RSA PRIVATE KEY")
|
||||||
|
}
|
||||||
|
if block.Headers["Proc-Type"] != "4,ENCRYPTED" {
|
||||||
|
t.Errorf("block does not have correct Proc-Type header")
|
||||||
|
}
|
||||||
|
der, err := DecryptPEMBlock(block, password)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("decrypt: ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(der, plainDER) {
|
||||||
|
t.Errorf("data mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testData = []struct {
|
||||||
|
kind PEMCipher
|
||||||
|
password []byte
|
||||||
|
pemData []byte
|
||||||
|
plainDER string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
kind: PEMCipherDES,
|
||||||
|
password: []byte("asdf"),
|
||||||
|
pemData: []byte(testingKey(`
|
||||||
|
-----BEGIN RSA TESTING KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: DES-CBC,34F09A4FC8DE22B5
|
||||||
|
|
||||||
|
WXxy8kbZdiZvANtKvhmPBLV7eVFj2A5z6oAxvI9KGyhG0ZK0skfnt00C24vfU7m5
|
||||||
|
ICXeoqP67lzJ18xCzQfHjDaBNs53DSDT+Iz4e8QUep1xQ30+8QKX2NA2coee3nwc
|
||||||
|
6oM1cuvhNUDemBH2i3dKgMVkfaga0zQiiOq6HJyGSncCMSruQ7F9iWEfRbFcxFCx
|
||||||
|
qtHb1kirfGKEtgWTF+ynyco6+2gMXNu70L7nJcnxnV/RLFkHt7AUU1yrclxz7eZz
|
||||||
|
XOH9VfTjb52q/I8Suozq9coVQwg4tXfIoYUdT//O+mB7zJb9HI9Ps77b9TxDE6Gm
|
||||||
|
4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD
|
||||||
|
r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug==
|
||||||
|
-----END RSA TESTING KEY-----`)),
|
||||||
|
plainDER: `
|
||||||
|
MIIBPAIBAAJBAPASZe+tCPU6p80AjHhDkVsLYa51D35e/YGa8QcZyooeZM8EHozo
|
||||||
|
KD0fNiKI+53bHdy07N+81VQ8/ejPcRoXPlsCAwEAAQJBAMTxIuSq27VpR+zZ7WJf
|
||||||
|
c6fvv1OBvpMZ0/d1pxL/KnOAgq2rD5hDtk9b0LGhTPgQAmrrMTKuSeGoIuYE+gKQ
|
||||||
|
QvkCIQD+GC1m+/do+QRurr0uo46Kx1LzLeSCrjBk34wiOp2+dwIhAPHfTLRXS2fv
|
||||||
|
7rljm0bYa4+eDZpz+E8RcXEgzhhvcQQ9AiAI5eHZJGOyml3MXnQjiPi55WcDOw0w
|
||||||
|
glcRgT6QCEtz2wIhANSyqaFtosIkHKqrDUGfz/bb5tqMYTAnBruVPaf/WEOBAiEA
|
||||||
|
9xORWeRG1tRpso4+dYy4KdDkuLPIO01KY6neYGm3BCM=`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: PEMCipher3DES,
|
||||||
|
password: []byte("asdf"),
|
||||||
|
pemData: []byte(testingKey(`
|
||||||
|
-----BEGIN RSA TESTING KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: DES-EDE3-CBC,C1F4A6A03682C2C7
|
||||||
|
|
||||||
|
0JqVdBEH6iqM7drTkj+e2W/bE3LqakaiWhb9WUVonFkhyu8ca/QzebY3b5gCvAZQ
|
||||||
|
YwBvDcT/GHospKqPx+cxDHJNsUASDZws6bz8ZXWJGwZGExKzr0+Qx5fgXn44Ms3x
|
||||||
|
8g1ENFuTXtxo+KoNK0zuAMAqp66Llcds3Fjl4XR18QaD0CrVNAfOdgATWZm5GJxk
|
||||||
|
Fgx5f84nT+/ovvreG+xeOzWgvtKo0UUZVrhGOgfKLpa57adumcJ6SkUuBtEFpZFB
|
||||||
|
ldw5w7WC7d13x2LsRkwo8ZrDKgIV+Y9GNvhuCCkTzNP0V3gNeJpd201HZHR+9n3w
|
||||||
|
3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq
|
||||||
|
gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk=
|
||||||
|
-----END RSA TESTING KEY-----`)),
|
||||||
|
plainDER: `
|
||||||
|
MIIBOwIBAAJBANOCXKdoNS/iP/MAbl9cf1/SF3P+Ns7ZeNL27CfmDh0O6Zduaax5
|
||||||
|
NBiumd2PmjkaCu7lQ5JOibHfWn+xJsc3kw0CAwEAAQJANX/W8d1Q/sCqzkuAn4xl
|
||||||
|
B5a7qfJWaLHndu1QRLNTRJPn0Ee7OKJ4H0QKOhQM6vpjRrz+P2u9thn6wUxoPsef
|
||||||
|
QQIhAP/jCkfejFcy4v15beqKzwz08/tslVjF+Yq41eJGejmxAiEA05pMoqfkyjcx
|
||||||
|
fyvGhpoOyoCp71vSGUfR2I9CR65oKh0CIC1Msjs66LlfJtQctRq6bCEtFCxEcsP+
|
||||||
|
eEjYo/Sk6WphAiEAxpgWPMJeU/shFT28gS+tmhjPZLpEoT1qkVlC14u0b3ECIQDX
|
||||||
|
tZZZxCtPAm7shftEib0VU77Lk8MsXJcx2C4voRsjEw==`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: PEMCipherAES128,
|
||||||
|
password: []byte("asdf"),
|
||||||
|
pemData: []byte(testingKey(`
|
||||||
|
-----BEGIN RSA TESTING KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-128-CBC,D4492E793FC835CC038A728ED174F78A
|
||||||
|
|
||||||
|
EyfQSzXSjv6BaNH+NHdXRlkHdimpF9izWlugVJAPApgXrq5YldPe2aGIOFXyJ+QE
|
||||||
|
ZIG20DYqaPzJRjTEbPNZ6Es0S2JJ5yCpKxwJuDkgJZKtF39Q2i36JeGbSZQIuWJE
|
||||||
|
GZbBpf1jDH/pr0iGonuAdl2PCCZUiy+8eLsD2tyviHUkFLOB+ykYoJ5t8ngZ/B6D
|
||||||
|
33U43LLb7+9zD4y3Q9OVHqBFGyHcxCY9+9Qh4ZnFp7DTf6RY5TNEvE3s4g6aDpBs
|
||||||
|
3NbvRVvYTgs8K9EPk4K+5R+P2kD8J8KvEIGxVa1vz8QoCJ/jr7Ka2rvNgPCex5/E
|
||||||
|
080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo
|
||||||
|
AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0=
|
||||||
|
-----END RSA TESTING KEY-----`)),
|
||||||
|
plainDER: `
|
||||||
|
MIIBOgIBAAJBAMBlj5FxYtqbcy8wY89d/S7n0+r5MzD9F63BA/Lpl78vQKtdJ5dT
|
||||||
|
cDGh/rBt1ufRrNp0WihcmZi7Mpl/3jHjiWECAwEAAQJABNOHYnKhtDIqFYj1OAJ3
|
||||||
|
k3GlU0OlERmIOoeY/cL2V4lgwllPBEs7r134AY4wMmZSBUj8UR/O4SNO668ElKPE
|
||||||
|
cQIhAOuqY7/115x5KCdGDMWi+jNaMxIvI4ETGwV40ykGzqlzAiEA0P9oEC3m9tHB
|
||||||
|
kbpjSTxaNkrXxDgdEOZz8X0uOUUwHNsCIAwzcSCiGLyYJTULUmP1ESERfW1mlV78
|
||||||
|
XzzESaJpIM/zAiBQkSTcl9VhcJreQqvjn5BnPZLP4ZHS4gPwJAGdsj5J4QIhAOVR
|
||||||
|
B3WlRNTXR2WsJ5JdByezg9xzdXzULqmga0OE339a`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: PEMCipherAES192,
|
||||||
|
password: []byte("asdf"),
|
||||||
|
pemData: []byte(testingKey(`
|
||||||
|
-----BEGIN RSA TESTING KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-192-CBC,E2C9FB02BCA23ADE1829F8D8BC5F5369
|
||||||
|
|
||||||
|
cqVslvHqDDM6qwU6YjezCRifXmKsrgEev7ng6Qs7UmDJOpHDgJQZI9fwMFUhIyn5
|
||||||
|
FbCu1SHkLMW52Ld3CuEqMnzWMlhPrW8tFvUOrMWPYSisv7nNq88HobZEJcUNL2MM
|
||||||
|
Y15XmHW6IJwPqhKyLHpWXyOCVEh4ODND2nV15PCoi18oTa475baxSk7+1qH7GuIs
|
||||||
|
Rb7tshNTMqHbCpyo9Rn3UxeFIf9efdl8YLiMoIqc7J8E5e9VlbeQSdLMQOgDAQJG
|
||||||
|
ReUtTw8exmKsY4gsSjhkg5uiw7/ZB1Ihto0qnfQJgjGc680qGkT1d6JfvOfeYAk6
|
||||||
|
xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t
|
||||||
|
Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw=
|
||||||
|
-----END RSA TESTING KEY-----`)),
|
||||||
|
plainDER: `
|
||||||
|
MIIBOwIBAAJBAMGcRrZiNNmtF20zyS6MQ7pdGx17aFDl+lTl+qnLuJRUCMUG05xs
|
||||||
|
OmxmL/O1Qlf+bnqR8Bgg65SfKg21SYuLhiMCAwEAAQJBAL94uuHyO4wux2VC+qpj
|
||||||
|
IzPykjdU7XRcDHbbvksf4xokSeUFjjD3PB0Qa83M94y89ZfdILIqS9x5EgSB4/lX
|
||||||
|
qNkCIQD6cCIqLfzq/lYbZbQgAAjpBXeQVYsbvVtJrPrXJAlVVQIhAMXpDKMeFPMn
|
||||||
|
J0g2rbx1gngx0qOa5r5iMU5w/noN4W2XAiBjf+WzCG5yFvazD+dOx3TC0A8+4x3P
|
||||||
|
uZ3pWbaXf5PNuQIgAcdXarvhelH2w2piY1g3BPeFqhzBSCK/yLGxR82KIh8CIQDD
|
||||||
|
+qGKsd09NhQ/G27y/DARzOYtml1NvdmCQAgsDIIOLA==`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: PEMCipherAES256,
|
||||||
|
password: []byte("asdf"),
|
||||||
|
pemData: []byte(testingKey(`
|
||||||
|
-----BEGIN RSA TESTING KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-256-CBC,8E7ED5CD731902CE938957A886A5FFBD
|
||||||
|
|
||||||
|
4Mxr+KIzRVwoOP0wwq6caSkvW0iS+GE2h2Ov/u+n9ZTMwL83PRnmjfjzBgfRZLVf
|
||||||
|
JFPXxUK26kMNpIdssNnqGOds+DhB+oSrsNKoxgxSl5OBoYv9eJTVYm7qOyAFIsjr
|
||||||
|
DRKAcjYCmzfesr7PVTowwy0RtHmYwyXMGDlAzzZrEvaiySFFmMyKKvtoavwaFoc7
|
||||||
|
Pz3RZScwIuubzTGJ1x8EzdffYOsdCa9Mtgpp3L136+23dOd6L/qK2EG2fzrJSHs/
|
||||||
|
2XugkleBFSMKzEp9mxXKRfa++uidQvMZTFLDK9w5YjrRvMBo/l2BoZIsq0jAIE1N
|
||||||
|
sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk
|
||||||
|
clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0=
|
||||||
|
-----END RSA TESTING KEY-----`)),
|
||||||
|
plainDER: `
|
||||||
|
MIIBOgIBAAJBAKy3GFkstoCHIEeUU/qO8207m8WSrjksR+p9B4tf1w5k+2O1V/GY
|
||||||
|
AQ5WFCApItcOkQe/I0yZZJk/PmCqMzSxrc8CAwEAAQJAOCAz0F7AW9oNelVQSP8F
|
||||||
|
Sfzx7O1yom+qWyAQQJF/gFR11gpf9xpVnnyu1WxIRnDUh1LZwUsjwlDYb7MB74id
|
||||||
|
oQIhANPcOiLwOPT4sIUpRM5HG6BF1BI7L77VpyGVk8xNP7X/AiEA0LMHZtk4I+lJ
|
||||||
|
nClgYp4Yh2JZ1Znbu7IoQMCEJCjwKDECIGd8Dzm5tViTkUW6Hs3Tlf73nNs65duF
|
||||||
|
aRnSglss8I3pAiEAonEnKruawgD8RavDFR+fUgmQiPz4FnGGeVgfwpGG1JECIBYq
|
||||||
|
PXHYtPqxQIbD2pScR5qum7iGUh11lEUPkmt+2uqS`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// generated with:
|
||||||
|
// openssl genrsa -aes128 -passout pass:asdf -out server.orig.key 128
|
||||||
|
kind: PEMCipherAES128,
|
||||||
|
password: []byte("asdf"),
|
||||||
|
pemData: []byte(testingKey(`
|
||||||
|
-----BEGIN RSA TESTING KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7
|
||||||
|
|
||||||
|
6ei/MlytjE0FFgZOGQ+jrwomKfpl8kdefeE0NSt/DMRrw8OacHAzBNi3pPEa0eX3
|
||||||
|
eND9l7C9meCirWovjj9QWVHrXyugFuDIqgdhQ8iHTgCfF3lrmcttVrbIfMDw+smD
|
||||||
|
hTP8O1mS/MHl92NE0nhv0w==
|
||||||
|
-----END RSA TESTING KEY-----`)),
|
||||||
|
plainDER: `
|
||||||
|
MGMCAQACEQC6ssxmYuauuHGOCDAI54RdAgMBAAECEQCWIn6Yv2O+kBcDF7STctKB
|
||||||
|
AgkA8SEfu/2i3g0CCQDGNlXbBHX7kQIIK3Ww5o0cYbECCQDCimPb0dYGsQIIeQ7A
|
||||||
|
jryIst8=`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var incompleteBlockPEM = testingKey(`
|
||||||
|
-----BEGIN RSA TESTING KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7
|
||||||
|
|
||||||
|
6L8yXK2MTQUWBk4ZD6OvCiYp+mXyR1594TQ1K38MxGvDw5pwcDME2Lek8RrR5fd40P2XsL2Z4KKt
|
||||||
|
ai+OP1BZUetfK6AW4MiqB2FDyIdOAJ8XeWuZy21Wtsh8wPD6yYOFM/w7WZL8weX3Y0TSeG/T
|
||||||
|
-----END RSA TESTING KEY-----`)
|
||||||
|
|
||||||
|
func TestIncompleteBlock(t *testing.T) {
|
||||||
|
// incompleteBlockPEM contains ciphertext that is not a multiple of the
|
||||||
|
// block size. This previously panicked. See #11215.
|
||||||
|
block, _ := pem.Decode([]byte(incompleteBlockPEM))
|
||||||
|
_, err := DecryptPEMBlock(block, []byte("foo"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Bad PEM data decrypted successfully")
|
||||||
|
}
|
||||||
|
const expectedSubstr = "block size"
|
||||||
|
if e := err.Error(); !strings.Contains(e, expectedSubstr) {
|
||||||
|
t.Fatalf("Expected error containing %q but got: %q", expectedSubstr, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
|
173
tempfork/x509/pkcs1.go
Normal file
173
tempfork/x509/pkcs1.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
// Copyright 2011 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
|
||||||
|
type pkcs1PrivateKey struct {
|
||||||
|
Version int
|
||||||
|
N *big.Int
|
||||||
|
E int
|
||||||
|
D *big.Int
|
||||||
|
P *big.Int
|
||||||
|
Q *big.Int
|
||||||
|
// We ignore these values, if present, because rsa will calculate them.
|
||||||
|
Dp *big.Int `asn1:"optional"`
|
||||||
|
Dq *big.Int `asn1:"optional"`
|
||||||
|
Qinv *big.Int `asn1:"optional"`
|
||||||
|
|
||||||
|
AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type pkcs1AdditionalRSAPrime struct {
|
||||||
|
Prime *big.Int
|
||||||
|
|
||||||
|
// We ignore these values because rsa will calculate them.
|
||||||
|
Exp *big.Int
|
||||||
|
Coeff *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkcs1PublicKey reflects the ASN.1 structure of a PKCS#1 public key.
|
||||||
|
type pkcs1PublicKey struct {
|
||||||
|
N *big.Int
|
||||||
|
E int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePKCS1PrivateKey parses an RSA private key in PKCS#1, ASN.1 DER form.
|
||||||
|
//
|
||||||
|
// This kind of key is commonly encoded in PEM blocks of type "RSA PRIVATE KEY".
|
||||||
|
func ParsePKCS1PrivateKey(der []byte) (*rsa.PrivateKey, error) {
|
||||||
|
var priv pkcs1PrivateKey
|
||||||
|
rest, err := asn1.Unmarshal(der, &priv)
|
||||||
|
if len(rest) > 0 {
|
||||||
|
return nil, asn1.SyntaxError{Msg: "trailing data"}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if _, err := asn1.Unmarshal(der, &ecPrivateKey{}); err == nil {
|
||||||
|
return nil, errors.New("x509: failed to parse private key (use ParseECPrivateKey instead for this key format)")
|
||||||
|
}
|
||||||
|
if _, err := asn1.Unmarshal(der, &pkcs8{}); err == nil {
|
||||||
|
return nil, errors.New("x509: failed to parse private key (use ParsePKCS8PrivateKey instead for this key format)")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if priv.Version > 1 {
|
||||||
|
return nil, errors.New("x509: unsupported private key version")
|
||||||
|
}
|
||||||
|
|
||||||
|
if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
|
||||||
|
return nil, errors.New("x509: private key contains zero or negative value")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := new(rsa.PrivateKey)
|
||||||
|
key.PublicKey = rsa.PublicKey{
|
||||||
|
E: priv.E,
|
||||||
|
N: priv.N,
|
||||||
|
}
|
||||||
|
|
||||||
|
key.D = priv.D
|
||||||
|
key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
|
||||||
|
key.Primes[0] = priv.P
|
||||||
|
key.Primes[1] = priv.Q
|
||||||
|
for i, a := range priv.AdditionalPrimes {
|
||||||
|
if a.Prime.Sign() <= 0 {
|
||||||
|
return nil, errors.New("x509: private key contains zero or negative prime")
|
||||||
|
}
|
||||||
|
key.Primes[i+2] = a.Prime
|
||||||
|
// We ignore the other two values because rsa will calculate
|
||||||
|
// them as needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
err = key.Validate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key.Precompute()
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalPKCS1PrivateKey converts an RSA private key to PKCS#1, ASN.1 DER form.
|
||||||
|
//
|
||||||
|
// This kind of key is commonly encoded in PEM blocks of type "RSA PRIVATE KEY".
|
||||||
|
// For a more flexible key format which is not RSA specific, use
|
||||||
|
// MarshalPKCS8PrivateKey.
|
||||||
|
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
|
||||||
|
key.Precompute()
|
||||||
|
|
||||||
|
version := 0
|
||||||
|
if len(key.Primes) > 2 {
|
||||||
|
version = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
priv := pkcs1PrivateKey{
|
||||||
|
Version: version,
|
||||||
|
N: key.N,
|
||||||
|
E: key.PublicKey.E,
|
||||||
|
D: key.D,
|
||||||
|
P: key.Primes[0],
|
||||||
|
Q: key.Primes[1],
|
||||||
|
Dp: key.Precomputed.Dp,
|
||||||
|
Dq: key.Precomputed.Dq,
|
||||||
|
Qinv: key.Precomputed.Qinv,
|
||||||
|
}
|
||||||
|
|
||||||
|
priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
|
||||||
|
for i, values := range key.Precomputed.CRTValues {
|
||||||
|
priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
|
||||||
|
priv.AdditionalPrimes[i].Exp = values.Exp
|
||||||
|
priv.AdditionalPrimes[i].Coeff = values.Coeff
|
||||||
|
}
|
||||||
|
|
||||||
|
b, _ := asn1.Marshal(priv)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePKCS1PublicKey parses an RSA public key in PKCS#1, ASN.1 DER form.
|
||||||
|
//
|
||||||
|
// This kind of key is commonly encoded in PEM blocks of type "RSA PUBLIC KEY".
|
||||||
|
func ParsePKCS1PublicKey(der []byte) (*rsa.PublicKey, error) {
|
||||||
|
var pub pkcs1PublicKey
|
||||||
|
rest, err := asn1.Unmarshal(der, &pub)
|
||||||
|
if err != nil {
|
||||||
|
if _, err := asn1.Unmarshal(der, &publicKeyInfo{}); err == nil {
|
||||||
|
return nil, errors.New("x509: failed to parse public key (use ParsePKIXPublicKey instead for this key format)")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rest) > 0 {
|
||||||
|
return nil, asn1.SyntaxError{Msg: "trailing data"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pub.N.Sign() <= 0 || pub.E <= 0 {
|
||||||
|
return nil, errors.New("x509: public key contains zero or negative value")
|
||||||
|
}
|
||||||
|
if pub.E > 1<<31-1 {
|
||||||
|
return nil, errors.New("x509: public key contains large public exponent")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rsa.PublicKey{
|
||||||
|
E: pub.E,
|
||||||
|
N: pub.N,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalPKCS1PublicKey converts an RSA public key to PKCS#1, ASN.1 DER form.
|
||||||
|
//
|
||||||
|
// This kind of key is commonly encoded in PEM blocks of type "RSA PUBLIC KEY".
|
||||||
|
func MarshalPKCS1PublicKey(key *rsa.PublicKey) []byte {
|
||||||
|
derBytes, _ := asn1.Marshal(pkcs1PublicKey{
|
||||||
|
N: key.N,
|
||||||
|
E: key.E,
|
||||||
|
})
|
||||||
|
return derBytes
|
||||||
|
}
|
136
tempfork/x509/pkcs8.go
Normal file
136
tempfork/x509/pkcs8.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// Copyright 2011 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
|
||||||
|
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
|
||||||
|
// and RFC 5208.
|
||||||
|
type pkcs8 struct {
|
||||||
|
Version int
|
||||||
|
Algo pkix.AlgorithmIdentifier
|
||||||
|
PrivateKey []byte
|
||||||
|
// optional attributes omitted.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePKCS8PrivateKey parses an unencrypted private key in PKCS#8, ASN.1 DER form.
|
||||||
|
//
|
||||||
|
// It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey.
|
||||||
|
// More types might be supported in the future.
|
||||||
|
//
|
||||||
|
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
|
||||||
|
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
|
||||||
|
var privKey pkcs8
|
||||||
|
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
|
||||||
|
if _, err := asn1.Unmarshal(der, &ecPrivateKey{}); err == nil {
|
||||||
|
return nil, errors.New("x509: failed to parse private key (use ParseECPrivateKey instead for this key format)")
|
||||||
|
}
|
||||||
|
if _, err := asn1.Unmarshal(der, &pkcs1PrivateKey{}); err == nil {
|
||||||
|
return nil, errors.New("x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format)")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
|
||||||
|
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
|
||||||
|
case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA):
|
||||||
|
bytes := privKey.Algo.Parameters.FullBytes
|
||||||
|
namedCurveOID := new(asn1.ObjectIdentifier)
|
||||||
|
if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil {
|
||||||
|
namedCurveOID = nil
|
||||||
|
}
|
||||||
|
key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
|
||||||
|
case privKey.Algo.Algorithm.Equal(oidPublicKeyEd25519):
|
||||||
|
if l := len(privKey.Algo.Parameters.FullBytes); l != 0 {
|
||||||
|
return nil, errors.New("x509: invalid Ed25519 private key parameters")
|
||||||
|
}
|
||||||
|
var curvePrivateKey []byte
|
||||||
|
if _, err := asn1.Unmarshal(privKey.PrivateKey, &curvePrivateKey); err != nil {
|
||||||
|
return nil, fmt.Errorf("x509: invalid Ed25519 private key: %v", err)
|
||||||
|
}
|
||||||
|
if l := len(curvePrivateKey); l != ed25519.SeedSize {
|
||||||
|
return nil, fmt.Errorf("x509: invalid Ed25519 private key length: %d", l)
|
||||||
|
}
|
||||||
|
return ed25519.NewKeyFromSeed(curvePrivateKey), nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalPKCS8PrivateKey converts a private key to PKCS#8, ASN.1 DER form.
|
||||||
|
//
|
||||||
|
// The following key types are currently supported: *rsa.PrivateKey, *ecdsa.PrivateKey
|
||||||
|
// and ed25519.PrivateKey. Unsupported key types result in an error.
|
||||||
|
//
|
||||||
|
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
|
||||||
|
func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) {
|
||||||
|
var privKey pkcs8
|
||||||
|
|
||||||
|
switch k := key.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
privKey.Algo = pkix.AlgorithmIdentifier{
|
||||||
|
Algorithm: oidPublicKeyRSA,
|
||||||
|
Parameters: asn1.NullRawValue,
|
||||||
|
}
|
||||||
|
privKey.PrivateKey = MarshalPKCS1PrivateKey(k)
|
||||||
|
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
oid, ok := oidFromNamedCurve(k.Curve)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
|
||||||
|
}
|
||||||
|
|
||||||
|
oidBytes, err := asn1.Marshal(oid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("x509: failed to marshal curve OID: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
privKey.Algo = pkix.AlgorithmIdentifier{
|
||||||
|
Algorithm: oidPublicKeyECDSA,
|
||||||
|
Parameters: asn1.RawValue{
|
||||||
|
FullBytes: oidBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if privKey.PrivateKey, err = marshalECPrivateKeyWithOID(k, nil); err != nil {
|
||||||
|
return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
case ed25519.PrivateKey:
|
||||||
|
privKey.Algo = pkix.AlgorithmIdentifier{
|
||||||
|
Algorithm: oidPublicKeyEd25519,
|
||||||
|
}
|
||||||
|
curvePrivateKey, err := asn1.Marshal(k.Seed())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("x509: failed to marshal private key: %v", err)
|
||||||
|
}
|
||||||
|
privKey.PrivateKey = curvePrivateKey
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return asn1.Marshal(privKey)
|
||||||
|
}
|
140
tempfork/x509/pkcs8_test.go
Normal file
140
tempfork/x509/pkcs8_test.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Copyright 2011 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/hex"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generated using:
|
||||||
|
// openssl genrsa 1024 | openssl pkcs8 -topk8 -nocrypt
|
||||||
|
var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
|
||||||
|
|
||||||
|
// Generated using:
|
||||||
|
// openssl ecparam -genkey -name secp224r1 | openssl pkcs8 -topk8 -nocrypt
|
||||||
|
var pkcs8P224PrivateKeyHex = `3078020100301006072a8648ce3d020106052b810400210461305f020101041cca3d72b3e88fed2684576dad9b80a9180363a5424986900e3abcab3fa13c033a0004f8f2a6372872a4e61263ed893afb919576a4cacfecd6c081a2cbc76873cf4ba8530703c6042b3a00e2205087e87d2435d2e339e25702fae1`
|
||||||
|
|
||||||
|
// Generated using:
|
||||||
|
// openssl ecparam -genkey -name secp256r1 | openssl pkcs8 -topk8 -nocrypt
|
||||||
|
var pkcs8P256PrivateKeyHex = `308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420dad6b2f49ca774c36d8ae9517e935226f667c929498f0343d2424d0b9b591b43a14403420004b9c9b90095476afe7b860d8bd43568cab7bcb2eed7b8bf2fa0ce1762dd20b04193f859d2d782b1e4cbfd48492f1f533113a6804903f292258513837f07fda735`
|
||||||
|
|
||||||
|
// Generated using:
|
||||||
|
// openssl ecparam -genkey -name secp384r1 | openssl pkcs8 -topk8 -nocrypt
|
||||||
|
var pkcs8P384PrivateKeyHex = `3081b6020100301006072a8648ce3d020106052b8104002204819e30819b02010104309bf832f6aaaeacb78ce47ffb15e6fd0fd48683ae79df6eca39bfb8e33829ac94aa29d08911568684c2264a08a4ceb679a164036200049070ad4ed993c7770d700e9f6dc2baa83f63dd165b5507f98e8ff29b5d2e78ccbe05c8ddc955dbf0f7497e8222cfa49314fe4e269459f8e880147f70d785e530f2939e4bf9f838325bb1a80ad4cf59272ae0e5efe9a9dc33d874492596304bd3`
|
||||||
|
|
||||||
|
// Generated using:
|
||||||
|
// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt
|
||||||
|
//
|
||||||
|
// Note that OpenSSL will truncate the private key if it can (i.e. it emits it
|
||||||
|
// like an integer, even though it's an OCTET STRING field). Thus if you
|
||||||
|
// regenerate this you may, randomly, find that it's a byte shorter than
|
||||||
|
// expected and the Go test will fail to recreate it exactly.
|
||||||
|
var pkcs8P521PrivateKeyHex = `3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044200cfe0b87113a205cf291bb9a8cd1a74ac6c7b2ebb8199aaa9a5010d8b8012276fa3c22ac913369fa61beec2a3b8b4516bc049bde4fb3b745ac11b56ab23ac52e361a1818903818600040138f75acdd03fbafa4f047a8e4b272ba9d555c667962b76f6f232911a5786a0964e5edea6bd21a6f8725720958de049c6e3e6661c1c91b227cebee916c0319ed6ca003db0a3206d372229baf9dd25d868bf81140a518114803ce40c1855074d68c4e9dab9e65efba7064c703b400f1767f217dac82715ac1f6d88c74baf47a7971de4ea`
|
||||||
|
|
||||||
|
// From RFC 8410, Section 7.
|
||||||
|
var pkcs8Ed25519PrivateKeyHex = `302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842`
|
||||||
|
|
||||||
|
func TestPKCS8(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
keyHex string
|
||||||
|
keyType reflect.Type
|
||||||
|
curve elliptic.Curve
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "RSA private key",
|
||||||
|
keyHex: pkcs8RSAPrivateKeyHex,
|
||||||
|
keyType: reflect.TypeOf(&rsa.PrivateKey{}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "P-224 private key",
|
||||||
|
keyHex: pkcs8P224PrivateKeyHex,
|
||||||
|
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
|
||||||
|
curve: elliptic.P224(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "P-256 private key",
|
||||||
|
keyHex: pkcs8P256PrivateKeyHex,
|
||||||
|
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
|
||||||
|
curve: elliptic.P256(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "P-384 private key",
|
||||||
|
keyHex: pkcs8P384PrivateKeyHex,
|
||||||
|
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
|
||||||
|
curve: elliptic.P384(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "P-521 private key",
|
||||||
|
keyHex: pkcs8P521PrivateKeyHex,
|
||||||
|
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
|
||||||
|
curve: elliptic.P521(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Ed25519 private key",
|
||||||
|
keyHex: pkcs8Ed25519PrivateKeyHex,
|
||||||
|
keyType: reflect.TypeOf(ed25519.PrivateKey{}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
derBytes, err := hex.DecodeString(test.keyHex)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: failed to decode hex: %s", test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
privKey, err := ParsePKCS8PrivateKey(derBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: failed to decode PKCS#8: %s", test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(privKey) != test.keyType {
|
||||||
|
t.Errorf("%s: decoded PKCS#8 returned unexpected key type: %T", test.name, privKey)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ecKey, isEC := privKey.(*ecdsa.PrivateKey); isEC && ecKey.Curve != test.curve {
|
||||||
|
t.Errorf("%s: decoded PKCS#8 returned unexpected curve %#v", test.name, ecKey.Curve)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
reserialised, err := MarshalPKCS8PrivateKey(privKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: failed to marshal into PKCS#8: %s", test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(derBytes, reserialised) {
|
||||||
|
t.Errorf("%s: marshaled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hexPKCS8TestPKCS1Key = "3082025c02010002818100b1a1e0945b9289c4d3f1329f8a982c4a2dcd59bfd372fb8085a9c517554607ebd2f7990eef216ac9f4605f71a03b04f42a5255b158cf8e0844191f5119348baa44c35056e20609bcf9510f30ead4b481c81d7865fb27b8e0090e112b717f3ee08cdfc4012da1f1f7cf2a1bc34c73a54a12b06372d09714742dd7895eadde4aa5020301000102818062b7fa1db93e993e40237de4d89b7591cc1ea1d04fed4904c643f17ae4334557b4295270d0491c161cb02a9af557978b32b20b59c267a721c4e6c956c2d147046e9ae5f2da36db0106d70021fa9343455f8f973a4b355a26fd19e6b39dee0405ea2b32deddf0f4817759ef705d02b34faab9ca93c6766e9f722290f119f34449024100d9c29a4a013a90e35fd1be14a3f747c589fac613a695282d61812a711906b8a0876c6181f0333ca1066596f57bff47e7cfcabf19c0fc69d9cd76df743038b3cb024100d0d3546fecf879b5551f2bd2c05e6385f2718a08a6face3d2aecc9d7e03645a480a46c81662c12ad6bd6901e3bd4f38029462de7290859567cdf371c79088d4f024100c254150657e460ea58573fcf01a82a4791e3d6223135c8bdfed69afe84fbe7857274f8eb5165180507455f9b4105c6b08b51fe8a481bb986a202245576b713530240045700003b7a867d0041df9547ae2e7f50248febd21c9040b12dae9c2feab0d3d4609668b208e4727a3541557f84d372ac68eaf74ce1018a4c9a0ef92682c8fd02405769731480bb3a4570abf422527c5f34bf732fa6c1e08cc322753c511ce055fac20fc770025663ad3165324314df907f1f1942f0448a7e9cdbf87ecd98b92156"
|
||||||
|
const hexPKCS8TestECKey = "3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50"
|
||||||
|
|
||||||
|
var pkcs8MismatchKeyTests = []struct {
|
||||||
|
hexKey string
|
||||||
|
errorContains string
|
||||||
|
}{
|
||||||
|
{hexKey: hexPKCS8TestECKey, errorContains: "use ParseECPrivateKey instead"},
|
||||||
|
{hexKey: hexPKCS8TestPKCS1Key, errorContains: "use ParsePKCS1PrivateKey instead"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPKCS8MismatchKeyFormat(t *testing.T) {
|
||||||
|
for i, test := range pkcs8MismatchKeyTests {
|
||||||
|
derBytes, _ := hex.DecodeString(test.hexKey)
|
||||||
|
_, err := ParsePKCS8PrivateKey(derBytes)
|
||||||
|
if !strings.Contains(err.Error(), test.errorContains) {
|
||||||
|
t.Errorf("#%d: expected error containing %q, got %s", i, test.errorContains, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
tempfork/x509/root.go
Normal file
25
tempfork/x509/root.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2012 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 x509
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
var (
|
||||||
|
once sync.Once
|
||||||
|
systemRoots *CertPool
|
||||||
|
systemRootsErr error
|
||||||
|
)
|
||||||
|
|
||||||
|
func systemRootsPool() *CertPool {
|
||||||
|
once.Do(initSystemRoots)
|
||||||
|
return systemRoots
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSystemRoots() {
|
||||||
|
systemRoots, systemRootsErr = loadSystemRoots()
|
||||||
|
if systemRootsErr != nil {
|
||||||
|
systemRoots = nil
|
||||||
|
}
|
||||||
|
}
|
10
tempfork/x509/root_aix.go
Normal file
10
tempfork/x509/root_aix.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// 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 x509
|
||||||
|
|
||||||
|
// Possible certificate files; stop after finding one.
|
||||||
|
var certFiles = []string{
|
||||||
|
"/var/ssl/certs/ca-bundle.crt",
|
||||||
|
}
|
15
tempfork/x509/root_bsd.go
Normal file
15
tempfork/x509/root_bsd.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
// Possible certificate files; stop after finding one.
|
||||||
|
var certFiles = []string{
|
||||||
|
"/usr/local/etc/ssl/cert.pem", // FreeBSD
|
||||||
|
"/etc/ssl/cert.pem", // OpenBSD
|
||||||
|
"/usr/local/share/certs/ca-root-nss.crt", // DragonFly
|
||||||
|
"/etc/openssl/certs/ca-certificates.crt", // NetBSD
|
||||||
|
}
|
314
tempfork/x509/root_cgo_darwin.go
Normal file
314
tempfork/x509/root_cgo_darwin.go
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
// +build cgo,!arm64,!ios
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -mmacosx-version-min=10.11
|
||||||
|
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <Security/Security.h>
|
||||||
|
|
||||||
|
static Boolean isSSLPolicy(SecPolicyRef policyRef) {
|
||||||
|
if (!policyRef) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CFDictionaryRef properties = SecPolicyCopyProperties(policyRef);
|
||||||
|
if (properties == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Boolean isSSL = false;
|
||||||
|
CFTypeRef value = NULL;
|
||||||
|
if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value)) {
|
||||||
|
isSSL = CFEqual(value, kSecPolicyAppleSSL);
|
||||||
|
}
|
||||||
|
CFRelease(properties);
|
||||||
|
return isSSL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sslTrustSettingsResult obtains the final kSecTrustSettingsResult value
|
||||||
|
// for a certificate in the user or admin domain, combining usage constraints
|
||||||
|
// for the SSL SecTrustSettingsPolicy, ignoring SecTrustSettingsKeyUsage and
|
||||||
|
// kSecTrustSettingsAllowedError.
|
||||||
|
// https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
|
||||||
|
static SInt32 sslTrustSettingsResult(SecCertificateRef cert) {
|
||||||
|
CFArrayRef trustSettings = NULL;
|
||||||
|
OSStatus err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &trustSettings);
|
||||||
|
|
||||||
|
// According to Apple's SecTrustServer.c, "user trust settings overrule admin trust settings",
|
||||||
|
// but the rules of the override are unclear. Let's assume admin trust settings are applicable
|
||||||
|
// if and only if user trust settings fail to load or are NULL.
|
||||||
|
if (err != errSecSuccess || trustSettings == NULL) {
|
||||||
|
if (trustSettings != NULL) CFRelease(trustSettings);
|
||||||
|
err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &trustSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// > no trust settings [...] means "this certificate must be verified to a known trusted certificate”
|
||||||
|
// (Should this cause a fallback from user to admin domain? It's unclear.)
|
||||||
|
if (err != errSecSuccess || trustSettings == NULL) {
|
||||||
|
if (trustSettings != NULL) CFRelease(trustSettings);
|
||||||
|
return kSecTrustSettingsResultUnspecified;
|
||||||
|
}
|
||||||
|
|
||||||
|
// > An empty trust settings array means "always trust this certificate” with an
|
||||||
|
// > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot.
|
||||||
|
if (CFArrayGetCount(trustSettings) == 0) {
|
||||||
|
CFRelease(trustSettings);
|
||||||
|
return kSecTrustSettingsResultTrustRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
|
||||||
|
// but the Go linker's internal linking mode can't handle CFSTR relocations.
|
||||||
|
// Create our own dynamic string instead and release it below.
|
||||||
|
CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString(
|
||||||
|
NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
|
||||||
|
CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString(
|
||||||
|
NULL, "kSecTrustSettingsPolicy", kCFStringEncodingUTF8);
|
||||||
|
CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString(
|
||||||
|
NULL, "kSecTrustSettingsPolicyString", kCFStringEncodingUTF8);
|
||||||
|
|
||||||
|
CFIndex m; SInt32 result = 0;
|
||||||
|
for (m = 0; m < CFArrayGetCount(trustSettings); m++) {
|
||||||
|
CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m);
|
||||||
|
|
||||||
|
// First, check if this trust setting is constrained to a non-SSL policy.
|
||||||
|
SecPolicyRef policyRef;
|
||||||
|
if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsPolicy, (const void**)&policyRef)) {
|
||||||
|
if (!isSSLPolicy(policyRef)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CFDictionaryContainsKey(tSetting, _kSecTrustSettingsPolicyString)) {
|
||||||
|
// Restricted to a hostname, not a root.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFNumberRef cfNum;
|
||||||
|
if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsResult, (const void**)&cfNum)) {
|
||||||
|
CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
|
||||||
|
} else {
|
||||||
|
// > If this key is not present, a default value of
|
||||||
|
// > kSecTrustSettingsResultTrustRoot is assumed.
|
||||||
|
result = kSecTrustSettingsResultTrustRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If multiple dictionaries match, we are supposed to "OR" them,
|
||||||
|
// the semantics of which are not clear. Since TrustRoot and TrustAsRoot
|
||||||
|
// are mutually exclusive, Deny should probably override, and Invalid and
|
||||||
|
// Unspecified be overridden, approximate this by stopping at the first
|
||||||
|
// TrustRoot, TrustAsRoot or Deny.
|
||||||
|
if (result == kSecTrustSettingsResultTrustRoot) {
|
||||||
|
break;
|
||||||
|
} else if (result == kSecTrustSettingsResultTrustAsRoot) {
|
||||||
|
break;
|
||||||
|
} else if (result == kSecTrustSettingsResultDeny) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If trust settings are present, but none of them match the policy...
|
||||||
|
// the docs don't tell us what to do.
|
||||||
|
//
|
||||||
|
// "Trust settings for a given use apply if any of the dictionaries in the
|
||||||
|
// certificate’s trust settings array satisfies the specified use." suggests
|
||||||
|
// that it's as if there were no trust settings at all, so we should probably
|
||||||
|
// fallback to the admin trust settings. TODO.
|
||||||
|
if (result == 0) {
|
||||||
|
result = kSecTrustSettingsResultUnspecified;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(_kSecTrustSettingsPolicy);
|
||||||
|
CFRelease(_kSecTrustSettingsPolicyString);
|
||||||
|
CFRelease(_kSecTrustSettingsResult);
|
||||||
|
CFRelease(trustSettings);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRootCertificate reports whether Subject and Issuer match.
|
||||||
|
static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) {
|
||||||
|
CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, errRef);
|
||||||
|
if (*errRef != NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, errRef);
|
||||||
|
if (*errRef != NULL) {
|
||||||
|
CFRelease(subjectName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Boolean equal = CFEqual(subjectName, issuerName);
|
||||||
|
CFRelease(subjectName);
|
||||||
|
CFRelease(issuerName);
|
||||||
|
return equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyPEMRoots fetches the system's list of trusted X.509 root certificates
|
||||||
|
// for the kSecTrustSettingsPolicy SSL.
|
||||||
|
//
|
||||||
|
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
|
||||||
|
// certificates of the system. On failure, the function returns -1.
|
||||||
|
// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
|
||||||
|
//
|
||||||
|
// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
|
||||||
|
// be released (using CFRelease) after we've consumed its content.
|
||||||
|
static int CopyPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (debugDarwinRoots) {
|
||||||
|
fprintf(stderr, "crypto/x509: kSecTrustSettingsResultInvalid = %d\n", kSecTrustSettingsResultInvalid);
|
||||||
|
fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n", kSecTrustSettingsResultTrustRoot);
|
||||||
|
fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n", kSecTrustSettingsResultTrustAsRoot);
|
||||||
|
fprintf(stderr, "crypto/x509: kSecTrustSettingsResultDeny = %d\n", kSecTrustSettingsResultDeny);
|
||||||
|
fprintf(stderr, "crypto/x509: kSecTrustSettingsResultUnspecified = %d\n", kSecTrustSettingsResultUnspecified);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get certificates from all domains, not just System, this lets
|
||||||
|
// the user add CAs to their "login" keychain, and Admins to add
|
||||||
|
// to the "System" keychain
|
||||||
|
SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
|
||||||
|
kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainUser };
|
||||||
|
|
||||||
|
int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
|
||||||
|
if (pemRoots == NULL || untrustedPemRoots == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||||
|
CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||||
|
for (i = 0; i < numDomains; i++) {
|
||||||
|
int j;
|
||||||
|
CFArrayRef certs = NULL;
|
||||||
|
OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
|
||||||
|
if (err != noErr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFIndex numCerts = CFArrayGetCount(certs);
|
||||||
|
for (j = 0; j < numCerts; j++) {
|
||||||
|
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
|
||||||
|
if (cert == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SInt32 result;
|
||||||
|
if (domains[i] == kSecTrustSettingsDomainSystem) {
|
||||||
|
// Certs found in the system domain are always trusted. If the user
|
||||||
|
// configures "Never Trust" on such a cert, it will also be found in the
|
||||||
|
// admin or user domain, causing it to be added to untrustedPemRoots. The
|
||||||
|
// Go code will then clean this up.
|
||||||
|
result = kSecTrustSettingsResultTrustRoot;
|
||||||
|
} else {
|
||||||
|
result = sslTrustSettingsResult(cert);
|
||||||
|
if (debugDarwinRoots) {
|
||||||
|
CFErrorRef errRef = NULL;
|
||||||
|
CFStringRef summary = SecCertificateCopyShortDescription(NULL, cert, &errRef);
|
||||||
|
if (errRef != NULL) {
|
||||||
|
fprintf(stderr, "crypto/x509: SecCertificateCopyShortDescription failed\n");
|
||||||
|
CFRelease(errRef);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFIndex length = CFStringGetLength(summary);
|
||||||
|
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
|
||||||
|
char *buffer = malloc(maxSize);
|
||||||
|
if (CFStringGetCString(summary, buffer, maxSize, kCFStringEncodingUTF8)) {
|
||||||
|
fprintf(stderr, "crypto/x509: %s returned %d\n", buffer, (int)result);
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
CFRelease(summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CFMutableDataRef appendTo;
|
||||||
|
// > Note the distinction between the results kSecTrustSettingsResultTrustRoot
|
||||||
|
// > and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to
|
||||||
|
// > root (self-signed) certificates; the latter can only be applied to
|
||||||
|
// > non-root certificates.
|
||||||
|
if (result == kSecTrustSettingsResultTrustRoot) {
|
||||||
|
CFErrorRef errRef = NULL;
|
||||||
|
if (!isRootCertificate(cert, &errRef) || errRef != NULL) {
|
||||||
|
if (errRef != NULL) CFRelease(errRef);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendTo = combinedData;
|
||||||
|
} else if (result == kSecTrustSettingsResultTrustAsRoot) {
|
||||||
|
CFErrorRef errRef = NULL;
|
||||||
|
if (isRootCertificate(cert, &errRef) || errRef != NULL) {
|
||||||
|
if (errRef != NULL) CFRelease(errRef);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendTo = combinedData;
|
||||||
|
} else if (result == kSecTrustSettingsResultDeny) {
|
||||||
|
appendTo = combinedUntrustedData;
|
||||||
|
} else if (result == kSecTrustSettingsResultUnspecified) {
|
||||||
|
// Certificates with unspecified trust should probably be added to a pool of
|
||||||
|
// intermediates for chain building, or checked for transitive trust and
|
||||||
|
// added to the root pool (which is an imprecise approximation because it
|
||||||
|
// cuts chains short) but we don't support either at the moment. TODO.
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFDataRef data = NULL;
|
||||||
|
err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
|
||||||
|
if (err != noErr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (data != NULL) {
|
||||||
|
CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
|
||||||
|
CFRelease(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CFRelease(certs);
|
||||||
|
}
|
||||||
|
*pemRoots = combinedData;
|
||||||
|
*untrustedPemRoots = combinedUntrustedData;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadSystemRoots() (*CertPool, error) {
|
||||||
|
var data, untrustedData C.CFDataRef
|
||||||
|
err := C.CopyPEMRoots(&data, &untrustedData, C.bool(debugDarwinRoots))
|
||||||
|
if err == -1 {
|
||||||
|
return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
|
||||||
|
}
|
||||||
|
defer C.CFRelease(C.CFTypeRef(data))
|
||||||
|
defer C.CFRelease(C.CFTypeRef(untrustedData))
|
||||||
|
|
||||||
|
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
|
||||||
|
roots := NewCertPool()
|
||||||
|
roots.AppendCertsFromPEM(buf)
|
||||||
|
|
||||||
|
if C.CFDataGetLength(untrustedData) == 0 {
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
|
||||||
|
untrustedRoots := NewCertPool()
|
||||||
|
untrustedRoots.AppendCertsFromPEM(buf)
|
||||||
|
|
||||||
|
trustedRoots := NewCertPool()
|
||||||
|
for _, c := range roots.certs {
|
||||||
|
if !untrustedRoots.contains(c) {
|
||||||
|
trustedRoots.AddCert(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trustedRoots, nil
|
||||||
|
}
|
288
tempfork/x509/root_darwin.go
Normal file
288
tempfork/x509/root_darwin.go
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
//go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var debugDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1")
|
||||||
|
|
||||||
|
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code is only used when compiling without cgo.
|
||||||
|
// It is here, instead of root_nocgo_darwin.go, so that tests can check it
|
||||||
|
// even if the tests are run with cgo enabled.
|
||||||
|
// The linker will not include these unused functions in binaries built with cgo enabled.
|
||||||
|
|
||||||
|
// execSecurityRoots finds the macOS list of trusted root certificates
|
||||||
|
// using only command-line tools. This is our fallback path when cgo isn't available.
|
||||||
|
//
|
||||||
|
// The strategy is as follows:
|
||||||
|
//
|
||||||
|
// 1. Run "security trust-settings-export" and "security
|
||||||
|
// trust-settings-export -d" to discover the set of certs with some
|
||||||
|
// user-tweaked trust policy. We're too lazy to parse the XML
|
||||||
|
// (Issue 26830) to understand what the trust
|
||||||
|
// policy actually is. We just learn that there is _some_ policy.
|
||||||
|
//
|
||||||
|
// 2. Run "security find-certificate" to dump the list of system root
|
||||||
|
// CAs in PEM format.
|
||||||
|
//
|
||||||
|
// 3. For each dumped cert, conditionally verify it with "security
|
||||||
|
// verify-cert" if that cert was in the set discovered in Step 1.
|
||||||
|
// Without the Step 1 optimization, running "security verify-cert"
|
||||||
|
// 150-200 times takes 3.5 seconds. With the optimization, the
|
||||||
|
// whole process takes about 180 milliseconds with 1 untrusted root
|
||||||
|
// CA. (Compared to 110ms in the cgo path)
|
||||||
|
func execSecurityRoots() (*CertPool, error) {
|
||||||
|
hasPolicy, err := getCertsWithTrustPolicy()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if debugDarwinRoots {
|
||||||
|
fmt.Fprintf(os.Stderr, "crypto/x509: %d certs have a trust policy\n", len(hasPolicy))
|
||||||
|
}
|
||||||
|
|
||||||
|
keychains := []string{"/Library/Keychains/System.keychain"}
|
||||||
|
|
||||||
|
// Note that this results in trusting roots from $HOME/... (the environment
|
||||||
|
// variable), which might not be expected.
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
if debugDarwinRoots {
|
||||||
|
fmt.Fprintf(os.Stderr, "crypto/x509: can't get user home directory: %v\n", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keychains = append(keychains,
|
||||||
|
filepath.Join(home, "/Library/Keychains/login.keychain"),
|
||||||
|
|
||||||
|
// Fresh installs of Sierra use a slightly different path for the login keychain
|
||||||
|
filepath.Join(home, "/Library/Keychains/login.keychain-db"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type rootCandidate struct {
|
||||||
|
c *Certificate
|
||||||
|
system bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
roots = NewCertPool()
|
||||||
|
numVerified int // number of execs of 'security verify-cert', for debug stats
|
||||||
|
wg sync.WaitGroup
|
||||||
|
verifyCh = make(chan rootCandidate)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Using 4 goroutines to pipe into verify-cert seems to be
|
||||||
|
// about the best we can do. The verify-cert binary seems to
|
||||||
|
// just RPC to another server with coarse locking anyway, so
|
||||||
|
// running 16 at a time for instance doesn't help at all. Due
|
||||||
|
// to the "if hasPolicy" check below, though, we will rarely
|
||||||
|
// (or never) call verify-cert on stock macOS systems, though.
|
||||||
|
// The hope is that we only call verify-cert when the user has
|
||||||
|
// tweaked their trust policy. These 4 goroutines are only
|
||||||
|
// defensive in the pathological case of many trust edits.
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for cert := range verifyCh {
|
||||||
|
sha1CapHex := fmt.Sprintf("%X", sha1.Sum(cert.c.Raw))
|
||||||
|
|
||||||
|
var valid bool
|
||||||
|
verifyChecks := 0
|
||||||
|
if hasPolicy[sha1CapHex] {
|
||||||
|
verifyChecks++
|
||||||
|
valid = verifyCertWithSystem(cert.c)
|
||||||
|
} else {
|
||||||
|
// Certificates not in SystemRootCertificates without user
|
||||||
|
// or admin trust settings are not trusted.
|
||||||
|
valid = cert.system
|
||||||
|
}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
numVerified += verifyChecks
|
||||||
|
if valid {
|
||||||
|
roots.AddCert(cert.c)
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
err = forEachCertInKeychains(keychains, func(cert *Certificate) {
|
||||||
|
verifyCh <- rootCandidate{c: cert, system: false}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
close(verifyCh)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = forEachCertInKeychains([]string{
|
||||||
|
"/System/Library/Keychains/SystemRootCertificates.keychain",
|
||||||
|
}, func(cert *Certificate) {
|
||||||
|
verifyCh <- rootCandidate{c: cert, system: true}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
close(verifyCh)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
close(verifyCh)
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if debugDarwinRoots {
|
||||||
|
fmt.Fprintf(os.Stderr, "crypto/x509: ran security verify-cert %d times\n", numVerified)
|
||||||
|
}
|
||||||
|
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func forEachCertInKeychains(paths []string, f func(*Certificate)) error {
|
||||||
|
args := append([]string{"find-certificate", "-a", "-p"}, paths...)
|
||||||
|
cmd := exec.Command("/usr/bin/security", args...)
|
||||||
|
data, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for len(data) > 0 {
|
||||||
|
var block *pem.Block
|
||||||
|
block, data = pem.Decode(data)
|
||||||
|
if block == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cert, err := ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f(cert)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyCertWithSystem(cert *Certificate) bool {
|
||||||
|
data := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "CERTIFICATE", Bytes: cert.Raw,
|
||||||
|
})
|
||||||
|
|
||||||
|
f, err := ioutil.TempFile("", "cert")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
if _, err := f.Write(data); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
cmd := exec.Command("/usr/bin/security", "verify-cert", "-p", "ssl", "-c", f.Name(), "-l", "-L")
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
if debugDarwinRoots {
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
}
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
if debugDarwinRoots {
|
||||||
|
fmt.Fprintf(os.Stderr, "crypto/x509: verify-cert rejected %s: %q\n", cert.Subject, bytes.TrimSpace(stderr.Bytes()))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if debugDarwinRoots {
|
||||||
|
fmt.Fprintf(os.Stderr, "crypto/x509: verify-cert approved %s\n", cert.Subject)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCertsWithTrustPolicy returns the set of certs that have a
|
||||||
|
// possibly-altered trust policy. The keys of the map are capitalized
|
||||||
|
// sha1 hex of the raw cert.
|
||||||
|
// They are the certs that should be checked against `security
|
||||||
|
// verify-cert` to see whether the user altered the default trust
|
||||||
|
// settings. This code is only used for cgo-disabled builds.
|
||||||
|
func getCertsWithTrustPolicy() (map[string]bool, error) {
|
||||||
|
set := map[string]bool{}
|
||||||
|
td, err := ioutil.TempDir("", "x509trustpolicy")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(td)
|
||||||
|
run := func(file string, args ...string) error {
|
||||||
|
file = filepath.Join(td, file)
|
||||||
|
args = append(args, file)
|
||||||
|
cmd := exec.Command("/usr/bin/security", args...)
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
// If there are no trust settings, the
|
||||||
|
// `security trust-settings-export` command
|
||||||
|
// fails with:
|
||||||
|
// exit status 1, SecTrustSettingsCreateExternalRepresentation: No Trust Settings were found.
|
||||||
|
// Rather than match on English substrings that are probably
|
||||||
|
// localized on macOS, just interpret any failure to mean that
|
||||||
|
// there are no trust settings.
|
||||||
|
if debugDarwinRoots {
|
||||||
|
fmt.Fprintf(os.Stderr, "crypto/x509: exec %q: %v, %s\n", cmd.Args, err, stderr.Bytes())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Gather all the runs of 40 capitalized hex characters.
|
||||||
|
br := bufio.NewReader(f)
|
||||||
|
var hexBuf bytes.Buffer
|
||||||
|
for {
|
||||||
|
b, err := br.ReadByte()
|
||||||
|
isHex := ('A' <= b && b <= 'F') || ('0' <= b && b <= '9')
|
||||||
|
if isHex {
|
||||||
|
hexBuf.WriteByte(b)
|
||||||
|
} else {
|
||||||
|
if hexBuf.Len() == 40 {
|
||||||
|
set[hexBuf.String()] = true
|
||||||
|
}
|
||||||
|
hexBuf.Reset()
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := run("user", "trust-settings-export"); err != nil {
|
||||||
|
return nil, fmt.Errorf("dump-trust-settings (user): %v", err)
|
||||||
|
}
|
||||||
|
if err := run("admin", "trust-settings-export", "-d"); err != nil {
|
||||||
|
return nil, fmt.Errorf("dump-trust-settings (admin): %v", err)
|
||||||
|
}
|
||||||
|
return set, nil
|
||||||
|
}
|
4311
tempfork/x509/root_darwin_arm64.go
Normal file
4311
tempfork/x509/root_darwin_arm64.go
Normal file
File diff suppressed because it is too large
Load Diff
184
tempfork/x509/root_darwin_arm_gen.go
Normal file
184
tempfork/x509/root_darwin_arm_gen.go
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
// Generates root_darwin_arm64.go.
|
||||||
|
//
|
||||||
|
// As of iOS 8, there is no API for querying the system trusted X.509 root
|
||||||
|
// certificates. We could use SecTrustEvaluate to verify that a trust chain
|
||||||
|
// exists for a certificate, but the x509 API requires returning the entire
|
||||||
|
// chain.
|
||||||
|
//
|
||||||
|
// Apple publishes the list of trusted root certificates for iOS on
|
||||||
|
// support.apple.com. So we parse the list and extract the certificates from
|
||||||
|
// an OS X machine and embed them into the x509 package.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var output = flag.String("output", "root_darwin_arm64.go", "file name to write")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
certs, err := selectCerts()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "// Code generated by root_darwin_arm_gen --output %s; DO NOT EDIT.\n", *output)
|
||||||
|
fmt.Fprintf(buf, "%s", header)
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "const systemRootsPEM = `\n")
|
||||||
|
for _, cert := range certs {
|
||||||
|
b := &pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: cert.Raw,
|
||||||
|
}
|
||||||
|
if err := pem.Encode(buf, b); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, "`")
|
||||||
|
|
||||||
|
source, err := format.Source(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("source format error:", err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(*output, source, 0644); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectCerts() ([]*x509.Certificate, error) {
|
||||||
|
ids, err := fetchCertIDs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scerts, err := sysCerts()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var certs []*x509.Certificate
|
||||||
|
for _, id := range ids {
|
||||||
|
if c, ok := scerts[id.fingerprint]; ok {
|
||||||
|
certs = append(certs, c)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("WARNING: cannot find certificate: %s (fingerprint: %s)\n", id.name, id.fingerprint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return certs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysCerts() (certs map[string]*x509.Certificate, err error) {
|
||||||
|
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
|
||||||
|
data, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certs = make(map[string]*x509.Certificate)
|
||||||
|
for len(data) > 0 {
|
||||||
|
var block *pem.Block
|
||||||
|
block, data = pem.Decode(data)
|
||||||
|
if block == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fingerprint := sha256.Sum256(cert.Raw)
|
||||||
|
certs[hex.EncodeToString(fingerprint[:])] = cert
|
||||||
|
}
|
||||||
|
return certs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type certID struct {
|
||||||
|
name string
|
||||||
|
fingerprint string
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
|
||||||
|
func fetchCertIDs() ([]certID, error) {
|
||||||
|
// Download the iOS 11 support page. The index for all iOS versions is here:
|
||||||
|
// https://support.apple.com/en-us/HT204132
|
||||||
|
resp, err := http.Get("https://support.apple.com/en-us/HT208125")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
text := string(body)
|
||||||
|
text = text[strings.Index(text, "<div id=trusted"):]
|
||||||
|
text = text[:strings.Index(text, "</div>")]
|
||||||
|
|
||||||
|
var ids []certID
|
||||||
|
cols := make(map[string]int)
|
||||||
|
for i, rowmatch := range regexp.MustCompile("(?s)<tr>(.*?)</tr>").FindAllStringSubmatch(text, -1) {
|
||||||
|
row := rowmatch[1]
|
||||||
|
if i == 0 {
|
||||||
|
// Parse table header row to extract column names
|
||||||
|
for i, match := range regexp.MustCompile("(?s)<th>(.*?)</th>").FindAllStringSubmatch(row, -1) {
|
||||||
|
cols[match[1]] = i
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
values := regexp.MustCompile("(?s)<td>(.*?)</td>").FindAllStringSubmatch(row, -1)
|
||||||
|
name := values[cols["Certificate name"]][1]
|
||||||
|
fingerprint := values[cols["Fingerprint (SHA-256)"]][1]
|
||||||
|
fingerprint = strings.ReplaceAll(fingerprint, "<br>", "")
|
||||||
|
fingerprint = strings.ReplaceAll(fingerprint, "\n", "")
|
||||||
|
fingerprint = strings.ReplaceAll(fingerprint, " ", "")
|
||||||
|
fingerprint = strings.ToLower(fingerprint)
|
||||||
|
|
||||||
|
ids = append(ids, certID{
|
||||||
|
name: name,
|
||||||
|
fingerprint: fingerprint,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = `
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !x509omitbundledroots
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
func loadSystemRoots() (*CertPool, error) {
|
||||||
|
p := NewCertPool()
|
||||||
|
p.AppendCertsFromPEM([]byte(systemRootsPEM))
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
`
|
129
tempfork/x509/root_darwin_test.go
Normal file
129
tempfork/x509/root_darwin_test.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright 2013 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSystemRoots(t *testing.T) {
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "arm64":
|
||||||
|
t.Skipf("skipping on %s/%s, no system root", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
sysRoots := systemRootsPool() // actual system roots
|
||||||
|
sysRootsDuration := time.Since(t0)
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
execRoots, err := execSecurityRoots() // non-cgo roots
|
||||||
|
execSysRootsDuration := time.Since(t1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read system roots: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf(" cgo sys roots: %v", sysRootsDuration)
|
||||||
|
t.Logf("non-cgo sys roots: %v", execSysRootsDuration)
|
||||||
|
|
||||||
|
// On Mavericks, there are 212 bundled certs, at least there was at
|
||||||
|
// one point in time on one machine. (Maybe it was a corp laptop
|
||||||
|
// with extra certs?) Other OS X users report 135, 142, 145...
|
||||||
|
// Let's try requiring at least 100, since this is just a sanity
|
||||||
|
// check.
|
||||||
|
if want, have := 100, len(sysRoots.certs); have < want {
|
||||||
|
t.Errorf("want at least %d system roots, have %d", want, have)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch any intermediate certificate that verify-cert might be aware of.
|
||||||
|
out, err := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p",
|
||||||
|
"/Library/Keychains/System.keychain",
|
||||||
|
filepath.Join(os.Getenv("HOME"), "/Library/Keychains/login.keychain"),
|
||||||
|
filepath.Join(os.Getenv("HOME"), "/Library/Keychains/login.keychain-db")).Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
allCerts := NewCertPool()
|
||||||
|
allCerts.AppendCertsFromPEM(out)
|
||||||
|
|
||||||
|
// Check that the two cert pools are the same.
|
||||||
|
sysPool := make(map[string]*Certificate, len(sysRoots.certs))
|
||||||
|
for _, c := range sysRoots.certs {
|
||||||
|
sysPool[string(c.Raw)] = c
|
||||||
|
}
|
||||||
|
for _, c := range execRoots.certs {
|
||||||
|
if _, ok := sysPool[string(c.Raw)]; ok {
|
||||||
|
delete(sysPool, string(c.Raw))
|
||||||
|
} else {
|
||||||
|
// verify-cert lets in certificates that are not trusted roots, but
|
||||||
|
// are signed by trusted roots. This is not great, but unavoidable
|
||||||
|
// until we parse real policies without cgo, so confirm that's the
|
||||||
|
// case and skip them.
|
||||||
|
if _, err := c.Verify(VerifyOptions{
|
||||||
|
Roots: sysRoots,
|
||||||
|
Intermediates: allCerts,
|
||||||
|
KeyUsages: []ExtKeyUsage{ExtKeyUsageAny},
|
||||||
|
CurrentTime: c.NotBefore, // verify-cert does not check expiration
|
||||||
|
}); err != nil {
|
||||||
|
t.Errorf("certificate only present in non-cgo pool: %v (verify error: %v)", c.Subject, err)
|
||||||
|
} else {
|
||||||
|
t.Logf("signed certificate only present in non-cgo pool (acceptable): %v", c.Subject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, c := range sysPool {
|
||||||
|
// The nocgo codepath uses verify-cert with the ssl policy, which also
|
||||||
|
// happens to check EKUs, so some certificates will appear only in the
|
||||||
|
// cgo pool. We can't easily make them consistent because the EKU check
|
||||||
|
// is only applied to the certificates passed to verify-cert.
|
||||||
|
var ekuOk bool
|
||||||
|
for _, eku := range c.ExtKeyUsage {
|
||||||
|
if eku == ExtKeyUsageServerAuth || eku == ExtKeyUsageNetscapeServerGatedCrypto ||
|
||||||
|
eku == ExtKeyUsageMicrosoftServerGatedCrypto || eku == ExtKeyUsageAny {
|
||||||
|
ekuOk = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(c.ExtKeyUsage) == 0 && len(c.UnknownExtKeyUsage) == 0 {
|
||||||
|
ekuOk = true
|
||||||
|
}
|
||||||
|
if !ekuOk {
|
||||||
|
t.Logf("off-EKU certificate only present in cgo pool (acceptable): %v", c.Subject)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same for expired certificates. We don't chain to them anyway.
|
||||||
|
now := time.Now()
|
||||||
|
if now.Before(c.NotBefore) || now.After(c.NotAfter) {
|
||||||
|
t.Logf("expired certificate only present in cgo pool (acceptable): %v", c.Subject)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// On 10.11 there are five unexplained roots that only show up from the
|
||||||
|
// C API. They have in common the fact that they are old, 1024-bit
|
||||||
|
// certificates. It's arguably better to ignore them anyway.
|
||||||
|
if key, ok := c.PublicKey.(*rsa.PublicKey); ok && key.N.BitLen() == 1024 {
|
||||||
|
t.Logf("1024-bit certificate only present in cgo pool (acceptable): %v", c.Subject)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Errorf("certificate only present in cgo pool: %v", c.Subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Failed() && debugDarwinRoots {
|
||||||
|
cmd := exec.Command("security", "dump-trust-settings")
|
||||||
|
cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
|
||||||
|
cmd.Run()
|
||||||
|
cmd = exec.Command("security", "dump-trust-settings", "-d")
|
||||||
|
cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
|
||||||
|
cmd.Run()
|
||||||
|
}
|
||||||
|
}
|
10
tempfork/x509/root_js.go
Normal file
10
tempfork/x509/root_js.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build js,wasm
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
// Possible certificate files; stop after finding one.
|
||||||
|
var certFiles = []string{}
|
15
tempfork/x509/root_linux.go
Normal file
15
tempfork/x509/root_linux.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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 x509
|
||||||
|
|
||||||
|
// Possible certificate files; stop after finding one.
|
||||||
|
var certFiles = []string{
|
||||||
|
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||||
|
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
||||||
|
"/etc/ssl/ca-bundle.pem", // OpenSUSE
|
||||||
|
"/etc/pki/tls/cacert.pem", // OpenELEC
|
||||||
|
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
|
||||||
|
"/etc/ssl/cert.pem", // Alpine Linux
|
||||||
|
}
|
11
tempfork/x509/root_nocgo_darwin.go
Normal file
11
tempfork/x509/root_nocgo_darwin.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build !cgo
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
func loadSystemRoots() (*CertPool, error) {
|
||||||
|
return execSecurityRoots()
|
||||||
|
}
|
21
tempfork/x509/root_omit.go
Normal file
21
tempfork/x509/root_omit.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2020 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.
|
||||||
|
|
||||||
|
// +build darwin,arm64,x509omitbundledroots
|
||||||
|
|
||||||
|
// This file provides the loadSystemRoots func when the
|
||||||
|
// "x509omitbundledroots" build tag has disabled bundling a copy,
|
||||||
|
// which currently on happens on darwin/arm64 (root_darwin_arm64.go).
|
||||||
|
// This then saves 256 KiB of binary size and another 560 KiB of
|
||||||
|
// runtime memory size retaining the parsed roots forever. Constrained
|
||||||
|
// environments can construct minimal x509 root CertPools on the fly
|
||||||
|
// in the crypto/tls.Config.VerifyPeerCertificate hook.
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func loadSystemRoots() (*CertPool, error) {
|
||||||
|
return nil, errors.New("x509: system root bundling disabled")
|
||||||
|
}
|
22
tempfork/x509/root_omit_test.go
Normal file
22
tempfork/x509/root_omit_test.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2020 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.
|
||||||
|
|
||||||
|
// +build darwin,arm64,x509omitbundledroots
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOmitBundledRoots(t *testing.T) {
|
||||||
|
cp, err := loadSystemRoots()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("loadSystemRoots = (pool %p, error %v); want non-nil error", cp, err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "root bundling disabled") {
|
||||||
|
t.Errorf("unexpected error doesn't mention bundling: %v", err)
|
||||||
|
}
|
||||||
|
}
|
40
tempfork/x509/root_plan9.go
Normal file
40
tempfork/x509/root_plan9.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build plan9
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Possible certificate files; stop after finding one.
|
||||||
|
var certFiles = []string{
|
||||||
|
"/sys/lib/tls/ca.pem",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSystemRoots() (*CertPool, error) {
|
||||||
|
roots := NewCertPool()
|
||||||
|
var bestErr error
|
||||||
|
for _, file := range certFiles {
|
||||||
|
data, err := ioutil.ReadFile(file)
|
||||||
|
if err == nil {
|
||||||
|
roots.AppendCertsFromPEM(data)
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
if bestErr == nil || (os.IsNotExist(bestErr) && !os.IsNotExist(err)) {
|
||||||
|
bestErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bestErr == nil {
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
return nil, bestErr
|
||||||
|
}
|
12
tempfork/x509/root_solaris.go
Normal file
12
tempfork/x509/root_solaris.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// 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 x509
|
||||||
|
|
||||||
|
// Possible certificate files; stop after finding one.
|
||||||
|
var certFiles = []string{
|
||||||
|
"/etc/certs/ca-certificates.crt", // Solaris 11.2+
|
||||||
|
"/etc/ssl/certs/ca-certificates.crt", // Joyent SmartOS
|
||||||
|
"/etc/ssl/cacert.pem", // OmniOS
|
||||||
|
}
|
92
tempfork/x509/root_unix.go
Normal file
92
tempfork/x509/root_unix.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
// +build aix dragonfly freebsd js,wasm linux netbsd openbsd solaris
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Possible directories with certificate files; stop after successfully
|
||||||
|
// reading at least one file from a directory.
|
||||||
|
var certDirectories = []string{
|
||||||
|
"/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139
|
||||||
|
"/system/etc/security/cacerts", // Android
|
||||||
|
"/usr/local/share/certs", // FreeBSD
|
||||||
|
"/etc/pki/tls/certs", // Fedora/RHEL
|
||||||
|
"/etc/openssl/certs", // NetBSD
|
||||||
|
"/var/ssl/certs", // AIX
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// certFileEnv is the environment variable which identifies where to locate
|
||||||
|
// the SSL certificate file. If set this overrides the system default.
|
||||||
|
certFileEnv = "SSL_CERT_FILE"
|
||||||
|
|
||||||
|
// certDirEnv is the environment variable which identifies which directory
|
||||||
|
// to check for SSL certificate files. If set this overrides the system default.
|
||||||
|
// It is a colon separated list of directories.
|
||||||
|
// See https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html.
|
||||||
|
certDirEnv = "SSL_CERT_DIR"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSystemRoots() (*CertPool, error) {
|
||||||
|
roots := NewCertPool()
|
||||||
|
|
||||||
|
files := certFiles
|
||||||
|
if f := os.Getenv(certFileEnv); f != "" {
|
||||||
|
files = []string{f}
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstErr error
|
||||||
|
for _, file := range files {
|
||||||
|
data, err := ioutil.ReadFile(file)
|
||||||
|
if err == nil {
|
||||||
|
roots.AppendCertsFromPEM(data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if firstErr == nil && !os.IsNotExist(err) {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs := certDirectories
|
||||||
|
if d := os.Getenv(certDirEnv); d != "" {
|
||||||
|
// OpenSSL and BoringSSL both use ":" as the SSL_CERT_DIR separator.
|
||||||
|
// See:
|
||||||
|
// * https://golang.org/issue/35325
|
||||||
|
// * https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html
|
||||||
|
dirs = strings.Split(d, ":")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, directory := range dirs {
|
||||||
|
fis, err := ioutil.ReadDir(directory)
|
||||||
|
if err != nil {
|
||||||
|
if firstErr == nil && !os.IsNotExist(err) {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, fi := range fis {
|
||||||
|
data, err := ioutil.ReadFile(directory + "/" + fi.Name())
|
||||||
|
if err == nil {
|
||||||
|
roots.AppendCertsFromPEM(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(roots.certs) > 0 || firstErr == nil {
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, firstErr
|
||||||
|
}
|
204
tempfork/x509/root_unix_test.go
Normal file
204
tempfork/x509/root_unix_test.go
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// Copyright 2017 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.
|
||||||
|
|
||||||
|
// +build dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
|
package x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testDir = "testdata"
|
||||||
|
testDirCN = "test-dir"
|
||||||
|
testFile = "test-file.crt"
|
||||||
|
testFileCN = "test-file"
|
||||||
|
testMissing = "missing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnvVars(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
fileEnv string
|
||||||
|
dirEnv string
|
||||||
|
files []string
|
||||||
|
dirs []string
|
||||||
|
cns []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// Environment variables override the default locations preventing fall through.
|
||||||
|
name: "override-defaults",
|
||||||
|
fileEnv: testMissing,
|
||||||
|
dirEnv: testMissing,
|
||||||
|
files: []string{testFile},
|
||||||
|
dirs: []string{testDir},
|
||||||
|
cns: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// File environment overrides default file locations.
|
||||||
|
name: "file",
|
||||||
|
fileEnv: testFile,
|
||||||
|
dirEnv: "",
|
||||||
|
files: nil,
|
||||||
|
dirs: nil,
|
||||||
|
cns: []string{testFileCN},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Directory environment overrides default directory locations.
|
||||||
|
name: "dir",
|
||||||
|
fileEnv: "",
|
||||||
|
dirEnv: testDir,
|
||||||
|
files: nil,
|
||||||
|
dirs: nil,
|
||||||
|
cns: []string{testDirCN},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// File & directory environment overrides both default locations.
|
||||||
|
name: "file+dir",
|
||||||
|
fileEnv: testFile,
|
||||||
|
dirEnv: testDir,
|
||||||
|
files: nil,
|
||||||
|
dirs: nil,
|
||||||
|
cns: []string{testFileCN, testDirCN},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Environment variable empty / unset uses default locations.
|
||||||
|
name: "empty-fall-through",
|
||||||
|
fileEnv: "",
|
||||||
|
dirEnv: "",
|
||||||
|
files: []string{testFile},
|
||||||
|
dirs: []string{testDir},
|
||||||
|
cns: []string{testFileCN, testDirCN},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save old settings so we can restore before the test ends.
|
||||||
|
origCertFiles, origCertDirectories := certFiles, certDirectories
|
||||||
|
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
|
||||||
|
defer func() {
|
||||||
|
certFiles = origCertFiles
|
||||||
|
certDirectories = origCertDirectories
|
||||||
|
os.Setenv(certFileEnv, origFile)
|
||||||
|
os.Setenv(certDirEnv, origDir)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
|
||||||
|
t.Fatalf("setenv %q failed: %v", certFileEnv, err)
|
||||||
|
}
|
||||||
|
if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
|
||||||
|
t.Fatalf("setenv %q failed: %v", certDirEnv, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certFiles, certDirectories = tc.files, tc.dirs
|
||||||
|
|
||||||
|
r, err := loadSystemRoots()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("unexpected failure:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
t.Fatal("nil roots")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the returned certs match, otherwise report where the mismatch is.
|
||||||
|
for i, cn := range tc.cns {
|
||||||
|
if i >= len(r.certs) {
|
||||||
|
t.Errorf("missing cert %v @ %v", cn, i)
|
||||||
|
} else if r.certs[i].Subject.CommonName != cn {
|
||||||
|
fmt.Printf("%#v\n", r.certs[0].Subject)
|
||||||
|
t.Errorf("unexpected cert common name %q, want %q", r.certs[i].Subject.CommonName, cn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(r.certs) > len(tc.cns) {
|
||||||
|
t.Errorf("got %v certs, which is more than %v wanted", len(r.certs), len(tc.cns))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that "SSL_CERT_DIR" when used as the environment
|
||||||
|
// variable delimited by colons, allows loadSystemRoots to
|
||||||
|
// load all the roots from the respective directories.
|
||||||
|
// See https://golang.org/issue/35325.
|
||||||
|
func TestLoadSystemCertsLoadColonSeparatedDirs(t *testing.T) {
|
||||||
|
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
|
||||||
|
origCertFiles := certFiles[:]
|
||||||
|
|
||||||
|
// To prevent any other certs from being loaded in
|
||||||
|
// through "SSL_CERT_FILE" or from known "certFiles",
|
||||||
|
// clear them all, and they'll be reverting on defer.
|
||||||
|
certFiles = certFiles[:0]
|
||||||
|
os.Setenv(certFileEnv, "")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
certFiles = origCertFiles[:]
|
||||||
|
os.Setenv(certDirEnv, origDir)
|
||||||
|
os.Setenv(certFileEnv, origFile)
|
||||||
|
}()
|
||||||
|
|
||||||
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "x509-issue35325")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create temporary directory: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
rootPEMs := []string{
|
||||||
|
geoTrustRoot,
|
||||||
|
googleLeaf,
|
||||||
|
startComRoot,
|
||||||
|
}
|
||||||
|
|
||||||
|
var certDirs []string
|
||||||
|
for i, certPEM := range rootPEMs {
|
||||||
|
certDir := filepath.Join(tmpDir, fmt.Sprintf("cert-%d", i))
|
||||||
|
if err := os.MkdirAll(certDir, 0755); err != nil {
|
||||||
|
t.Fatalf("Failed to create certificate dir: %v", err)
|
||||||
|
}
|
||||||
|
certOutFile := filepath.Join(certDir, "cert.crt")
|
||||||
|
if err := ioutil.WriteFile(certOutFile, []byte(certPEM), 0655); err != nil {
|
||||||
|
t.Fatalf("Failed to write certificate to file: %v", err)
|
||||||
|
}
|
||||||
|
certDirs = append(certDirs, certDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check: the number of certDirs should be equal to the number of roots.
|
||||||
|
if g, w := len(certDirs), len(rootPEMs); g != w {
|
||||||
|
t.Fatalf("Failed sanity check: len(certsDir)=%d is not equal to len(rootsPEMS)=%d", g, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now finally concatenate them with a colon.
|
||||||
|
colonConcatCertDirs := strings.Join(certDirs, ":")
|
||||||
|
os.Setenv(certDirEnv, colonConcatCertDirs)
|
||||||
|
gotPool, err := loadSystemRoots()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load system roots: %v", err)
|
||||||
|
}
|
||||||
|
subjects := gotPool.Subjects()
|
||||||
|
// We expect exactly len(rootPEMs) subjects back.
|
||||||
|
if g, w := len(subjects), len(rootPEMs); g != w {
|
||||||
|
t.Fatalf("Invalid number of subjects: got %d want %d", g, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantPool := NewCertPool()
|
||||||
|
for _, certPEM := range rootPEMs {
|
||||||
|
wantPool.AppendCertsFromPEM([]byte(certPEM))
|
||||||
|
}
|
||||||
|
strCertPool := func(p *CertPool) string {
|
||||||
|
return string(bytes.Join(p.Subjects(), []byte("\n")))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotPool, wantPool) {
|
||||||
|
g, w := strCertPool(gotPool), strCertPool(wantPool)
|
||||||
|
t.Fatalf("Mismatched certPools\nGot:\n%s\n\nWant:\n%s", g, w)
|
||||||
|
}
|
||||||
|
}
|
286
tempfork/x509/root_windows.go
Normal file
286
tempfork/x509/root_windows.go
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
// Copyright 2012 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
|
||||||
|
// certificate store containing itself and all of the intermediate certificates specified
|
||||||
|
// in the opts.Intermediates CertPool.
|
||||||
|
//
|
||||||
|
// A pointer to the in-memory store is available in the returned CertContext's Store field.
|
||||||
|
// The store is automatically freed when the CertContext is freed using
|
||||||
|
// syscall.CertFreeCertificateContext.
|
||||||
|
func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
|
||||||
|
var storeCtx *syscall.CertContext
|
||||||
|
|
||||||
|
leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer syscall.CertFreeCertificateContext(leafCtx)
|
||||||
|
|
||||||
|
handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer syscall.CertCloseStore(handle, 0)
|
||||||
|
|
||||||
|
err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Intermediates != nil {
|
||||||
|
for _, intermediate := range opts.Intermediates.certs {
|
||||||
|
ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
|
||||||
|
syscall.CertFreeCertificateContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return storeCtx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
|
||||||
|
func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
|
||||||
|
if simpleChain == nil || count == 0 {
|
||||||
|
return nil, errors.New("x509: invalid simple chain")
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:count:count]
|
||||||
|
lastChain := simpleChains[count-1]
|
||||||
|
elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:lastChain.NumElements:lastChain.NumElements]
|
||||||
|
for i := 0; i < int(lastChain.NumElements); i++ {
|
||||||
|
// Copy the buf, since ParseCertificate does not create its own copy.
|
||||||
|
cert := elements[i].CertContext
|
||||||
|
encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length:cert.Length]
|
||||||
|
buf := make([]byte, cert.Length)
|
||||||
|
copy(buf, encodedCert)
|
||||||
|
parsedCert, err := ParseCertificate(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
chain = append(chain, parsedCert)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkChainTrustStatus checks the trust status of the certificate chain, translating
|
||||||
|
// any errors it finds into Go errors in the process.
|
||||||
|
func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
|
||||||
|
if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
|
||||||
|
status := chainCtx.TrustStatus.ErrorStatus
|
||||||
|
switch status {
|
||||||
|
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
|
||||||
|
return CertificateInvalidError{c, Expired, ""}
|
||||||
|
default:
|
||||||
|
return UnknownAuthorityError{c, nil, nil}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
|
||||||
|
// use as a certificate chain for a SSL/TLS server.
|
||||||
|
func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
|
||||||
|
servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sslPara := &syscall.SSLExtraCertChainPolicyPara{
|
||||||
|
AuthType: syscall.AUTHTYPE_SERVER,
|
||||||
|
ServerName: servernamep,
|
||||||
|
}
|
||||||
|
sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
|
||||||
|
|
||||||
|
para := &syscall.CertChainPolicyPara{
|
||||||
|
ExtraPolicyPara: (syscall.Pointer)(unsafe.Pointer(sslPara)),
|
||||||
|
}
|
||||||
|
para.Size = uint32(unsafe.Sizeof(*para))
|
||||||
|
|
||||||
|
status := syscall.CertChainPolicyStatus{}
|
||||||
|
err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mkrautz): use the lChainIndex and lElementIndex fields
|
||||||
|
// of the CertChainPolicyStatus to provide proper context, instead
|
||||||
|
// using c.
|
||||||
|
if status.Error != 0 {
|
||||||
|
switch status.Error {
|
||||||
|
case syscall.CERT_E_EXPIRED:
|
||||||
|
return CertificateInvalidError{c, Expired, ""}
|
||||||
|
case syscall.CERT_E_CN_NO_MATCH:
|
||||||
|
return HostnameError{c, opts.DNSName}
|
||||||
|
case syscall.CERT_E_UNTRUSTEDROOT:
|
||||||
|
return UnknownAuthorityError{c, nil, nil}
|
||||||
|
default:
|
||||||
|
return UnknownAuthorityError{c, nil, nil}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// systemVerify is like Verify, except that it uses CryptoAPI calls
|
||||||
|
// to build certificate chains and verify them.
|
||||||
|
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||||
|
hasDNSName := opts != nil && len(opts.DNSName) > 0
|
||||||
|
|
||||||
|
storeCtx, err := createStoreContext(c, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer syscall.CertFreeCertificateContext(storeCtx)
|
||||||
|
|
||||||
|
para := new(syscall.CertChainPara)
|
||||||
|
para.Size = uint32(unsafe.Sizeof(*para))
|
||||||
|
|
||||||
|
// If there's a DNSName set in opts, assume we're verifying
|
||||||
|
// a certificate from a TLS server.
|
||||||
|
if hasDNSName {
|
||||||
|
oids := []*byte{
|
||||||
|
&syscall.OID_PKIX_KP_SERVER_AUTH[0],
|
||||||
|
// Both IE and Chrome allow certificates with
|
||||||
|
// Server Gated Crypto as well. Some certificates
|
||||||
|
// in the wild require them.
|
||||||
|
&syscall.OID_SERVER_GATED_CRYPTO[0],
|
||||||
|
&syscall.OID_SGC_NETSCAPE[0],
|
||||||
|
}
|
||||||
|
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
|
||||||
|
para.RequestedUsage.Usage.Length = uint32(len(oids))
|
||||||
|
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
|
||||||
|
} else {
|
||||||
|
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
|
||||||
|
para.RequestedUsage.Usage.Length = 0
|
||||||
|
para.RequestedUsage.Usage.UsageIdentifiers = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var verifyTime *syscall.Filetime
|
||||||
|
if opts != nil && !opts.CurrentTime.IsZero() {
|
||||||
|
ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
|
||||||
|
verifyTime = &ft
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertGetCertificateChain will traverse Windows's root stores
|
||||||
|
// in an attempt to build a verified certificate chain. Once
|
||||||
|
// it has found a verified chain, it stops. MSDN docs on
|
||||||
|
// CERT_CHAIN_CONTEXT:
|
||||||
|
//
|
||||||
|
// When a CERT_CHAIN_CONTEXT is built, the first simple chain
|
||||||
|
// begins with an end certificate and ends with a self-signed
|
||||||
|
// certificate. If that self-signed certificate is not a root
|
||||||
|
// or otherwise trusted certificate, an attempt is made to
|
||||||
|
// build a new chain. CTLs are used to create the new chain
|
||||||
|
// beginning with the self-signed certificate from the original
|
||||||
|
// chain as the end certificate of the new chain. This process
|
||||||
|
// continues building additional simple chains until the first
|
||||||
|
// self-signed certificate is a trusted certificate or until
|
||||||
|
// an additional simple chain cannot be built.
|
||||||
|
//
|
||||||
|
// The result is that we'll only get a single trusted chain to
|
||||||
|
// return to our caller.
|
||||||
|
var chainCtx *syscall.CertChainContext
|
||||||
|
err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer syscall.CertFreeCertificateChain(chainCtx)
|
||||||
|
|
||||||
|
err = checkChainTrustStatus(c, chainCtx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasDNSName {
|
||||||
|
err = checkChainSSLServerPolicy(c, chainCtx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(chain) < 1 {
|
||||||
|
return nil, errors.New("x509: internal error: system verifier returned an empty chain")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mitigate CVE-2020-0601, where the Windows system verifier might be
|
||||||
|
// tricked into using custom curve parameters for a trusted root, by
|
||||||
|
// double-checking all ECDSA signatures. If the system was tricked into
|
||||||
|
// using spoofed parameters, the signature will be invalid for the correct
|
||||||
|
// ones we parsed. (We don't support custom curves ourselves.)
|
||||||
|
for i, parent := range chain[1:] {
|
||||||
|
if parent.PublicKeyAlgorithm != ECDSA {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := parent.CheckSignature(chain[i].SignatureAlgorithm,
|
||||||
|
chain[i].RawTBSCertificate, chain[i].Signature); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [][]*Certificate{chain}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSystemRoots() (*CertPool, error) {
|
||||||
|
// TODO: restore this functionality on Windows. We tried to do
|
||||||
|
// it in Go 1.8 but had to revert it. See Issue 18609.
|
||||||
|
// Returning (nil, nil) was the old behavior, prior to CL 30578.
|
||||||
|
// The if statement here avoids vet complaining about
|
||||||
|
// unreachable code below.
|
||||||
|
if true {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const CRYPT_E_NOT_FOUND = 0x80092004
|
||||||
|
|
||||||
|
store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer syscall.CertCloseStore(store, 0)
|
||||||
|
|
||||||
|
roots := NewCertPool()
|
||||||
|
var cert *syscall.CertContext
|
||||||
|
for {
|
||||||
|
cert, err = syscall.CertEnumCertificatesInStore(store, cert)
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok {
|
||||||
|
if errno == CRYPT_E_NOT_FOUND {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Copy the buf, since ParseCertificate does not create its own copy.
|
||||||
|
buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length:cert.Length]
|
||||||
|
buf2 := make([]byte, cert.Length)
|
||||||
|
copy(buf2, buf)
|
||||||
|
if c, err := ParseCertificate(buf2); err == nil {
|
||||||
|
roots.AddCert(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return roots, nil
|
||||||
|
}
|
123
tempfork/x509/sec1.go
Normal file
123
tempfork/x509/sec1.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright 2012 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ecPrivKeyVersion = 1
|
||||||
|
|
||||||
|
// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
|
||||||
|
// References:
|
||||||
|
// RFC 5915
|
||||||
|
// SEC1 - http://www.secg.org/sec1-v2.pdf
|
||||||
|
// Per RFC 5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
|
||||||
|
// most cases it is not.
|
||||||
|
type ecPrivateKey struct {
|
||||||
|
Version int
|
||||||
|
PrivateKey []byte
|
||||||
|
NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
|
||||||
|
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseECPrivateKey parses an EC private key in SEC 1, ASN.1 DER form.
|
||||||
|
//
|
||||||
|
// This kind of key is commonly encoded in PEM blocks of type "EC PRIVATE KEY".
|
||||||
|
func ParseECPrivateKey(der []byte) (*ecdsa.PrivateKey, error) {
|
||||||
|
return parseECPrivateKey(nil, der)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalECPrivateKey converts an EC private key to SEC 1, ASN.1 DER form.
|
||||||
|
//
|
||||||
|
// This kind of key is commonly encoded in PEM blocks of type "EC PRIVATE KEY".
|
||||||
|
// For a more flexible key format which is not EC specific, use
|
||||||
|
// MarshalPKCS8PrivateKey.
|
||||||
|
func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||||
|
oid, ok := oidFromNamedCurve(key.Curve)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("x509: unknown elliptic curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
return marshalECPrivateKeyWithOID(key, oid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshalECPrivateKey marshals an EC private key into ASN.1, DER format and
|
||||||
|
// sets the curve ID to the given OID, or omits it if OID is nil.
|
||||||
|
func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier) ([]byte, error) {
|
||||||
|
privateKeyBytes := key.D.Bytes()
|
||||||
|
paddedPrivateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8)
|
||||||
|
copy(paddedPrivateKey[len(paddedPrivateKey)-len(privateKeyBytes):], privateKeyBytes)
|
||||||
|
|
||||||
|
return asn1.Marshal(ecPrivateKey{
|
||||||
|
Version: 1,
|
||||||
|
PrivateKey: paddedPrivateKey,
|
||||||
|
NamedCurveOID: oid,
|
||||||
|
PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
|
||||||
|
// The OID for the named curve may be provided from another source (such as
|
||||||
|
// the PKCS8 container) - if it is provided then use this instead of the OID
|
||||||
|
// that may exist in the EC private key structure.
|
||||||
|
func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
|
||||||
|
var privKey ecPrivateKey
|
||||||
|
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
|
||||||
|
if _, err := asn1.Unmarshal(der, &pkcs8{}); err == nil {
|
||||||
|
return nil, errors.New("x509: failed to parse private key (use ParsePKCS8PrivateKey instead for this key format)")
|
||||||
|
}
|
||||||
|
if _, err := asn1.Unmarshal(der, &pkcs1PrivateKey{}); err == nil {
|
||||||
|
return nil, errors.New("x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format)")
|
||||||
|
}
|
||||||
|
return nil, errors.New("x509: failed to parse EC private key: " + err.Error())
|
||||||
|
}
|
||||||
|
if privKey.Version != ecPrivKeyVersion {
|
||||||
|
return nil, fmt.Errorf("x509: unknown EC private key version %d", privKey.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
var curve elliptic.Curve
|
||||||
|
if namedCurveOID != nil {
|
||||||
|
curve = namedCurveFromOID(*namedCurveOID)
|
||||||
|
} else {
|
||||||
|
curve = namedCurveFromOID(privKey.NamedCurveOID)
|
||||||
|
}
|
||||||
|
if curve == nil {
|
||||||
|
return nil, errors.New("x509: unknown elliptic curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
k := new(big.Int).SetBytes(privKey.PrivateKey)
|
||||||
|
curveOrder := curve.Params().N
|
||||||
|
if k.Cmp(curveOrder) >= 0 {
|
||||||
|
return nil, errors.New("x509: invalid elliptic curve private key value")
|
||||||
|
}
|
||||||
|
priv := new(ecdsa.PrivateKey)
|
||||||
|
priv.Curve = curve
|
||||||
|
priv.D = k
|
||||||
|
|
||||||
|
privateKey := make([]byte, (curveOrder.BitLen()+7)/8)
|
||||||
|
|
||||||
|
// Some private keys have leading zero padding. This is invalid
|
||||||
|
// according to [SEC1], but this code will ignore it.
|
||||||
|
for len(privKey.PrivateKey) > len(privateKey) {
|
||||||
|
if privKey.PrivateKey[0] != 0 {
|
||||||
|
return nil, errors.New("x509: invalid private key length")
|
||||||
|
}
|
||||||
|
privKey.PrivateKey = privKey.PrivateKey[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some private keys remove all leading zeros, this is also invalid
|
||||||
|
// according to [SEC1] but since OpenSSL used to do this, we ignore
|
||||||
|
// this too.
|
||||||
|
copy(privateKey[len(privateKey)-len(privKey.PrivateKey):], privKey.PrivateKey)
|
||||||
|
priv.X, priv.Y = curve.ScalarBaseMult(privateKey)
|
||||||
|
|
||||||
|
return priv, nil
|
||||||
|
}
|
66
tempfork/x509/sec1_test.go
Normal file
66
tempfork/x509/sec1_test.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2012 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 x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ecKeyTests = []struct {
|
||||||
|
derHex string
|
||||||
|
shouldReserialize bool
|
||||||
|
}{
|
||||||
|
// Generated using:
|
||||||
|
// openssl ecparam -genkey -name secp384r1 -outform PEM
|
||||||
|
{"3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50", true},
|
||||||
|
// This key was generated by GnuTLS and has illegal zero-padding of the
|
||||||
|
// private key. See https://golang.org/issues/13699.
|
||||||
|
{"3078020101042100f9f43a04b9bdc3ab01f53be6df80e7a7bc3eaf7b87fc24e630a4a0aa97633645a00a06082a8648ce3d030107a1440342000441a51bc318461b4c39a45048a16d4fc2a935b1ea7fe86e8c1fa219d6f2438f7c7fd62957d3442efb94b6a23eb0ea66dda663dc42f379cda6630b21b7888a5d3d", false},
|
||||||
|
// This was generated using an old version of OpenSSL and is missing a
|
||||||
|
// leading zero byte in the private key that should be present.
|
||||||
|
{"3081db0201010441607b4f985774ac21e633999794542e09312073480baa69550914d6d43d8414441e61b36650567901da714f94dffb3ce0e2575c31928a0997d51df5c440e983ca17a00706052b81040023a181890381860004001661557afedd7ac8d6b70e038e576558c626eb62edda36d29c3a1310277c11f67a8c6f949e5430a37dcfb95d902c1b5b5379c389873b9dd17be3bdb088a4774a7401072f830fb9a08d93bfa50a03dd3292ea07928724ddb915d831917a338f6b0aecfbc3cf5352c4a1295d356890c41c34116d29eeb93779aab9d9d78e2613437740f6", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseECPrivateKey(t *testing.T) {
|
||||||
|
for i, test := range ecKeyTests {
|
||||||
|
derBytes, _ := hex.DecodeString(test.derHex)
|
||||||
|
key, err := ParseECPrivateKey(derBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("#%d: failed to decode EC private key: %s", i, err)
|
||||||
|
}
|
||||||
|
serialized, err := MarshalECPrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("#%d: failed to encode EC private key: %s", i, err)
|
||||||
|
}
|
||||||
|
matches := bytes.Equal(serialized, derBytes)
|
||||||
|
if matches != test.shouldReserialize {
|
||||||
|
t.Fatalf("#%d: when serializing key: matches=%t, should match=%t: original %x, reserialized %x", i, matches, test.shouldReserialize, serialized, derBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hexECTestPKCS1Key = "3082025c02010002818100b1a1e0945b9289c4d3f1329f8a982c4a2dcd59bfd372fb8085a9c517554607ebd2f7990eef216ac9f4605f71a03b04f42a5255b158cf8e0844191f5119348baa44c35056e20609bcf9510f30ead4b481c81d7865fb27b8e0090e112b717f3ee08cdfc4012da1f1f7cf2a1bc34c73a54a12b06372d09714742dd7895eadde4aa5020301000102818062b7fa1db93e993e40237de4d89b7591cc1ea1d04fed4904c643f17ae4334557b4295270d0491c161cb02a9af557978b32b20b59c267a721c4e6c956c2d147046e9ae5f2da36db0106d70021fa9343455f8f973a4b355a26fd19e6b39dee0405ea2b32deddf0f4817759ef705d02b34faab9ca93c6766e9f722290f119f34449024100d9c29a4a013a90e35fd1be14a3f747c589fac613a695282d61812a711906b8a0876c6181f0333ca1066596f57bff47e7cfcabf19c0fc69d9cd76df743038b3cb024100d0d3546fecf879b5551f2bd2c05e6385f2718a08a6face3d2aecc9d7e03645a480a46c81662c12ad6bd6901e3bd4f38029462de7290859567cdf371c79088d4f024100c254150657e460ea58573fcf01a82a4791e3d6223135c8bdfed69afe84fbe7857274f8eb5165180507455f9b4105c6b08b51fe8a481bb986a202245576b713530240045700003b7a867d0041df9547ae2e7f50248febd21c9040b12dae9c2feab0d3d4609668b208e4727a3541557f84d372ac68eaf74ce1018a4c9a0ef92682c8fd02405769731480bb3a4570abf422527c5f34bf732fa6c1e08cc322753c511ce055fac20fc770025663ad3165324314df907f1f1942f0448a7e9cdbf87ecd98b92156"
|
||||||
|
const hexECTestPKCS8Key = "30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031"
|
||||||
|
|
||||||
|
var ecMismatchKeyTests = []struct {
|
||||||
|
hexKey string
|
||||||
|
errorContains string
|
||||||
|
}{
|
||||||
|
{hexKey: hexECTestPKCS8Key, errorContains: "use ParsePKCS8PrivateKey instead"},
|
||||||
|
{hexKey: hexECTestPKCS1Key, errorContains: "use ParsePKCS1PrivateKey instead"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestECMismatchKeyFormat(t *testing.T) {
|
||||||
|
for i, test := range ecMismatchKeyTests {
|
||||||
|
derBytes, _ := hex.DecodeString(test.hexKey)
|
||||||
|
_, err := ParseECPrivateKey(derBytes)
|
||||||
|
if !strings.Contains(err.Error(), test.errorContains) {
|
||||||
|
t.Errorf("#%d: expected error containing %q, got %s", i, test.errorContains, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
tempfork/x509/test-file.crt
Normal file
32
tempfork/x509/test-file.crt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFbTCCA1WgAwIBAgIJAN338vEmMtLsMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNV
|
||||||
|
BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
|
||||||
|
dHMxEjAQBgNVBAMMCXRlc3QtZmlsZTAeFw0xNzAyMDEyMzUyMDhaFw0yNzAxMzAy
|
||||||
|
MzUyMDhaME0xCzAJBgNVBAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYD
|
||||||
|
VQQKDAxHb2xhbmcgVGVzdHMxEjAQBgNVBAMMCXRlc3QtZmlsZTCCAiIwDQYJKoZI
|
||||||
|
hvcNAQEBBQADggIPADCCAgoCggIBAPMGiLjdiffQo3Xc8oUe7wsDhSaAJFOhO6Qs
|
||||||
|
i0xYrYl7jmCuz9rGD2fdgk5cLqGazKuQ6fIFzHXFU2BKs4CWXt9KO0KFEhfvZeuW
|
||||||
|
jG5d7C1ZUiuKOrPqjKVu8SZtFPc7y7Ke7msXzY+Z2LLyiJJ93LCMq4+cTSGNXVlI
|
||||||
|
KqUxhxeoD5/QkUPyQy/ilu3GMYfx/YORhDP6Edcuskfj8wRh1UxBejP8YPMvI6St
|
||||||
|
cE2GkxoEGqDWnQ/61F18te6WI3MD29tnKXOkXVhnSC+yvRLljotW2/tAhHKBG4tj
|
||||||
|
iQWT5Ri4Wrw2tXxPKRLsVWc7e1/hdxhnuvYpXkWNhKsm002jzkFXlzfEwPd8nZdw
|
||||||
|
5aT6gPUBN2AAzdoqZI7E200i0orEF7WaSoMfjU1tbHvExp3vyAPOfJ5PS2MQ6W03
|
||||||
|
Zsy5dTVH+OBH++rkRzQCFcnIv/OIhya5XZ9KX9nFPgBEP7Xq2A+IjH7B6VN/S/bv
|
||||||
|
8lhp2V+SQvlew9GttKC4hKuPsl5o7+CMbcqcNUdxm9gGkN8epGEKCuix97bpNlxN
|
||||||
|
fHZxHE5+8GMzPXMkCD56y5TNKR6ut7JGHMPtGl5lPCLqzG/HzYyFgxsDfDUu2B0A
|
||||||
|
GKj0lGpnLfGqwhs2/s3jpY7+pcvVQxEpvVTId5byDxu1ujP4HjO/VTQ2P72rE8Ft
|
||||||
|
C6J2Av0tAgMBAAGjUDBOMB0GA1UdDgQWBBTLT/RbyfBB/Pa07oBnaM+QSJPO9TAf
|
||||||
|
BgNVHSMEGDAWgBTLT/RbyfBB/Pa07oBnaM+QSJPO9TAMBgNVHRMEBTADAQH/MA0G
|
||||||
|
CSqGSIb3DQEBCwUAA4ICAQB3sCntCcQwhMgRPPyvOCMyTcQ/Iv+cpfxz2Ck14nlx
|
||||||
|
AkEAH2CH0ov5GWTt07/ur3aa5x+SAKi0J3wTD1cdiw4U/6Uin6jWGKKxvoo4IaeK
|
||||||
|
SbM8w/6eKx6UbmHx7PA/eRABY9tTlpdPCVgw7/o3WDr03QM+IAtatzvaCPPczake
|
||||||
|
pbdLwmBZB/v8V+6jUajy6jOgdSH0PyffGnt7MWgDETmNC6p/Xigp5eh+C8Fb4NGT
|
||||||
|
xgHES5PBC+sruWp4u22bJGDKTvYNdZHsnw/CaKQWNsQqwisxa3/8N5v+PCff/pxl
|
||||||
|
r05pE3PdHn9JrCl4iWdVlgtiI9BoPtQyDfa/OEFaScE8KYR8LxaAgdgp3zYncWls
|
||||||
|
BpwQ6Y/A2wIkhlD9eEp5Ib2hz7isXOs9UwjdriKqrBXqcIAE5M+YIk3+KAQKxAtd
|
||||||
|
4YsK3CSJ010uphr12YKqlScj4vuKFjuOtd5RyyMIxUG3lrrhAu2AzCeKCLdVgA8+
|
||||||
|
75FrYMApUdvcjp4uzbBoED4XRQlx9kdFHVbYgmE/+yddBYJM8u4YlgAL0hW2/D8p
|
||||||
|
z9JWIfxVmjJnBnXaKGBuiUyZ864A3PJndP6EMMo7TzS2CDnfCYuJjvI0KvDjFNmc
|
||||||
|
rQA04+qfMSEz3nmKhbbZu4eYLzlADhfH8tT4GMtXf71WLA5AUHGf2Y4+HIHTsmHG
|
||||||
|
vQ==
|
||||||
|
-----END CERTIFICATE-----
|
31
tempfork/x509/testdata/test-dir.crt
vendored
Normal file
31
tempfork/x509/testdata/test-dir.crt
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFazCCA1OgAwIBAgIJAL8a/lsnspOqMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
|
||||||
|
BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
|
||||||
|
dHMxETAPBgNVBAMMCHRlc3QtZGlyMB4XDTE3MDIwMTIzNTAyN1oXDTI3MDEzMDIz
|
||||||
|
NTAyN1owTDELMAkGA1UEBhMCVUsxEzARBgNVBAgMClRlc3QtU3RhdGUxFTATBgNV
|
||||||
|
BAoMDEdvbGFuZyBUZXN0czERMA8GA1UEAwwIdGVzdC1kaXIwggIiMA0GCSqGSIb3
|
||||||
|
DQEBAQUAA4ICDwAwggIKAoICAQDzBoi43Yn30KN13PKFHu8LA4UmgCRToTukLItM
|
||||||
|
WK2Je45grs/axg9n3YJOXC6hmsyrkOnyBcx1xVNgSrOAll7fSjtChRIX72Xrloxu
|
||||||
|
XewtWVIrijqz6oylbvEmbRT3O8uynu5rF82Pmdiy8oiSfdywjKuPnE0hjV1ZSCql
|
||||||
|
MYcXqA+f0JFD8kMv4pbtxjGH8f2DkYQz+hHXLrJH4/MEYdVMQXoz/GDzLyOkrXBN
|
||||||
|
hpMaBBqg1p0P+tRdfLXuliNzA9vbZylzpF1YZ0gvsr0S5Y6LVtv7QIRygRuLY4kF
|
||||||
|
k+UYuFq8NrV8TykS7FVnO3tf4XcYZ7r2KV5FjYSrJtNNo85BV5c3xMD3fJ2XcOWk
|
||||||
|
+oD1ATdgAM3aKmSOxNtNItKKxBe1mkqDH41NbWx7xMad78gDznyeT0tjEOltN2bM
|
||||||
|
uXU1R/jgR/vq5Ec0AhXJyL/ziIcmuV2fSl/ZxT4ARD+16tgPiIx+welTf0v27/JY
|
||||||
|
adlfkkL5XsPRrbSguISrj7JeaO/gjG3KnDVHcZvYBpDfHqRhCgrosfe26TZcTXx2
|
||||||
|
cRxOfvBjMz1zJAg+esuUzSkerreyRhzD7RpeZTwi6sxvx82MhYMbA3w1LtgdABio
|
||||||
|
9JRqZy3xqsIbNv7N46WO/qXL1UMRKb1UyHeW8g8btboz+B4zv1U0Nj+9qxPBbQui
|
||||||
|
dgL9LQIDAQABo1AwTjAdBgNVHQ4EFgQUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwHwYD
|
||||||
|
VR0jBBgwFoAUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwDAYDVR0TBAUwAwEB/zANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAgEAvEVnUYsIOt87rggmLPqEueynkuQ+562M8EDHSQl82zbe
|
||||||
|
xDCxeg3DvPgKb+RvaUdt1362z/szK10SoeMgx6+EQLoV9LiVqXwNqeYfixrhrdw3
|
||||||
|
ppAhYYhymdkbUQCEMHypmXP1vPhAz4o8Bs+eES1M+zO6ErBiD7SqkmBElT+GixJC
|
||||||
|
6epC9ZQFs+dw3lPlbiZSsGE85sqc3VAs0/JgpL/pb1/Eg4s0FUhZD2C2uWdSyZGc
|
||||||
|
g0/v3aXJCp4j/9VoNhI1WXz3M45nysZIL5OQgXymLqJElQa1pZ3Wa4i/nidvT4AT
|
||||||
|
Xlxc/qijM8set/nOqp7hVd5J0uG6qdwLRILUddZ6OpXd7ZNi1EXg+Bpc7ehzGsDt
|
||||||
|
3UFGzYXDjxYnK2frQfjLS8stOQIqSrGthW6x0fdkVx0y8BByvd5J6+JmZl4UZfzA
|
||||||
|
m99VxXSt4B9x6BvnY7ktzcFDOjtuLc4B/7yg9fv1eQuStA4cHGGAttsCg1X/Kx8W
|
||||||
|
PvkkeH0UWDZ9vhH9K36703z89da6MWF+bz92B0+4HoOmlVaXRkvblsNaynJnL0LC
|
||||||
|
Ayry7QBxuh5cMnDdRwJB3AVJIiJ1GVpb7aGvBOnx+s2lwRv9HWtghb+cbwwktx1M
|
||||||
|
JHyBf3GZNSWTpKY7cD8V+NnBv3UuioOVVo+XAU4LF/bYUjdRpxWADJizNtZrtFo=
|
||||||
|
-----END CERTIFICATE-----
|
1100
tempfork/x509/verify.go
Normal file
1100
tempfork/x509/verify.go
Normal file
File diff suppressed because it is too large
Load Diff
2126
tempfork/x509/verify_test.go
Normal file
2126
tempfork/x509/verify_test.go
Normal file
File diff suppressed because it is too large
Load Diff
2828
tempfork/x509/x509.go
Normal file
2828
tempfork/x509/x509.go
Normal file
File diff suppressed because it is too large
Load Diff
2646
tempfork/x509/x509_test.go
Normal file
2646
tempfork/x509/x509_test.go
Normal file
File diff suppressed because it is too large
Load Diff
56
tempfork/x509/x509_test_import.go
Normal file
56
tempfork/x509/x509_test_import.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
// This file is run by the x509 tests to ensure that a program with minimal
|
||||||
|
// imports can sign certificates without errors resulting from missing hash
|
||||||
|
// functions.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
block, _ := pem.Decode([]byte(pemPrivateKey))
|
||||||
|
rsaPriv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
panic("Failed to parse private key: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
template := x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "test",
|
||||||
|
Organization: []string{"Σ Acme Co"},
|
||||||
|
},
|
||||||
|
NotBefore: time.Unix(1000, 0),
|
||||||
|
NotAfter: time.Unix(100000, 0),
|
||||||
|
KeyUsage: x509.KeyUsageCertSign,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = x509.CreateCertificate(rand.Reader, &template, &template, &rsaPriv.PublicKey, rsaPriv); err != nil {
|
||||||
|
panic("failed to create certificate with basic imports: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pemPrivateKey = testingKey(`-----BEGIN RSA TESTING KEY-----
|
||||||
|
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
|
||||||
|
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
|
||||||
|
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
|
||||||
|
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
|
||||||
|
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
|
||||||
|
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
|
||||||
|
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
|
||||||
|
-----END RSA TESTING KEY-----
|
||||||
|
`)
|
||||||
|
|
||||||
|
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
|
Loading…
x
Reference in New Issue
Block a user