-
Notifications
You must be signed in to change notification settings - Fork 1
/
entity.go
133 lines (109 loc) · 3.07 KB
/
entity.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
package testca
import (
"bytes"
"crypto"
"crypto/x509"
"github.com/effective-security/xpki/certutil"
"github.com/effective-security/xpki/x/fileutil"
"github.com/spf13/afero"
)
// Entity is a certificate and private key.
type Entity struct {
Issuer *Entity
PrivateKey crypto.Signer
Certificate *x509.Certificate
NextSN int64
}
// NewEntity creates a new CA.
func NewEntity(opts ...Option) *Entity {
c := &configuration{}
for _, opt := range opts {
option(opt)(c)
}
return c.generate()
}
// Issue issues a new Entity with this one as its parent.
func (id *Entity) Issue(opts ...Option) *Entity {
opts = append(opts, Issuer(id))
return NewEntity(opts...)
}
// PFX wraps the certificate and private key in an encrypted PKCS#12 packet. The
// provided password must be alphanumeric.
func (id *Entity) 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 *Entity) 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 *Entity) 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 *Entity) IncrementSN() int64 {
defer func() {
id.NextSN++
}()
return id.NextSN
}
// Root returns root CA for this entity.
func (id *Entity) Root() *x509.Certificate {
var root *Entity
for root = id; root.Issuer != nil; root = root.Issuer {
}
return root.Certificate
}
// KeyAndCertChain provides PrivateKey and its certificates chain
type KeyAndCertChain struct {
PrivateKey crypto.Signer
Certificate *x509.Certificate
Chain []*x509.Certificate
Root *x509.Certificate
}
// KeyAndCertChain returns chain for the PrivateKey
func (id *Entity) KeyAndCertChain() *KeyAndCertChain {
s := &KeyAndCertChain{
PrivateKey: id.PrivateKey,
Certificate: id.Certificate,
Chain: []*x509.Certificate{},
Root: id.Root(),
}
for issuer := id.Issuer; issuer != nil && !bytes.Equal(issuer.Certificate.Raw, s.Root.Raw); issuer = issuer.Issuer {
s.Chain = append(s.Chain, issuer.Certificate)
}
return s
}
// SaveCertAndKey stores the cert and key to provided locations
// withChain specifies to store entire chain up to the root in cert's pem file
func (id *Entity) SaveCertAndKey(certFile string, keyFile string, withChain bool) (err error) {
if keyFile != "" {
err = afero.WriteFile(fileutil.Vfs, keyFile, PrivKeyToPEM(id.PrivateKey), 0600)
if err != nil {
return err
}
}
if certFile != "" {
fcert, err := fileutil.Vfs.Create(certFile)
if err != nil {
return err
}
certs := []*x509.Certificate{
id.Certificate,
}
if withChain {
certs = append(certs, id.KeyAndCertChain().Chain...)
}
_ = certutil.EncodeToPEM(fcert, true, certs...)
fcert.Close()
}
return nil
}