Skip to content

Commit 0707dc7

Browse files
authored
Merge pull request #1181 from myokoyama28/fix-certadd
2 parents b1512a1 + ea20c62 commit 0707dc7

File tree

4 files changed

+147
-34
lines changed

4 files changed

+147
-34
lines changed

api/certadd/insert.go

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package certadd
22

33
import (
44
"bytes"
5+
"database/sql"
56
"encoding/hex"
67
"encoding/json"
78
"io/ioutil"
@@ -14,6 +15,7 @@ import (
1415
"github.com/cloudflare/cfssl/errors"
1516
"github.com/cloudflare/cfssl/helpers"
1617
"github.com/cloudflare/cfssl/ocsp"
18+
"github.com/jmoiron/sqlx/types"
1719

1820
"encoding/base64"
1921

@@ -48,14 +50,19 @@ func NewHandler(dbAccessor certdb.Accessor, signer ocsp.Signer) http.Handler {
4850
// AddRequest describes a request from a client to insert a
4951
// certificate into the database.
5052
type AddRequest struct {
51-
Serial string `json:"serial_number"`
52-
AKI string `json:"authority_key_identifier"`
53-
CALabel string `json:"ca_label"`
54-
Status string `json:"status"`
55-
Reason int `json:"reason"`
56-
Expiry time.Time `json:"expiry"`
57-
RevokedAt time.Time `json:"revoked_at"`
58-
PEM string `json:"pem"`
53+
Serial string `json:"serial_number"`
54+
AKI string `json:"authority_key_identifier"`
55+
CALabel string `json:"ca_label"`
56+
Status string `json:"status"`
57+
Reason int `json:"reason"`
58+
Expiry time.Time `json:"expiry"`
59+
RevokedAt time.Time `json:"revoked_at"`
60+
PEM string `json:"pem"`
61+
IssuedAt *time.Time `json:"issued_at"`
62+
NotBefore *time.Time `json:"not_before"`
63+
MetadataJSON types.JSONText `json:"metadata"`
64+
SansJSON types.JSONText `json:"sans"`
65+
CommonName string `json:"common_name"`
5966
}
6067

6168
// Map of valid reason codes
@@ -113,14 +120,18 @@ func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
113120
return errors.NewBadRequestString("The provided certificate is empty")
114121
}
115122

123+
if req.Expiry.IsZero() {
124+
return errors.NewBadRequestString("Expiry is required but not provided")
125+
}
126+
116127
// Parse the certificate and validate that it matches
117128
cert, err := helpers.ParseCertificatePEM([]byte(req.PEM))
118129
if err != nil {
119130
return errors.NewBadRequestString("Unable to parse PEM encoded certificates")
120131
}
121132

122133
serialBigInt := new(big.Int)
123-
if _, success := serialBigInt.SetString(req.Serial, 16); !success {
134+
if _, success := serialBigInt.SetString(req.Serial, 10); !success {
124135
return errors.NewBadRequestString("Unable to parse serial key of request")
125136
}
126137

@@ -137,15 +148,24 @@ func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
137148
return errors.NewBadRequestString("Authority key identifier of request and certificate do not match")
138149
}
139150

151+
if req.Expiry != cert.NotAfter {
152+
return errors.NewBadRequestString("Expiry of request and certificate do not match")
153+
}
154+
140155
cr := certdb.CertificateRecord{
141-
Serial: req.Serial,
142-
AKI: req.AKI,
143-
CALabel: req.CALabel,
144-
Status: req.Status,
145-
Reason: req.Reason,
146-
Expiry: req.Expiry,
147-
RevokedAt: req.RevokedAt,
148-
PEM: req.PEM,
156+
Serial: req.Serial,
157+
AKI: req.AKI,
158+
CALabel: req.CALabel,
159+
Status: req.Status,
160+
Reason: req.Reason,
161+
Expiry: req.Expiry,
162+
RevokedAt: req.RevokedAt,
163+
PEM: req.PEM,
164+
IssuedAt: req.IssuedAt,
165+
NotBefore: req.NotBefore,
166+
MetadataJSON: req.MetadataJSON,
167+
SANsJSON: req.SansJSON,
168+
CommonName: sql.NullString{String: req.CommonName, Valid: req.CommonName != ""},
149169
}
150170

151171
err = h.dbAccessor.InsertCertificate(cr)

api/certadd/insert_test.go

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func makeCertificate() (serialNumber *big.Int, cert *x509.Certificate, pemBytes
7373
Organization: []string{"Cornell CS 5152"},
7474
},
7575
AuthorityKeyId: []byte{42, 42, 42, 42},
76+
NotAfter: time.Now(),
7677
}
7778
cert = &template
7879

@@ -91,9 +92,9 @@ func makeCertificate() (serialNumber *big.Int, cert *x509.Certificate, pemBytes
9192
Subject: pkix.Name{
9293
Organization: []string{"Cornell CS 5152"},
9394
},
94-
AuthorityKeyId: []byte{42, 42, 42, 42},
95-
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
96-
IsCA: true,
95+
AuthorityKeyId: []byte{42, 42, 42, 42},
96+
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
97+
IsCA: true,
9798
BasicConstraintsValid: true,
9899
}
99100
issuerBytes, err := x509.CreateCertificate(rand.Reader, &issuerTemplate, &issuerTemplate, &privKey.PublicKey, privKey)
@@ -153,10 +154,11 @@ func TestInsertValidCertificate(t *testing.T) {
153154
}
154155

155156
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
156-
"serial_number": serialNumber.Text(16),
157+
"serial_number": serialNumber.Text(10),
157158
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
158159
"status": "good",
159160
"pem": string(pemBytes),
161+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
160162
})
161163

