forked from cloudflare/cfssl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
generator.go
319 lines (275 loc) · 8.98 KB
/
generator.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
// Package generator implements the HTTP handlers for certificate generation.
package generator
import (
"crypto/md5"
"crypto/sha1"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/bundler"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/signer"
"github.com/cloudflare/cfssl/signer/universal"
)
const (
// CSRNoHostMessage is used to alert the user to a certificate lacking a hosts field.
CSRNoHostMessage = `This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").`
// NoBundlerMessage is used to alert the user that the server does not have a bundler initialized.
NoBundlerMessage = `This request requires a bundler, but one is not initialized for the API server.`
)
// Sum contains digests for a certificate or certificate request.
type Sum struct {
MD5 string `json:"md5"`
SHA1 string `json:"sha-1"`
}
// Validator is a type of function that contains the logic for validating
// a certificate request.
type Validator func(*csr.CertificateRequest) error
// A CertRequest stores a PEM-encoded private key and corresponding
// CSR; this is returned from the CSR generation endpoint.
type CertRequest struct {
Key string `json:"private_key"`
CSR string `json:"certificate_request"`
Sums map[string]Sum `json:"sums"`
}
// A Handler accepts JSON-encoded certificate requests and
// returns a new private key and certificate request.
type Handler struct {
generator *csr.Generator
}
// NewHandler builds a new Handler from the
// validation function provided.
func NewHandler(validator Validator) (http.Handler, error) {
log.Info("setting up key / CSR generator")
return &api.HTTPHandler{
Handler: &Handler{
generator: &csr.Generator{Validator: validator},
},
Methods: []string{"POST"},
}, nil
}
func computeSum(in []byte) (sum Sum, err error) {
var data []byte
p, _ := pem.Decode(in)
if p == nil {
err = errors.NewBadRequestString("not a CSR or certificate")
return
}
switch p.Type {
case "CERTIFICATE REQUEST":
var req *x509.CertificateRequest
req, err = x509.ParseCertificateRequest(p.Bytes)
if err != nil {
return
}
data = req.Raw
case "CERTIFICATE":
var cert *x509.Certificate
cert, err = x509.ParseCertificate(p.Bytes)
if err != nil {
return
}
data = cert.Raw
default:
err = errors.NewBadRequestString("not a CSR or certificate")
return
}
md5Sum := md5.Sum(data)
sha1Sum := sha1.Sum(data)
sum.MD5 = fmt.Sprintf("%X", md5Sum[:])
sum.SHA1 = fmt.Sprintf("%X", sha1Sum[:])
return
}
// Handle responds to requests for the CA to generate a new private
// key and certificate request on behalf of the client. The format for
// these requests is documented in the API documentation.
func (g *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
log.Info("request for CSR")
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Warningf("failed to read request body: %v", err)
return errors.NewBadRequest(err)
}
r.Body.Close()
req := new(csr.CertificateRequest)
req.KeyRequest = csr.NewBasicKeyRequest()
err = json.Unmarshal(body, req)
if err != nil {
log.Warningf("failed to unmarshal request: %v", err)
return errors.NewBadRequest(err)
}
if req.CA != nil {
log.Warningf("request received with CA section")
return errors.NewBadRequestString("ca section only permitted in initca")
}
csr, key, err := g.generator.ProcessRequest(req)
if err != nil {
log.Warningf("failed to process CSR: %v", err)
// The validator returns a *cfssl/errors.HttpError
return err
}
sum, err := computeSum(csr)
if err != nil {
return errors.NewBadRequest(err)
}
// Both key and csr are returned PEM-encoded.
response := api.NewSuccessResponse(&CertRequest{
Key: string(key),
CSR: string(csr),
Sums: map[string]Sum{"certificate_request": sum},
})
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
err = enc.Encode(response)
return err
}
// A CertGeneratorHandler accepts JSON-encoded certificate requests
// and returns a new private key and signed certificate; it handles
// sending the CSR to the server.
type CertGeneratorHandler struct {
generator *csr.Generator
bundler *bundler.Bundler
signer signer.Signer
}
// NewCertGeneratorHandler builds a new handler for generating
// certificates directly from certificate requests; the validator covers
// the certificate request and the CA's key and certificate are used to
// sign the generated request. If remote is not an empty string, the
// handler will send signature requests to the CFSSL instance contained
// in remote.
func NewCertGeneratorHandler(validator Validator, caFile, caKeyFile string, policy *config.Signing) (http.Handler, error) {
var err error
log.Info("setting up new generator / signer")
cg := new(CertGeneratorHandler)
if policy == nil {
policy = &config.Signing{
Default: config.DefaultConfig(),
Profiles: nil,
}
}
root := universal.Root{
Config: map[string]string{
"ca-file": caFile,
"ca-key-file": caKeyFile,
},
}
if cg.signer, err = universal.NewSigner(root, policy); err != nil {
log.Errorf("setting up signer failed: %v", err)
return nil, err
}
cg.generator = &csr.Generator{Validator: validator}
return api.HTTPHandler{Handler: cg, Methods: []string{"POST"}}, nil
}
// NewCertGeneratorHandlerFromSigner returns a handler directly from
// the signer and validation function.
func NewCertGeneratorHandlerFromSigner(validator Validator, signer signer.Signer) http.Handler {
return api.HTTPHandler{
Handler: &CertGeneratorHandler{
generator: &csr.Generator{Validator: validator},
signer: signer,
},
Methods: []string{"POST"},
}
}
// SetBundler allows injecting an optional Bundler into the CertGeneratorHandler.
func (cg *CertGeneratorHandler) SetBundler(caBundleFile, intBundleFile string) (err error) {
cg.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile)
return err
}
type genSignRequest struct {
Request *csr.CertificateRequest `json:"request"`
Profile string `json:"profile"`
Label string `json:"label"`
Bundle bool `json:"bundle"`
}
// Handle responds to requests for the CA to generate a new private
// key and certificate on behalf of the client. The format for these
// requests is documented in the API documentation.
func (cg *CertGeneratorHandler) Handle(w http.ResponseWriter, r *http.Request) error {
log.Info("request for CSR")
req := new(genSignRequest)
req.Request = csr.New()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Warningf("failed to read request body: %v", err)
return errors.NewBadRequest(err)
}
r.Body.Close()
err = json.Unmarshal(body, req)
if err != nil {
log.Warningf("failed to unmarshal request: %v", err)
return errors.NewBadRequest(err)
}
if req.Request == nil {
log.Warning("empty request received")
return errors.NewBadRequestString("missing request section")
}
if req.Request.CA != nil {
log.Warningf("request received with CA section")
return errors.NewBadRequestString("ca section only permitted in initca")
}
csr, key, err := cg.generator.ProcessRequest(req.Request)
if err != nil {
log.Warningf("failed to process CSR: %v", err)
// The validator returns a *cfssl/errors.HttpError
return err
}
signReq := signer.SignRequest{
Request: string(csr),
Profile: req.Profile,
Label: req.Label,
}
certBytes, err := cg.signer.Sign(signReq)
if err != nil {
log.Warningf("failed to sign request: %v", err)
return err
}
reqSum, err := computeSum(csr)
if err != nil {
return errors.NewBadRequest(err)
}
certSum, err := computeSum(certBytes)
if err != nil {
return errors.NewBadRequest(err)
}
result := map[string]interface{}{
"private_key": string(key),
"certificate_request": string(csr),
"certificate": string(certBytes),
"sums": map[string]Sum{
"certificate_request": reqSum,
"certificate": certSum,
},
}
if req.Bundle {
if cg.bundler == nil {
return api.SendResponseWithMessage(w, result, NoBundlerMessage,
errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
}
bundle, err := cg.bundler.BundleFromPEMorDER(certBytes, nil, bundler.Optimal, "")
if err != nil {
return err
}
result["bundle"] = bundle
}
if len(req.Request.Hosts) == 0 {
return api.SendResponseWithMessage(w, result, CSRNoHostMessage,
errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
}
return api.SendResponse(w, result)
}
// CSRValidate does nothing and will never return an error. It exists because NewHandler
// requires a Validator as a parameter.
func CSRValidate(req *csr.CertificateRequest) error {
return nil
}