github.com/adecaro/fabric-ca@v2.0.0-alpha+incompatible/lib/certdbaccessor_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lib 8 9 import ( 10 "crypto/ecdsa" 11 "crypto/elliptic" 12 "crypto/rand" 13 "crypto/x509" 14 "crypto/x509/pkix" 15 "encoding/pem" 16 "math/big" 17 "os" 18 "testing" 19 "time" 20 21 "github.com/cloudflare/cfssl/certdb" 22 "github.com/cloudflare/cfssl/log" 23 "github.com/hyperledger/fabric-ca/lib/mocks" 24 "github.com/hyperledger/fabric-ca/lib/server/certificaterequest" 25 "github.com/hyperledger/fabric-ca/lib/server/db" 26 dbutil "github.com/hyperledger/fabric-ca/lib/server/db/util" 27 cadbuser "github.com/hyperledger/fabric-ca/lib/server/user" 28 "github.com/hyperledger/fabric-ca/util" 29 "github.com/hyperledger/fabric/common/metrics/metricsfakes" 30 "github.com/jmoiron/sqlx" 31 "github.com/pkg/errors" 32 "github.com/stretchr/testify/assert" 33 ) 34 35 func TestGetCertificatesDB(t *testing.T) { 36 os.RemoveAll("getCertDBTest") 37 defer os.RemoveAll("getCertDBTest") 38 log.Level = log.LevelDebug 39 40 level := &dbutil.Levels{ 41 Affiliation: 1, 42 Identity: 1, 43 Certificate: 1, 44 } 45 mockOperationsServer := &mocks.OperationsServer{} 46 fakeCounter := &metricsfakes.Counter{} 47 fakeCounter.WithReturns(fakeCounter) 48 mockOperationsServer.NewCounterReturns(fakeCounter) 49 fakeHistogram := &metricsfakes.Histogram{} 50 fakeHistogram.WithReturns(fakeHistogram) 51 mockOperationsServer.NewHistogramReturns(fakeHistogram) 52 srv := &Server{ 53 Operations: mockOperationsServer, 54 levels: level, 55 } 56 ca, err := newCA("getCertDBTest/config.yaml", &CAConfig{}, srv, false) 57 util.FatalError(t, err, "Failed to get CA") 58 59 populateCertificatesTable(t, ca) 60 61 certReq := getCertReq("testCertificate1", "", "", false, false, nil, nil, nil, nil) 62 rows, err := ca.certDBAccessor.GetCertificates(certReq, "") 63 assert.NoError(t, err, "Failed to get certificates from database") 64 certs, err := readRows(rows) 65 assert.Equal(t, "testCertificate1", certs[0].Subject.CommonName) 66 67 certReq = getCertReq("", "1111", "", false, false, nil, nil, nil, nil) 68 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 69 assert.NoError(t, err, "Failed to get certificates from database") 70 certs, err = readRows(rows) 71 assert.Equal(t, big.NewInt(1111), certs[0].SerialNumber) 72 73 certReq = getCertReq("", "", "9876", false, false, nil, nil, nil, nil) 74 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 75 assert.NoError(t, err, "Failed to get certificates from database") 76 certs, err = readRows(rows) 77 assert.Equal(t, 2, len(certs)) 78 assert.Equal(t, []byte("9876"), certs[0].AuthorityKeyId) 79 80 certReq = getCertReq("", "", "", true, false, nil, nil, nil, nil) 81 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 82 assert.NoError(t, err, "Failed to get certificates from database") 83 certs, err = readRows(rows) 84 assert.Equal(t, 5, len(certs)) 85 86 certReq = getCertReq("", "", "", false, true, nil, nil, nil, nil) 87 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 88 assert.NoError(t, err, "Failed to get certificates from database") 89 certs, err = readRows(rows) 90 assert.Equal(t, 6, len(certs)) 91 92 certReq = getCertReq("", "1111", "", false, false, nil, nil, nil, nil) 93 rows, err = ca.certDBAccessor.GetCertificates(certReq, "dept1") 94 assert.NoError(t, err, "Failed to get certificates from database") 95 certs, err = readRows(rows) 96 assert.Equal(t, 1, len(certs)) 97 assert.Equal(t, "testCertificate1", certs[0].Subject.CommonName) 98 99 certReq = getCertReq("", "", "9876AB", false, false, nil, nil, nil, nil) 100 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 101 assert.NoError(t, err, "Failed to get certificates from database") 102 certs, err = readRows(rows) 103 assert.Equal(t, 1, len(certs)) 104 assert.Equal(t, "testCertificate3", certs[0].Subject.CommonName) 105 106 revokedStart := time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC) 107 certReq = getCertReq("", "", "", false, false, &revokedStart, nil, nil, nil) 108 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 109 assert.NoError(t, err, "Failed to get certificates from database") 110 certs, err = readRows(rows) 111 assert.Equal(t, 1, len(certs)) 112 assert.Equal(t, "revoked1", certs[0].Subject.CommonName) 113 114 revokedEnd := time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC) 115 certReq = getCertReq("", "", "", false, false, nil, &revokedEnd, nil, nil) 116 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 117 assert.NoError(t, err, "Failed to get certificates from database") 118 certs, err = readRows(rows) 119 assert.Equal(t, 2, len(certs)) 120 121 revokedStart = time.Date(2017, time.January, 1, 0, 0, 0, 0, time.UTC) 122 revokedEnd = time.Date(2017, time.August, 1, 0, 0, 0, 0, time.UTC) 123 certReq = getCertReq("", "", "", false, false, &revokedStart, &revokedEnd, nil, nil) 124 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 125 assert.NoError(t, err, "Failed to get certificates from database") 126 certs, err = readRows(rows) 127 assert.Equal(t, 1, len(certs)) 128 assert.Equal(t, "revoked2", certs[0].Subject.CommonName) 129 130 expiredStart := time.Date(2018, time.March, 2, 0, 0, 0, 0, time.UTC) 131 certReq = getCertReq("", "", "", false, false, nil, nil, &expiredStart, nil) 132 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 133 assert.NoError(t, err, "Failed to get certificates from database") 134 certs, err = readRows(rows) 135 assert.Equal(t, 6, len(certs)) 136 137 dur, err := time.ParseDuration("+100h") 138 expiredEnd := time.Now().Add(dur).UTC() 139 certReq = getCertReq("", "", "", false, false, nil, nil, nil, &expiredEnd) 140 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 141 assert.NoError(t, err, "Failed to get certificates from database") 142 certs, err = readRows(rows) 143 assert.Equal(t, 7, len(certs)) 144 145 expiredStart = time.Date(2017, time.January, 1, 0, 0, 0, 0, time.UTC) 146 expiredEnd = time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC) 147 certReq = getCertReq("", "", "", false, false, nil, nil, &expiredStart, &expiredEnd) 148 rows, err = ca.certDBAccessor.GetCertificates(certReq, "") 149 assert.NoError(t, err, "Failed to get certificates from database") 150 certs, err = readRows(rows) 151 assert.Equal(t, 1, len(certs)) 152 assert.Equal(t, "expire1", certs[0].Subject.CommonName) 153 } 154 155 func readRows(rows *sqlx.Rows) ([]*x509.Certificate, error) { 156 var certs []*x509.Certificate 157 158 for rows.Next() { 159 var cert certPEM 160 err := rows.StructScan(&cert) 161 if err != nil { 162 return nil, errors.Errorf("Failed to get read row: %s", err) 163 } 164 165 block, rest := pem.Decode([]byte(cert.PEM)) 166 if block == nil || len(rest) > 0 { 167 return nil, errors.New("Certificate decoding error") 168 } 169 certificate, err := x509.ParseCertificate(block.Bytes) 170 if err != nil { 171 return nil, err 172 } 173 174 certs = append(certs, certificate) 175 } 176 177 return certs, nil 178 } 179 180 func populateCertificatesTable(t *testing.T, ca *CA) { 181 var err error 182 183 dur, err := time.ParseDuration("+100h") 184 util.FatalError(t, err, "Failed to parse duration '+100h'") 185 futureTime := time.Now().Add(dur).UTC() 186 187 ca.registry.InsertUser(&cadbuser.Info{ 188 Name: "testCertificate1", 189 Affiliation: "dept1", 190 }) 191 // Active Certs 192 err = testInsertCertificate(&certdb.CertificateRecord{ 193 Serial: "1111", 194 AKI: "9876", 195 Expiry: futureTime, 196 }, "testCertificate1", ca) 197 util.FatalError(t, err, "Failed to insert certificate with serial/AKI") 198 199 ca.registry.InsertUser(&cadbuser.Info{ 200 Name: "testCertificate2", 201 Affiliation: "dept1", 202 }) 203 err = testInsertCertificate(&certdb.CertificateRecord{ 204 Serial: "1112", 205 AKI: "9876", 206 Expiry: futureTime, 207 }, "testCertificate2", ca) 208 util.FatalError(t, err, "Failed to insert certificate with serial/AKI") 209 210 err = testInsertCertificate(&certdb.CertificateRecord{ 211 Serial: "1132", 212 AKI: "9876ab", 213 Expiry: futureTime, 214 }, "testCertificate3", ca) 215 util.FatalError(t, err, "Failed to insert certificate with revocation date") 216 217 // Expired 218 err = testInsertCertificate(&certdb.CertificateRecord{ 219 Serial: "1121", 220 AKI: "98765", 221 Expiry: time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC), 222 }, "expire1", ca) 223 util.FatalError(t, err, "Failed to insert certificate with expiration date") 224 225 // Not Expired 226 err = testInsertCertificate(&certdb.CertificateRecord{ 227 Serial: "1122", 228 AKI: "98765", 229 Expiry: futureTime, 230 }, "expire2", ca) 231 util.FatalError(t, err, "Failed to insert certificate with expiration date") 232 233 // Revoked 234 err = testInsertCertificate(&certdb.CertificateRecord{ 235 Serial: "1131", 236 AKI: "98765", 237 Expiry: futureTime, 238 RevokedAt: time.Date(2018, time.February, 15, 0, 0, 0, 0, time.UTC), 239 }, "revoked1", ca) 240 util.FatalError(t, err, "Failed to insert certificate with revocation date") 241 242 err = testInsertCertificate(&certdb.CertificateRecord{ 243 Serial: "1132", 244 AKI: "98765", 245 Expiry: futureTime, 246 RevokedAt: time.Date(2017, time.February, 15, 0, 0, 0, 0, time.UTC), 247 }, "revoked2", ca) 248 util.FatalError(t, err, "Failed to insert certificate with revocation date") 249 } 250 251 func testInsertCertificate(req *certdb.CertificateRecord, id string, ca *CA) error { 252 priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 253 if err != nil { 254 return errors.Errorf("Failed to generate private key: %s", err) 255 } 256 257 serial := new(big.Int) 258 serial.SetString(req.Serial, 10) //base 10 259 260 template := x509.Certificate{ 261 Subject: pkix.Name{ 262 CommonName: id, 263 }, 264 SerialNumber: serial, 265 AuthorityKeyId: []byte(req.AKI), 266 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 267 } 268 269 derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) 270 if err != nil { 271 log.Fatalf("Failed to create certificate: %s", err) 272 } 273 274 cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) 275 276 var record = &db.CertRecord{ 277 ID: id, 278 CertificateRecord: certdb.CertificateRecord{ 279 Serial: req.Serial, 280 AKI: req.AKI, 281 CALabel: req.CALabel, 282 Status: req.Status, 283 Reason: req.Reason, 284 Expiry: req.Expiry.UTC(), 285 RevokedAt: req.RevokedAt.UTC(), 286 PEM: string(cert), 287 }, 288 } 289 290 db := ca.GetDB() 291 res, err := db.NamedExec("", insertSQL, record) 292 if err != nil { 293 return errors.Wrap(err, "Failed to insert record into database") 294 } 295 296 numRowsAffected, err := res.RowsAffected() 297 298 if numRowsAffected == 0 { 299 return errors.New("Failed to insert the certificate record; no rows affected") 300 } 301 302 if numRowsAffected != 1 { 303 return errors.Errorf("Expected to affect 1 entry in certificate database but affected %d", 304 numRowsAffected) 305 } 306 307 return err 308 } 309 310 func getCertReq(id, serial, aki string, notrevoked, notexpired bool, revokedTimeStart, revokedTimeEnd, expiredTimeStart, expiredTimeEnd *time.Time) *certificaterequest.Impl { 311 return &certificaterequest.Impl{ 312 ID: id, 313 SerialNumber: serial, 314 Aki: aki, 315 Notexpired: notexpired, 316 Notrevoked: notrevoked, 317 ExpiredTimeStart: expiredTimeStart, 318 ExpiredTimeEnd: expiredTimeEnd, 319 RevokedTimeStart: revokedTimeStart, 320 RevokedTimeEnd: revokedTimeEnd, 321 } 322 }