-
Notifications
You must be signed in to change notification settings - Fork 0
/
certdbaccessor.go
273 lines (220 loc) · 7.55 KB
/
certdbaccessor.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
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package lib
import (
"errors"
"fmt"
"math/big"
"strings"
"time"
"github.com/cloudflare/cfssl/certdb"
certsql "github.com/cloudflare/cfssl/certdb/sql"
"github.com/cloudflare/cfssl/log"
"github.com/hyperledger/fabric-ca/util"
"github.com/kisielk/sqlstruct"
"github.com/jmoiron/sqlx"
)
const (
insertSQL = `
INSERT INTO certificates (id, serial_number, authority_key_identifier, ca_label, status, reason, expiry, revoked_at, pem)
VALUES (:id, :serial_number, :authority_key_identifier, :ca_label, :status, :reason, :expiry, :revoked_at, :pem);`
selectSQLbyID = `
SELECT %s FROM certificates
WHERE (id = ?);`
selectSQL = `
SELECT %s FROM certificates
WHERE (serial_number = ? AND authority_key_identifier = ?);`
updateRevokeSQL = `
UPDATE certificates
SET status='revoked', revoked_at=CURRENT_TIMESTAMP, reason=:reason
WHERE (id = :id AND status != 'revoked');`
)
// CertRecord extends CFSSL CertificateRecord by adding an enrollment ID to the record
type CertRecord struct {
ID string `db:"id"`
certdb.CertificateRecord
}
// CertDBAccessor implements certdb.Accessor interface.
type CertDBAccessor struct {
accessor certdb.Accessor
db *sqlx.DB
}
// NewCertDBAccessor returns a new Accessor.
func NewCertDBAccessor(db *sqlx.DB) *CertDBAccessor {
cffslAcc := new(CertDBAccessor)
cffslAcc.db = db
cffslAcc.accessor = certsql.NewAccessor(db)
return cffslAcc
}
func (d *CertDBAccessor) checkDB() error {
if d.db == nil {
return errors.New("Error")
}
return nil
}
// SetDB changes the underlying sql.DB object Accessor is manipulating.
func (d *CertDBAccessor) SetDB(db *sqlx.DB) {
d.db = db
return
}
// InsertCertificate puts a CertificateRecord into db.
func (d *CertDBAccessor) InsertCertificate(cr certdb.CertificateRecord) error {
log.Debug("DB: Insert Certificate")
err := d.checkDB()
if err != nil {
return err
}
id, err := util.GetEnrollmentIDFromPEM([]byte(cr.PEM))
if err != nil {
return err
}
ip := new(big.Int)
ip.SetString(cr.Serial, 10) //base 10
serial := util.GetSerialAsHex(ip)
aki := strings.TrimLeft(cr.AKI, "0")
log.Debug("Saved serial number as hex ", serial)
var record = new(CertRecord)
record.ID = id
record.Serial = serial
record.AKI = aki
record.CALabel = cr.CALabel
record.Status = cr.Status
record.Reason = cr.Reason
record.Expiry = cr.Expiry.UTC()
record.RevokedAt = cr.RevokedAt.UTC()
record.PEM = cr.PEM
res, err := d.db.NamedExec(insertSQL, record)
if err != nil {
return fmt.Errorf("Failed to insert record into database: %s", err)
}
numRowsAffected, err := res.RowsAffected()
if numRowsAffected == 0 {
return fmt.Errorf("Failed to insert the certificate record")
}
if numRowsAffected != 1 {
return fmt.Errorf("Expected to affect 1 entry in certificate database but affected %d",
numRowsAffected)
}
return err
}
// GetCertificatesByID gets a CertificateRecord indexed by id.
func (d *CertDBAccessor) GetCertificatesByID(id string) (crs []CertRecord, err error) {
log.Debugf("DB: Get certificate by ID (%s)", id)
err = d.checkDB()
if err != nil {
return nil, err
}
err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(selectSQLbyID), sqlstruct.Columns(CertRecord{})), id)
if err != nil {
return nil, err
}
return crs, nil
}
// GetCertificate gets a CertificateRecord indexed by serial.
func (d *CertDBAccessor) GetCertificate(serial, aki string) (crs []certdb.CertificateRecord, err error) {
log.Debugf("DB: Get certificate by serial (%s) and aki (%s)", serial, aki)
crs, err = d.accessor.GetCertificate(serial, aki)
if err != nil {
return nil, err
}
return crs, nil
}
// GetCertificateWithID gets a CertificateRecord indexed by serial and returns user too.
func (d *CertDBAccessor) GetCertificateWithID(serial, aki string) (crs CertRecord, err error) {
log.Debugf("DB: Get certificate by serial (%s) and aki (%s)", serial, aki)
err = d.checkDB()
if err != nil {
return crs, err
}
err = d.db.Get(&crs, fmt.Sprintf(d.db.Rebind(selectSQL), sqlstruct.Columns(CertRecord{})), serial, aki)
if err != nil {
return crs, err
}
return crs, nil
}
// GetUnexpiredCertificates gets all unexpired certificate from db.
func (d *CertDBAccessor) GetUnexpiredCertificates() (crs []certdb.CertificateRecord, err error) {
crs, err = d.accessor.GetUnexpiredCertificates()
if err != nil {
return nil, err
}
return crs, err
}
// GetRevokedAndUnexpiredCertificates returns all revoked and unexpired certificates
func (d *CertDBAccessor) GetRevokedAndUnexpiredCertificates() ([]certdb.CertificateRecord, error) {
crs, err := d.accessor.GetRevokedAndUnexpiredCertificates()
if err != nil {
return nil, err
}
return crs, err
}
// GetRevokedAndUnexpiredCertificatesByLabel returns revoked and unexpired certificates matching the label
func (d *CertDBAccessor) GetRevokedAndUnexpiredCertificatesByLabel(label string) ([]certdb.CertificateRecord, error) {
crs, err := d.accessor.GetRevokedAndUnexpiredCertificatesByLabel(label)
if err != nil {
return nil, err
}
return crs, err
}
// RevokeCertificatesByID updates all certificates for a given ID and marks them revoked.
func (d *CertDBAccessor) RevokeCertificatesByID(id string, reasonCode int) (crs []CertRecord, err error) {
log.Debugf("DB: Revoke certificate by ID (%s)", id)
err = d.checkDB()
if err != nil {
return nil, err
}
var record = new(CertRecord)
record.ID = id
record.Reason = reasonCode
err = d.db.Select(&crs, d.db.Rebind("SELECT * FROM certificates WHERE (id = ? AND status != 'revoked')"), id)
if err != nil {
return nil, err
}
_, err = d.db.NamedExec(updateRevokeSQL, record)
if err != nil {
return nil, err
}
return crs, err
}
// RevokeCertificate updates a certificate with a given serial number and marks it revoked.
func (d *CertDBAccessor) RevokeCertificate(serial, aki string, reasonCode int) error {
log.Debugf("DB: Revoke certificate by serial (%s) and aki (%s)", serial, aki)
err := d.accessor.RevokeCertificate(serial, aki, reasonCode)
return err
}
// InsertOCSP puts a new certdb.OCSPRecord into the db.
func (d *CertDBAccessor) InsertOCSP(rr certdb.OCSPRecord) error {
err := d.accessor.InsertOCSP(rr)
return err
}
// GetOCSP retrieves a certdb.OCSPRecord from db by serial.
func (d *CertDBAccessor) GetOCSP(serial, aki string) (ors []certdb.OCSPRecord, err error) {
ors, err = d.accessor.GetOCSP(serial, aki)
return ors, err
}
// GetUnexpiredOCSPs retrieves all unexpired certdb.OCSPRecord from db.
func (d *CertDBAccessor) GetUnexpiredOCSPs() (ors []certdb.OCSPRecord, err error) {
ors, err = d.accessor.GetUnexpiredOCSPs()
return ors, err
}
// UpdateOCSP updates a ocsp response record with a given serial number.
func (d *CertDBAccessor) UpdateOCSP(serial, aki, body string, expiry time.Time) error {
err := d.accessor.UpdateOCSP(serial, aki, body, expiry)
return err
}
// UpsertOCSP update a ocsp response record with a given serial number,
// or insert the record if it doesn't yet exist in the db
func (d *CertDBAccessor) UpsertOCSP(serial, aki, body string, expiry time.Time) error {
err := d.accessor.UpsertOCSP(serial, aki, body, expiry)
return err
}