github.com/bestchains/fabric-ca@v2.0.0-alpha+incompatible/test/integration/default/default_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package defserver 8 9 import ( 10 "bytes" 11 "crypto/ecdsa" 12 "crypto/elliptic" 13 "crypto/rand" 14 "crypto/x509" 15 "crypto/x509/pkix" 16 "encoding/pem" 17 "fmt" 18 "io" 19 "math/big" 20 "os" 21 "path/filepath" 22 "testing" 23 "time" 24 25 "github.com/cloudflare/cfssl/certdb" 26 "github.com/cloudflare/cfssl/config" 27 "github.com/cloudflare/cfssl/log" 28 "github.com/hyperledger/fabric-ca/cmd/fabric-ca-client/command" 29 "github.com/hyperledger/fabric-ca/lib" 30 "github.com/hyperledger/fabric-ca/lib/metadata" 31 "github.com/hyperledger/fabric-ca/lib/server/db" 32 "github.com/hyperledger/fabric-ca/util" 33 "github.com/pkg/errors" 34 "github.com/stretchr/testify/assert" 35 ) 36 37 const ( 38 cmdName = "fabric-ca-client" 39 clientHome = "clientHome" 40 ) 41 42 var ( 43 defaultServer *lib.Server 44 defaultServerPort = 7054 45 defaultServerEnrollURL = fmt.Sprintf("http://admin:adminpw@localhost:%d", defaultServerPort) 46 defaultServerHomeDir = "defaultServerDir" 47 storeCertsDir = "/tmp/testCerts" 48 ) 49 50 func TestMain(m *testing.M) { 51 var err error 52 53 metadata.Version = "1.1.0" 54 55 os.RemoveAll(defaultServerHomeDir) 56 defaultServer, err = getDefaultServer() 57 if err != nil { 58 log.Errorf("Failed to get instance of server: %s", err) 59 os.Exit(1) 60 } 61 62 err = defaultServer.Start() 63 if err != nil { 64 log.Errorf("Failed to start server: %s", err) 65 os.Exit(1) 66 } 67 68 rc := m.Run() 69 70 err = defaultServer.Stop() 71 if err != nil { 72 log.Errorf("Failed to stop server: %s, integration test results: %d", err, rc) 73 os.Exit(1) 74 } 75 76 os.RemoveAll(defaultServerHomeDir) 77 os.RemoveAll(storeCertsDir) 78 os.Exit(rc) 79 } 80 81 func TestListCertificateCmdNegative(t *testing.T) { 82 var err error 83 // Remove default client home location to remove any existing enrollment information 84 os.RemoveAll(filepath.Dir(util.GetDefaultConfigFile("fabric-ca-client"))) 85 86 // Command should fail if caller has not yet enrolled 87 err = command.RunMain([]string{cmdName, "certificate", "list", "-d"}) 88 util.ErrorContains(t, err, "Enrollment information does not exist", "Should have failed to call command, if caller has not yet enrolled") 89 90 // Enroll a user that will be used for subsequent certificate commands 91 err = command.RunMain([]string{cmdName, "enroll", "-u", defaultServerEnrollURL, "-d"}) 92 util.FatalError(t, err, "Failed to enroll user") 93 94 // Test with --revocation flag 95 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "-30d:-15d"}) 96 t.Log("Error: ", err) 97 assert.Error(t, err, "Should fail, only one ':' specified need to specify two '::'") 98 99 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "30d::-15d"}) 100 t.Log("Error: ", err) 101 assert.Error(t, err, "Should fail, missing +/- on starting duration") 102 103 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "+30d::15d"}) 104 t.Log("Error: ", err) 105 assert.Error(t, err, "Should fail, missing +/- on ending duration") 106 107 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "+30d::+15y"}) 108 t.Log("Error: ", err) 109 assert.Error(t, err, "Should fail, invalid duration type (y)") 110 111 // Test with --expiration flag 112 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "-30d:-15d"}) 113 t.Log("Error: ", err) 114 assert.Error(t, err, "Should fail, only one ':' specified need to specify two '::'") 115 116 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "30d::-15d"}) 117 t.Log("Error: ", err) 118 assert.Error(t, err, "Should fail, missing +/- on starting duration") 119 120 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "+30d::15d"}) 121 t.Log("Error: ", err) 122 assert.Error(t, err, "Should fail, missing +/- on ending duration") 123 124 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "1/30/18::2/14/2018"}) 125 t.Log("Error: ", err) 126 assert.Error(t, err, "Should fail, using slashes instead of dashes in time format") 127 } 128 129 func TestListCertificateCmdPositive(t *testing.T) { 130 populateCertificatesTable(t, defaultServer) 131 132 var err error 133 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "+30d::+15d", "--notexpired"}) 134 t.Log("Error: ", err) 135 assert.Error(t, err, "Should fail, --expiration and --notexpired together") 136 137 err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "-30d::-10d", "--notrevoked"}) 138 t.Log("Error: ", err) 139 assert.Error(t, err, "Should fail, --revocation and --notrevoked together") 140 141 // Enroll a user that will be used for subsequent certificate commands 142 err = command.RunMain([]string{cmdName, "enroll", "-u", defaultServerEnrollURL, "-d"}) 143 util.FatalError(t, err, "Failed to enroll user") 144 145 err = command.RunMain([]string{cmdName, "certificate", "list", "-d"}) 146 assert.NoError(t, err, "Failed to get certificates") 147 148 result, err := captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "expire1"}) 149 assert.NoError(t, err, "Failed to get certificate for an id") 150 assert.Contains(t, result, "expire1") 151 152 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--serial", "1111"}) 153 assert.NoError(t, err, "Failed to parse a correctly formatted revocation duration") 154 assert.Contains(t, result, "Serial Number: 1111") 155 156 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--aki", "9876"}) 157 assert.NoError(t, err, "Failed to parse a correctly formatted revocation duration") 158 assert.Contains(t, result, "Serial Number: 1111") 159 assert.Contains(t, result, "Serial Number: 1112") 160 161 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--serial", "1112", "--aki", "9876"}) 162 assert.NoError(t, err, "Failed to parse a correctly formatted revocation duration") 163 assert.Contains(t, result, "Serial Number: 1112") 164 assert.NotContains(t, result, "Serial Number: 1111") 165 166 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "testCertificate3", "--aki", "9876AB"}) 167 assert.NoError(t, err, "Failed to parse a correctly formatted revocation duration") 168 assert.Contains(t, result, "1113") 169 assert.Contains(t, result, "testCertificate3") 170 assert.NotContains(t, result, "testCertificate1") 171 172 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "expire1", "--expiration", "2018-01-01::2018-03-05"}) 173 assert.NoError(t, err, "Failed to parse a correctly formatted expiration date range") 174 assert.Contains(t, result, "expire1") 175 assert.NotContains(t, result, "expire3") 176 177 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "revoked2", "--revocation", "2017-01-01::2017-12-31"}) 178 assert.NoError(t, err, "Failed to parse a correctly formatted revocation date range") 179 assert.Contains(t, result, "revoked2") 180 assert.NotContains(t, result, "revoked3") 181 182 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "2018-03-01T00:00:00Z::2018-03-03T23:00:00Z"}) 183 assert.NoError(t, err, "Failed to parse a correctly formatted expiration date range") 184 assert.Contains(t, result, "Serial Number: 1121") 185 assert.NotContains(t, result, "Serial Number: 1123") 186 187 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "2017-02-01T01:00:00Z::2017-02-20T23:00:00Z"}) 188 assert.NoError(t, err, "Failed to parse a correctly formatted revocation date range") 189 assert.Contains(t, result, "Serial Number: 1132") 190 assert.NotContains(t, result, "Serial Number: 1131") 191 192 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "now::+101h"}) 193 assert.NoError(t, err, "Failed to parse a expiration date range using 'now'") 194 assert.Contains(t, result, "Serial Number: 1123") 195 assert.NotContains(t, result, "Serial Number: 1121") 196 197 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "-15d::now"}) 198 assert.NoError(t, err, "Failed to parse a revocation date range using 'now'") 199 assert.Contains(t, result, "Serial Number: 1131") 200 assert.NotContains(t, result, "Serial Number: 1111") 201 202 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "now::"}) 203 assert.NoError(t, err, "Failed to parse a expiration date range using 'now' and empty end date") 204 assert.NotContains(t, result, "Serial Number: 1121") 205 206 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "::now"}) 207 assert.NoError(t, err, "Failed to parse a expiration date range using 'now' and empty start date") 208 assert.Contains(t, result, "Serial Number: 1121") 209 assert.Contains(t, result, "Serial Number: 1122") 210 assert.Contains(t, result, "Serial Number: 1124") 211 212 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "::now", "--notrevoked"}) 213 assert.NoError(t, err, "Failed to parse a expiration date range using 'now' and empty start date") 214 assert.Contains(t, result, "Serial Number: 1121") 215 assert.Contains(t, result, "Serial Number: 1122") 216 assert.NotContains(t, result, "Serial Number: 1124") 217 218 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "2018-02-01T01:00:00Z::"}) 219 assert.NoError(t, err, "Failed to parse a revocation date range using 'now' and empty end date") 220 assert.Contains(t, result, "1131") 221 222 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "::now"}) 223 assert.NoError(t, err, "Failed to parse a revocation date range using 'now' and empty start date") 224 assert.Contains(t, result, "Serial Number: 1131") 225 assert.Contains(t, result, "Serial Number: 1132") 226 assert.Contains(t, result, "Serial Number: 1124") 227 228 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "::now", "--notexpired"}) 229 assert.NoError(t, err, "Failed to parse a revocation date range using 'now' and empty start date") 230 assert.Contains(t, result, "Serial Number: 1131") 231 assert.Contains(t, result, "Serial Number: 1132") 232 assert.NotContains(t, result, "Serial Number: 1124") 233 234 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--notrevoked", "--notexpired"}) 235 assert.NoError(t, err, "Failed to parse a revocation date range using 'now' and empty start date") 236 assert.Contains(t, result, "Serial Number: 1111") 237 assert.Contains(t, result, "Serial Number: 1112") 238 assert.Contains(t, result, "Serial Number: 1113") 239 assert.Contains(t, result, "Serial Number: 1123") 240 assert.NotContains(t, result, "Serial Number: 1121") 241 assert.NotContains(t, result, "Serial Number: 1122") 242 assert.NotContains(t, result, "Serial Number: 1124") 243 assert.NotContains(t, result, "Serial Number: 1131") 244 assert.NotContains(t, result, "Serial Number: 1132") 245 assert.NotContains(t, result, "Serial Number: 1133") 246 247 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "fakeID"}) 248 assert.NoError(t, err, "Should not error if the ID does not exist") 249 assert.Contains(t, result, "No results returned") 250 251 result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "--id", "expire1", "--store", storeCertsDir}) 252 assert.NoError(t, err, "Should not error if the ID does not exist") 253 assert.Equal(t, true, util.FileExists(filepath.Join(storeCertsDir, "expire1-1.pem"))) 254 assert.Equal(t, true, util.FileExists(filepath.Join(storeCertsDir, "expire1-2.pem"))) 255 assert.Contains(t, result, "Serial Number: 1121") 256 assert.Contains(t, result, "Serial Number: 1124") 257 } 258 259 func populateCertificatesTable(t *testing.T, srv *lib.Server) { 260 var err error 261 262 dur, err := time.ParseDuration("+100h") 263 util.FatalError(t, err, "Failed to parse duration '+100h'") 264 futureTime := time.Now().Add(dur).UTC() 265 266 dur, err = time.ParseDuration("-72h") 267 util.FatalError(t, err, "Failed to parse duration '-72h'") 268 pastTime := time.Now().Add(dur).UTC() 269 270 // Active Certs 271 err = testInsertCertificate(&certdb.CertificateRecord{ 272 Serial: "1111", 273 AKI: "9876", 274 Expiry: futureTime, 275 }, "testCertificate1", srv) 276 util.FatalError(t, err, "Failed to insert certificate with serial/AKI") 277 278 err = testInsertCertificate(&certdb.CertificateRecord{ 279 Serial: "1112", 280 AKI: "9876", 281 Expiry: futureTime, 282 }, "testCertificate2", srv) 283 util.FatalError(t, err, "Failed to insert certificate with serial/AKI") 284 285 err = testInsertCertificate(&certdb.CertificateRecord{ 286 Serial: "1113", 287 AKI: "9876ab", 288 Expiry: futureTime, 289 }, "testCertificate3", srv) 290 util.FatalError(t, err, "Failed to insert certificate with serial/AKI") 291 292 // Expired 293 err = testInsertCertificate(&certdb.CertificateRecord{ 294 Serial: "1121", 295 AKI: "98765", 296 Expiry: time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC), 297 }, "expire1", srv) 298 util.FatalError(t, err, "Failed to insert certificate with expiration date") 299 300 err = testInsertCertificate(&certdb.CertificateRecord{ 301 Serial: "1122", 302 AKI: "98765", 303 Expiry: time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC), 304 }, "expire3", srv) 305 util.FatalError(t, err, "Failed to insert certificate with expiration date") 306 307 // Not Expired 308 err = testInsertCertificate(&certdb.CertificateRecord{ 309 Serial: "1123", 310 AKI: "98765", 311 Expiry: futureTime, 312 }, "expire2", srv) 313 util.FatalError(t, err, "Failed to insert certificate with expiration date") 314 315 // Expired and Revoked 316 err = testInsertCertificate(&certdb.CertificateRecord{ 317 Serial: "1124", 318 AKI: "98765", 319 Expiry: time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC), 320 RevokedAt: pastTime, 321 }, "expire1", srv) 322 util.FatalError(t, err, "Failed to insert certificate with expiration date") 323 324 // Revoked 325 err = testInsertCertificate(&certdb.CertificateRecord{ 326 Serial: "1131", 327 AKI: "98765", 328 Expiry: futureTime, 329 RevokedAt: pastTime, 330 }, "revoked1", srv) 331 util.FatalError(t, err, "Failed to insert certificate with revocation date") 332 333 err = testInsertCertificate(&certdb.CertificateRecord{ 334 Serial: "1132", 335 AKI: "98765", 336 Expiry: futureTime, 337 RevokedAt: time.Date(2017, time.February, 15, 0, 0, 0, 0, time.UTC), 338 }, "revoked2", srv) 339 util.FatalError(t, err, "Failed to insert certificate with revocation date") 340 341 err = testInsertCertificate(&certdb.CertificateRecord{ 342 Serial: "1133", 343 AKI: "98765", 344 Expiry: futureTime, 345 RevokedAt: time.Date(2017, time.February, 15, 0, 0, 0, 0, time.UTC), 346 }, "revoked3", srv) 347 util.FatalError(t, err, "Failed to insert certificate with revocation date") 348 } 349 350 func captureCLICertificatesOutput(f func(args []string) error, args []string) (string, error) { 351 old := os.Stdout 352 r, w, err := os.Pipe() 353 if err != nil { 354 panic(err) 355 } 356 os.Stdout = w 357 err = f(args) 358 if err != nil { 359 return "", err 360 } 361 w.Close() 362 os.Stdout = old 363 var buf bytes.Buffer 364 io.Copy(&buf, r) 365 return buf.String(), nil 366 } 367 368 func TestRevokeWithColons(t *testing.T) { 369 var err error 370 371 err = testInsertCertificate(&certdb.CertificateRecord{ 372 Serial: "11aa22bb", 373 AKI: "33cc44dd", 374 }, "testingRevoke", defaultServer) 375 util.FatalError(t, err, "Failed to insert certificate with serial/AKI") 376 377 // Enroll a user that will be used for subsequent revoke commands 378 err = command.RunMain([]string{cmdName, "enroll", "-u", defaultServerEnrollURL, "-d"}) 379 util.FatalError(t, err, "Failed to enroll user") 380 381 err = command.RunMain([]string{cmdName, "register", "-u", defaultServerEnrollURL, "--id.name", "testingRevoke", "-d"}) 382 util.FatalError(t, err, "Failed to enroll user") 383 384 err = command.RunMain([]string{cmdName, "revoke", "-s", "11:AA:22:bb", "-a", "33:Cc:44:DD", "-d"}) 385 assert.NoError(t, err, "Failed to revoke certificate, when serial number and AKI contained colons") 386 } 387 388 func getDefaultServer() (*lib.Server, error) { 389 affiliations := map[string]interface{}{ 390 "hyperledger": map[string]interface{}{ 391 "fabric": []string{"ledger", "orderer", "security"}, 392 "fabric-ca": nil, 393 "sdk": nil, 394 }, 395 "org2": []string{"dept1"}, 396 "org1": nil, 397 "org2dept1": nil, 398 } 399 profiles := map[string]*config.SigningProfile{ 400 "tls": &config.SigningProfile{ 401 Usage: []string{"signing", "key encipherment", "server auth", "client auth", "key agreement"}, 402 ExpiryString: "8760h", 403 }, 404 "ca": &config.SigningProfile{ 405 Usage: []string{"cert sign", "crl sign"}, 406 ExpiryString: "8760h", 407 CAConstraint: config.CAConstraint{ 408 IsCA: true, 409 MaxPathLen: 0, 410 }, 411 }, 412 } 413 defaultProfile := &config.SigningProfile{ 414 Usage: []string{"cert sign"}, 415 ExpiryString: "8760h", 416 } 417 srv := &lib.Server{ 418 Config: &lib.ServerConfig{ 419 Port: defaultServerPort, 420 Debug: true, 421 }, 422 CA: lib.CA{ 423 Config: &lib.CAConfig{ 424 Intermediate: lib.IntermediateCA{ 425 ParentServer: lib.ParentServer{ 426 URL: "", 427 }, 428 }, 429 Affiliations: affiliations, 430 Registry: lib.CAConfigRegistry{ 431 MaxEnrollments: -1, 432 }, 433 Signing: &config.Signing{ 434 Profiles: profiles, 435 Default: defaultProfile, 436 }, 437 Version: "1.1.0", // The default test server/ca should use the latest version 438 }, 439 }, 440 HomeDir: defaultServerHomeDir, 441 } 442 // The bootstrap user's affiliation is the empty string, which 443 // means the user is at the affiliation root 444 err := srv.RegisterBootstrapUser("admin", "adminpw", "") 445 if err != nil { 446 return nil, err 447 } 448 return srv, nil 449 } 450 451 func testInsertCertificate(req *certdb.CertificateRecord, id string, srv *lib.Server) error { 452 priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 453 if err != nil { 454 return errors.Errorf("Failed to generate private key: %s", err) 455 } 456 457 serial := new(big.Int) 458 serial.SetString(req.Serial, 10) //base 10 459 template := x509.Certificate{ 460 Subject: pkix.Name{ 461 CommonName: id, 462 }, 463 SerialNumber: serial, 464 AuthorityKeyId: []byte(req.AKI), 465 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 466 } 467 468 derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) 469 if err != nil { 470 return err 471 } 472 473 cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) 474 record := &db.CertRecord{ 475 ID: id, 476 CertificateRecord: certdb.CertificateRecord{ 477 Serial: req.Serial, 478 AKI: req.AKI, 479 CALabel: req.CALabel, 480 Status: req.Status, 481 Reason: req.Reason, 482 Expiry: req.Expiry.UTC(), 483 RevokedAt: req.RevokedAt.UTC(), 484 PEM: string(cert), 485 }, 486 } 487 488 db := srv.CA.GetDB() 489 res, err := db.NamedExec("", `INSERT INTO certificates (id, serial_number, authority_key_identifier, ca_label, status, reason, expiry, revoked_at, pem, level) 490 VALUES (:id, :serial_number, :authority_key_identifier, :ca_label, :status, :reason, :expiry, :revoked_at, :pem, :level);`, record) 491 492 if err != nil { 493 return errors.Wrap(err, "Failed to insert record into database") 494 } 495 496 numRowsAffected, err := res.RowsAffected() 497 498 if numRowsAffected == 0 { 499 return errors.New("Failed to insert the certificate record; no rows affected") 500 } 501 502 if numRowsAffected != 1 { 503 return errors.Errorf("Expected to affect 1 entry in certificate database but affected %d", 504 numRowsAffected) 505 } 506 507 return err 508 }