forked from smallstep/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
crt.go
135 lines (127 loc) 路 3.44 KB
/
crt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package x509util
import (
"bytes"
"crypto/ecdsa"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"io/ioutil"
"net"
"os"
"path/filepath"
"strings"
"github.com/RTradeLtd/ca-cli/errs"
"github.com/pkg/errors"
"golang.org/x/crypto/ed25519"
)
// Fingerprint returns the SHA-256 fingerprint of the certificate.
func Fingerprint(cert *x509.Certificate) string {
sum := sha256.Sum256(cert.Raw)
return strings.ToLower(hex.EncodeToString(sum[:]))
}
// VerifyCertKey that the public key of a certificate matches the given private key.
func VerifyCertKey(cert *x509.Certificate, key interface{}) error {
switch pub := cert.PublicKey.(type) {
case *rsa.PublicKey:
priv, ok := key.(*rsa.PrivateKey)
if !ok {
return errors.New("private key type does not match public key type")
}
if pub.N.Cmp(priv.N) != 0 {
return errors.New("private key does not match public key")
}
case *ecdsa.PublicKey:
priv, ok := key.(*ecdsa.PrivateKey)
if !ok {
return errors.New("private key type does not match public key type")
}
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
return errors.New("private key does not match public key")
}
case ed25519.PublicKey:
priv, ok := key.(ed25519.PrivateKey)
if !ok {
return errors.New("private key type does not match public key type")
}
if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
return errors.New("private key does not match public key")
}
default:
return errors.Errorf("unsupported public key type %T", pub)
}
return nil
}
// SplitSANs splits a slice of Subject Alternative Names into slices of
// IP Addresses and DNS Names. If an element is not an IP address, then it
// is bucketed as a DNS Name.
func SplitSANs(sans []string) (dnsNames []string, ips []net.IP, emails []string) {
dnsNames = []string{}
ips = []net.IP{}
emails = []string{}
if sans == nil {
return
}
for _, san := range sans {
if strings.Contains(san, "@") {
emails = append(emails, san)
} else if ip := net.ParseIP(san); ip != nil {
ips = append(ips, ip)
} else {
// If not IP then assume DNSName.
dnsNames = append(dnsNames, san)
}
}
return
}
// ReadCertPool loads a certificate pool from disk.
// *path*: a file, a directory, or a comma-separated list of files.
func ReadCertPool(path string) (*x509.CertPool, error) {
info, err := os.Stat(path)
if err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "os.Stat %s failed", path)
}
var (
files []string
pool = x509.NewCertPool()
)
if info != nil && info.IsDir() {
finfos, err := ioutil.ReadDir(path)
if err != nil {
return nil, errs.FileError(err, path)
}
for _, finfo := range finfos {
files = append(files, filepath.Join(path, finfo.Name()))
}
} else {
files = strings.Split(path, ",")
for i := range files {
files[i] = strings.TrimSpace(files[i])
}
}
var pems []byte
for _, f := range files {
bytes, err := ioutil.ReadFile(f)
if err != nil {
return nil, errs.FileError(err, f)
}
for len(bytes) > 0 {
var block *pem.Block
block, bytes = pem.Decode(bytes)
if block == nil {
// TODO: at a higher log level we should log the file we could not find.
break
}
// Ignore PEM blocks that are not CERTIFICATEs.
if block.Type != "CERTIFICATE" {
continue
}
pems = append(pems, pem.EncodeToMemory(block)...)
}
}
if ok := pool.AppendCertsFromPEM(pems); !ok {
return nil, errors.Errorf("error loading Root certificates")
}
return pool, nil
}