-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
signer.go
170 lines (141 loc) · 4.38 KB
/
signer.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Package localca implements a localca that is useful for testing the
// transport package. To use the localca, see the New and Load
// functions.
package localca
import (
"crypto/x509"
"encoding/pem"
"errors"
"time"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/initca"
"github.com/cloudflare/cfssl/signer"
"github.com/cloudflare/cfssl/signer/local"
"github.com/kisom/goutils/assert"
)
// CA is a local transport CertificateAuthority that is useful for
// tests.
type CA struct {
s *local.Signer
disabled bool
// Label and Profile are used to select the CFSSL signer
// components if they should be anything but the default.
Label string `json:"label"`
Profile string `json:"profile"`
// The KeyFile and CertFile are required when using Load to
// construct a CA.
KeyFile string `json:"private_key,omitempty"`
CertFile string `json:"certificate,omitempty"`
}
// Toggle switches the CA between operable mode and inoperable
// mode. This is useful in testing to verify behaviours when a CA is
// unavailable.
func (lca *CA) Toggle() {
lca.disabled = !lca.disabled
}
var errNotSetup = errors.New("transport: local CA has not been setup")
// CACertificate returns the certificate authority's certificate.
func (lca *CA) CACertificate() ([]byte, error) {
if lca.s == nil {
return nil, errNotSetup
}
cert, err := lca.s.Certificate(lca.Label, lca.Profile)
if err != nil {
return nil, err
}
p := &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
}
return pem.EncodeToMemory(p), nil
}
var errDisabled = errors.New("transport: local CA is deactivated")
// SignCSR submits a PKCS #10 certificate signing request to a CA for
// signing.
func (lca *CA) SignCSR(csrPEM []byte) ([]byte, error) {
if lca == nil || lca.s == nil {
return nil, errNotSetup
}
if lca.disabled {
return nil, errDisabled
}
p, _ := pem.Decode(csrPEM)
if p == nil || p.Type != "CERTIFICATE REQUEST" {
return nil, errors.New("transport: invalid PEM-encoded certificate signing request")
}
csr, err := x509.ParseCertificateRequest(p.Bytes)
if err != nil {
return nil, err
}
hosts := make([]string, 0, len(csr.DNSNames)+len(csr.IPAddresses))
copy(hosts, csr.DNSNames)
for i := range csr.IPAddresses {
hosts = append(hosts, csr.IPAddresses[i].String())
}
sreq := signer.SignRequest{
Hosts: hosts,
Request: string(csrPEM),
Profile: lca.Profile,
Label: lca.Label,
}
return lca.s.Sign(sreq)
}
// ExampleRequest can be used as a sample request, or the returned
// request can be modified.
func ExampleRequest() *csr.CertificateRequest {
return &csr.CertificateRequest{
Hosts: []string{"localhost"},
KeyRequest: &csr.KeyRequest{
A: "ecdsa",
S: 256,
},
CN: "Transport Failover Test Local CA",
CA: &csr.CAConfig{
PathLength: 1,
Expiry: "30m",
},
}
}
// ExampleSigningConfig returns a sample config.Signing with only a
// default profile.
func ExampleSigningConfig() *config.Signing {
return &config.Signing{
Default: &config.SigningProfile{
Expiry: 15 * time.Minute,
Usage: []string{
"server auth", "client auth",
"signing", "key encipherment",
},
},
}
}
// New generates a new CA from a certificate request and signing profile.
func New(req *csr.CertificateRequest, profiles *config.Signing) (*CA, error) {
certPEM, _, keyPEM, err := initca.New(req)
if err != nil {
return nil, err
}
// If initca returns successfully, the following (which are
// all CFSSL internal functions) should not return an
// error. If they do, we should abort --- something about
// CFSSL has become inconsistent, and it can't be trusted.
priv, err := helpers.ParsePrivateKeyPEM(keyPEM)
assert.NoError(err, "CFSSL-generated private key can't be parsed")
cert, err := helpers.ParseCertificatePEM(certPEM)
assert.NoError(err, "CFSSL-generated certificate can't be parsed")
s, err := local.NewSigner(priv, cert, helpers.SignerAlgo(priv), profiles)
assert.NoError(err, "a signer could not be constructed")
return NewFromSigner(s), nil
}
// NewFromSigner constructs a local CA from a CFSSL signer.
func NewFromSigner(s *local.Signer) *CA {
return &CA{s: s}
}
// Load reads the key and certificate from the files specified in the
// CA.
func Load(lca *CA, profiles *config.Signing) (err error) {
lca.s, err = local.NewSignerFromFile(lca.CertFile, lca.KeyFile, profiles)
return err
}