-
Notifications
You must be signed in to change notification settings - Fork 136
/
identity.go
144 lines (116 loc) · 2.91 KB
/
identity.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
136
137
138
139
140
141
142
143
144
package fakeca
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"os/exec"
)
// Identity is a certificate and private key.
type Identity struct {
Issuer *Identity
PrivateKey crypto.Signer
Certificate *x509.Certificate
NextSN int64
}
// New creates a new CA.
func New(opts ...Option) *Identity {
c := &configuration{}
for _, opt := range opts {
option(opt)(c)
}
return c.generate()
}
// Issue issues a new Identity with this one as its parent.
func (id *Identity) Issue(opts ...Option) *Identity {
opts = append(opts, Issuer(id))
return New(opts...)
}
// PFX wraps the certificate and private key in an encrypted PKCS#12 packet. The
// provided password must be alphanumeric.
func (id *Identity) PFX(password string) []byte {
return toPFX(id.Certificate, id.PrivateKey, password)
}
// Chain builds a slice of *x509.Certificate from this CA and its issuers.
func (id *Identity) Chain() []*x509.Certificate {
chain := []*x509.Certificate{}
for this := id; this != nil; this = this.Issuer {
chain = append(chain, this.Certificate)
}
return chain
}
// ChainPool builds an *x509.CertPool from this CA and its issuers.
func (id *Identity) ChainPool() *x509.CertPool {
chain := x509.NewCertPool()
for this := id; this != nil; this = this.Issuer {
chain.AddCert(this.Certificate)
}
return chain
}
// IncrementSN returns the next serial number.
func (id *Identity) IncrementSN() int64 {
defer func() {
id.NextSN++
}()
return id.NextSN
}
func toPFX(cert *x509.Certificate, priv interface{}, password string) []byte {
// only allow alphanumeric passwords
for _, c := range password {
switch {
case c >= 'a' && c <= 'z':
case c >= 'A' && c <= 'Z':
case c >= '0' && c <= '9':
default:
panic("password must be alphanumeric")
}
}
passout := fmt.Sprintf("pass:%s", password)
cmd := exec.Command("openssl", "pkcs12", "-export", "-passout", passout)
cmd.Stdin = bytes.NewReader(append(append(toPKCS8(priv), '\n'), toPEM(cert)...))
out := new(bytes.Buffer)
cmd.Stdout = out
if err := cmd.Run(); err != nil {
panic(err)
}
return out.Bytes()
}
func toPEM(cert *x509.Certificate) []byte {
buf := new(bytes.Buffer)
if err := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil {
panic(err)
}
return buf.Bytes()
}
func toDER(priv interface{}) []byte {
var (
der []byte
err error
)
switch p := priv.(type) {
case *rsa.PrivateKey:
der = x509.MarshalPKCS1PrivateKey(p)
case *ecdsa.PrivateKey:
der, err = x509.MarshalECPrivateKey(p)
default:
err = errors.New("unknown key type")
}
if err != nil {
panic(err)
}
return der
}
func toPKCS8(priv interface{}) []byte {
cmd := exec.Command("openssl", "pkcs8", "-topk8", "-nocrypt", "-inform", "DER")
cmd.Stdin = bytes.NewReader(toDER(priv))
out := new(bytes.Buffer)
cmd.Stdout = out
if err := cmd.Run(); err != nil {
panic(err)
}
return out.Bytes()
}