162164
if resp.StatusCode != http.StatusOK {
@@ -179,7 +181,7 @@ func TestInsertValidCertificate(t *testing.T) {
179181
t.Fatal("Could not parse returned OCSP response", err)
180182
}
181183

182-
ocsps, err := dbAccessor.GetOCSP(serialNumber.Text(16), hex.EncodeToString(cert.AuthorityKeyId))
184+
ocsps, err := dbAccessor.GetOCSP(serialNumber.Text(10), hex.EncodeToString(cert.AuthorityKeyId))
183185
if err != nil {
184186
t.Fatal(err)
185187
}
@@ -223,6 +225,7 @@ func TestInsertMissingSerial(t *testing.T) {
223225
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
224226
"status": "good",
225227
"pem": string(pemBytes),
228+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
226229
})
227230

228231
if resp.StatusCode != http.StatusBadRequest {
@@ -236,16 +239,41 @@ func TestInsertMissingAKI(t *testing.T) {
236239
t.Fatal(err)
237240
}
238241

239-
serialNumber, _, pemBytes, signer, err := makeCertificate()
242+
serialNumber, cert, pemBytes, signer, err := makeCertificate()
240243

241244
if err != nil {
242245
t.Fatal(err)
243246
}
244247

245248
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
246-
"serial_number": serialNumber.Text(16),
249+
"serial_number": serialNumber.Text(10),
247250
"status": "good",
248251
"pem": string(pemBytes),
252+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
253+
})
254+
255+
if resp.StatusCode != http.StatusBadRequest {
256+
t.Fatal("Expected HTTP Bad Request", resp.StatusCode, string(body))
257+
}
258+
}
259+
260+
func TestInsertMissingExpiry(t *testing.T) {
261+
dbAccessor, err := prepDB()
262+
if err != nil {
263+
t.Fatal(err)
264+
}
265+
266+
serialNumber, cert, pemBytes, signer, err := makeCertificate()
267+
268+
if err != nil {
269+
t.Fatal(err)
270+
}
271+
272+
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
273+
"serial_number": serialNumber.Text(10),
274+
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
275+
"status": "good",
276+
"pem": string(pemBytes),
249277
})
250278

251279
if resp.StatusCode != http.StatusBadRequest {
@@ -266,9 +294,10 @@ func TestInsertMissingPEM(t *testing.T) {
266294
}
267295

268296
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
269-
"serial_number": serialNumber.Text(16),
297+
"serial_number": serialNumber.Text(10),
270298
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
271299
"status": "good",
300+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
272301
})
273302

274303
if resp.StatusCode != http.StatusBadRequest {
@@ -293,6 +322,7 @@ func TestInsertInvalidSerial(t *testing.T) {
293322
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
294323
"status": "good",
295324
"pem": string(pemBytes),
325+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
296326
})
297327

298328
if resp.StatusCode != http.StatusBadRequest {
@@ -306,17 +336,18 @@ func TestInsertInvalidAKI(t *testing.T) {
306336
t.Fatal(err)
307337
}
308338

309-
serialNumber, _, pemBytes, signer, err := makeCertificate()
339+
serialNumber, cert, pemBytes, signer, err := makeCertificate()
310340

311341
if err != nil {
312342
t.Fatal(err)
313343
}
314344

315345
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
316-
"serial_number": serialNumber.Text(16),
346+
"serial_number": serialNumber.Text(10),
317347
"authority_key_identifier": "this is not an AKI",
318348
"status": "good",
319349
"pem": string(pemBytes),
350+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
320351
})
321352

322353
if resp.StatusCode != http.StatusBadRequest {
@@ -337,10 +368,11 @@ func TestInsertInvalidStatus(t *testing.T) {
337368
}
338369

339370
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
340-
"serial_number": serialNumber.Text(16),
371+
"serial_number": serialNumber.Text(10),
341372
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
342373
"status": "invalid",
343374
"pem": string(pemBytes),
375+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
344376
})
345377

346378
if resp.StatusCode != http.StatusBadRequest {
@@ -361,10 +393,36 @@ func TestInsertInvalidPEM(t *testing.T) {
361393
}
362394

363395
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
364-
"serial_number": serialNumber.Text(16),
396+
"serial_number": serialNumber.Text(10),
365397
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
366398
"status": "good",
367399
"pem": "this is not a PEM certificate",
400+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
401+
})
402+
403+
if resp.StatusCode != http.StatusBadRequest {
404+
t.Fatal("Expected HTTP Bad Request, got", resp.StatusCode, string(body))
405+
}
406+
}
407+
408+
func TestInsertInvalidExpiry(t *testing.T) {
409+
dbAccessor, err := prepDB()
410+
if err != nil {
411+
t.Fatal(err)
412+
}
413+
414+
serialNumber, cert, pemBytes, signer, err := makeCertificate()
415+
416+
if err != nil {
417+
t.Fatal(err)
418+
}
419+
420+
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
421+
"serial_number": serialNumber.Text(10),
422+
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
423+
"status": "good",
424+
"pem": string(pemBytes),
425+
"expiry": "this is not an expiry",
368426
})
369427

370428
if resp.StatusCode != http.StatusBadRequest {
@@ -385,10 +443,11 @@ func TestInsertWrongSerial(t *testing.T) {
385443
}
386444

387445
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
388-
"serial_number": big.NewInt(1).Text(16),
446+
"serial_number": big.NewInt(1).Text(10),
389447
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
390448
"status": "good",
391449
"pem": string(pemBytes),
450+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
392451
})
393452

394453
if resp.StatusCode != http.StatusBadRequest {
@@ -402,17 +461,43 @@ func TestInsertWrongAKI(t *testing.T) {
402461
t.Fatal(err)
403462
}
404463

464+
serialNumber, cert, pemBytes, signer, err := makeCertificate()
465+
466+
if err != nil {
467+
t.Fatal(err)
468+
}
469+
470+
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
471+
"serial_number": serialNumber.Text(10),
472+
"authority_key_identifier": hex.EncodeToString([]byte{7, 7}),
473+
"status": "good",
474+
"pem": string(pemBytes),
475+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
476+
})
477+
478+
if resp.StatusCode != http.StatusBadRequest {
479+
t.Fatal("Expected HTTP Bad Request", resp.StatusCode, string(body))
480+
}
481+
}
482+
483+
func TestInsertWrongExpiry(t *testing.T) {
484+
dbAccessor, err := prepDB()
485+
if err != nil {
486+
t.Fatal(err)
487+
}
488+
405489
serialNumber, _, pemBytes, signer, err := makeCertificate()
406490

407491
if err != nil {
408492
t.Fatal(err)
409493
}
410494

411495
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
412-
"serial_number": serialNumber.Text(16),
496+
"serial_number": serialNumber.Text(10),
413497
"authority_key_identifier": hex.EncodeToString([]byte{7, 7}),
414498
"status": "good",
415499
"pem": string(pemBytes),
500+
"expiry": time.Now().UTC().Format(time.RFC3339),
416501
})
417502

418503
if resp.StatusCode != http.StatusBadRequest {
@@ -433,18 +518,19 @@ func TestInsertRevokedCertificate(t *testing.T) {
433518
}
434519

435520
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
436-
"serial_number": serialNumber.Text(16),
521+
"serial_number": serialNumber.Text(10),
437522
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
438523
"status": "revoked",
439524
"pem": string(pemBytes),
440525
"revoked_at": time.Now(),
526+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
441527
})
442528

443529
if resp.StatusCode != http.StatusOK {
444530
t.Fatal("Expected HTTP OK", resp.StatusCode, string(body))
445531
}
446532

447-
ocsps, err := dbAccessor.GetOCSP(serialNumber.Text(16), hex.EncodeToString(cert.AuthorityKeyId))
533+
ocsps, err := dbAccessor.GetOCSP(serialNumber.Text(10), hex.EncodeToString(cert.AuthorityKeyId))
448534
if err != nil {
449535
t.Fatal(err)
450536
}
@@ -477,10 +563,11 @@ func TestInsertRevokedCertificateWithoutTime(t *testing.T) {
477563
}
478564

479565
resp, body := makeRequest(t, dbAccessor, signer, map[string]interface{}{
480-
"serial_number": serialNumber.Text(16),
566+
"serial_number": serialNumber.Text(10),
481567
"authority_key_identifier": hex.EncodeToString(cert.AuthorityKeyId),
482568
"status": "revoked",
483569
"pem": string(pemBytes),
570+
"expiry": cert.NotAfter.UTC().Format(time.RFC3339),
484571
// Omit RevokedAt
485572
})
486573

cli/serve/serve.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
rice "github.com/GeertJohan/go.rice"
1717
"github.com/cloudflare/cfssl/api"
1818
"github.com/cloudflare/cfssl/api/bundle"
19+
"github.com/cloudflare/cfssl/api/certadd"
1920
"github.com/cloudflare/cfssl/api/certinfo"
2021
"github.com/cloudflare/cfssl/api/crl"
2122
"github.com/cloudflare/cfssl/api/gencrl"
@@ -251,6 +252,10 @@ var endpoints = map[string]func() (http.Handler, error){
251252
"health": func() (http.Handler, error) {
252253
return health.NewHealthCheck(), nil
253254
},
255+
256+
"certadd": func() (http.Handler, error) {
257+
return certadd.NewHandler(certsql.NewAccessor(db), nil), nil
258+
},
254259
}
255260

256261
// registerHandlers instantiates various handlers and associate them to corresponding endpoints.

0 commit comments

Comments
 (0)