github.com/blockchain-gm/fabric-ca@v0.0.0-20200423072702-b2c40c7ac69c/lib/client.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 "bytes" 11 "encoding/json" 12 "fmt" 13 "io/ioutil" 14 "net" 15 "net/http" 16 "net/url" 17 "os" 18 "path" 19 "path/filepath" 20 "strconv" 21 "strings" 22 23 cfsslapi "github.com/cloudflare/cfssl/api" 24 "github.com/cloudflare/cfssl/csr" 25 "github.com/cloudflare/cfssl/log" 26 proto "github.com/golang/protobuf/proto" 27 fp256bn "github.com/hyperledger/fabric-amcl/amcl/FP256BN" 28 "github.com/hyperledger/fabric-ca/api" 29 "github.com/hyperledger/fabric-ca/lib/client/credential" 30 idemixcred "github.com/hyperledger/fabric-ca/lib/client/credential/idemix" 31 x509cred "github.com/hyperledger/fabric-ca/lib/client/credential/x509" 32 "github.com/hyperledger/fabric-ca/lib/common" 33 "github.com/hyperledger/fabric-ca/lib/streamer" 34 "github.com/hyperledger/fabric-ca/lib/tls" 35 "github.com/hyperledger/fabric-ca/util" 36 "github.com/hyperledger/fabric/bccsp" 37 "github.com/hyperledger/fabric/idemix" 38 "github.com/mitchellh/mapstructure" 39 "github.com/pkg/errors" 40 ) 41 42 // Client is the fabric-ca client object 43 type Client struct { 44 // The client's home directory 45 HomeDir string `json:"homeDir,omitempty"` 46 // The client's configuration 47 Config *ClientConfig 48 // Denotes if the client object is already initialized 49 initialized bool 50 // File and directory paths 51 keyFile, certFile, idemixCredFile, idemixCredsDir, ipkFile, caCertsDir string 52 // The crypto service provider (BCCSP) 53 csp bccsp.BCCSP 54 // HTTP client associated with this Fabric CA client 55 httpClient *http.Client 56 // Public key of Idemix issuer 57 issuerPublicKey *idemix.IssuerPublicKey 58 } 59 60 // GetCAInfoResponse is the response from the GetCAInfo call 61 type GetCAInfoResponse struct { 62 // CAName is the name of the CA 63 CAName string 64 // CAChain is the PEM-encoded bytes of the fabric-ca-server's CA chain. 65 // The 1st element of the chain is the root CA cert 66 CAChain []byte 67 // Idemix issuer public key of the CA 68 IssuerPublicKey []byte 69 // Idemix issuer revocation public key of the CA 70 IssuerRevocationPublicKey []byte 71 // Version of the server 72 Version string 73 } 74 75 // EnrollmentResponse is the response from Client.Enroll and Identity.Reenroll 76 type EnrollmentResponse struct { 77 Identity *Identity 78 CAInfo GetCAInfoResponse 79 } 80 81 // Init initializes the client 82 func (c *Client) Init() error { 83 if !c.initialized { 84 cfg := c.Config 85 log.Debugf("Initializing client with config: %+v", cfg) 86 if cfg.MSPDir == "" { 87 cfg.MSPDir = "msp" 88 } 89 mspDir, err := util.MakeFileAbs(cfg.MSPDir, c.HomeDir) 90 if err != nil { 91 return err 92 } 93 cfg.MSPDir = mspDir 94 // Key directory and file 95 keyDir := path.Join(mspDir, "keystore") 96 err = os.MkdirAll(keyDir, 0700) 97 if err != nil { 98 return errors.Wrap(err, "Failed to create keystore directory") 99 } 100 c.keyFile = path.Join(keyDir, "key.pem") 101 102 // Cert directory and file 103 certDir := path.Join(mspDir, "signcerts") 104 err = os.MkdirAll(certDir, 0755) 105 if err != nil { 106 return errors.Wrap(err, "Failed to create signcerts directory") 107 } 108 c.certFile = path.Join(certDir, "cert.pem") 109 110 // CA certs directory 111 c.caCertsDir = path.Join(mspDir, "cacerts") 112 err = os.MkdirAll(c.caCertsDir, 0755) 113 if err != nil { 114 return errors.Wrap(err, "Failed to create cacerts directory") 115 } 116 117 // CA's Idemix public key 118 c.ipkFile = filepath.Join(mspDir, "IssuerPublicKey") 119 120 // Idemix credentials directory 121 c.idemixCredsDir = path.Join(mspDir, "user") 122 err = os.MkdirAll(c.idemixCredsDir, 0755) 123 if err != nil { 124 return errors.Wrap(err, "Failed to create Idemix credentials directory 'user'") 125 } 126 c.idemixCredFile = path.Join(c.idemixCredsDir, "SignerConfig") 127 128 // Initialize BCCSP (the crypto layer) 129 c.csp, err = util.InitBCCSP(&cfg.CSP, mspDir, c.HomeDir) 130 if err != nil { 131 return err 132 } 133 // Create http.Client object and associate it with this client 134 err = c.initHTTPClient() 135 if err != nil { 136 return err 137 } 138 139 // Successfully initialized the client 140 c.initialized = true 141 } 142 SetProviderName(c.Config.CSP.ProviderName) 143 return nil 144 } 145 146 func (c *Client) initHTTPClient() error { 147 tr := new(http.Transport) 148 if c.Config.TLS.Enabled { 149 log.Info("TLS Enabled") 150 151 err := tls.AbsTLSClient(&c.Config.TLS, c.HomeDir) 152 if err != nil { 153 return err 154 } 155 156 tlsConfig, err2 := tls.GetClientTLSConfig(&c.Config.TLS, c.csp) 157 if err2 != nil { 158 return fmt.Errorf("Failed to get client TLS config: %s", err2) 159 } 160 // set the default ciphers 161 tlsConfig.CipherSuites = tls.DefaultCipherSuites 162 tr.TLSClientConfig = tlsConfig 163 } 164 c.httpClient = &http.Client{Transport: tr} 165 return nil 166 } 167 168 // GetCAInfo returns generic CA information 169 func (c *Client) GetCAInfo(req *api.GetCAInfoRequest) (*GetCAInfoResponse, error) { 170 err := c.Init() 171 if err != nil { 172 return nil, err 173 } 174 body, err := util.Marshal(req, "GetCAInfo") 175 if err != nil { 176 return nil, err 177 } 178 cainforeq, err := c.newPost("cainfo", body) 179 if err != nil { 180 return nil, err 181 } 182 netSI := &common.CAInfoResponseNet{} 183 err = c.SendReq(cainforeq, netSI) 184 if err != nil { 185 return nil, err 186 } 187 localSI := &GetCAInfoResponse{} 188 err = c.net2LocalCAInfo(netSI, localSI) 189 if err != nil { 190 return nil, err 191 } 192 return localSI, nil 193 } 194 195 // GenCSR generates a CSR (Certificate Signing Request) 196 func (c *Client) GenCSR(req *api.CSRInfo, id string) ([]byte, bccsp.Key, error) { 197 log.Debugf("GenCSR %+v", req) 198 199 err := c.Init() 200 if err != nil { 201 return nil, nil, err 202 } 203 204 cr := c.newCertificateRequest(req) 205 cr.CN = id 206 207 if (cr.KeyRequest == nil) || (cr.KeyRequest.Size() == 0 && cr.KeyRequest.Algo() == "") { 208 // cr.KeyRequest = newCfsslBasicKeyRequest(api.NewBasicKeyRequest()) 209 keyRequest := api.NewBasicKeyRequest() 210 if IsGMConfig() { 211 keyRequest.Algo = "gmsm2" //"GMSM3" //"gmsm2" 212 keyRequest.Size = 256 213 cr.KeyRequest = newCfsslBasicKeyRequest(keyRequest) 214 215 } else { 216 keyRequest.Algo = "ecdsa" 217 keyRequest.Size = 256 218 cr.KeyRequest = newCfsslBasicKeyRequest(keyRequest) 219 } 220 } 221 222 key, cspSigner, err := util.BCCSPKeyRequestGenerate(cr, c.csp) 223 if err != nil { 224 log.Debugf("failed generating BCCSP key: %s", err) 225 return nil, nil, err 226 } 227 228 // csrPEM, err := csr.Generate(cspSigner, cr) 229 // if err != nil { 230 // log.Debugf("failed generating CSR: %s", err) 231 // return nil, nil, err 232 // } 233 var csrPEM []byte 234 if IsGMConfig() { 235 csrPEM, err = generate(cspSigner, cr, key) 236 if err != nil { 237 log.Debugf("failed generating CSR: %s", err) 238 return nil, nil, err 239 } 240 } else { 241 csrPEM, err = csr.Generate(cspSigner, cr) 242 if err != nil { 243 log.Debugf("failed generating CSR: %s", err) 244 return nil, nil, err 245 } 246 } 247 248 return csrPEM, key, nil 249 } 250 251 // Enroll enrolls a new identity 252 // @param req The enrollment request 253 func (c *Client) Enroll(req *api.EnrollmentRequest) (*EnrollmentResponse, error) { 254 log.Debugf("Enrolling %+v", req) 255 256 err := c.Init() 257 if err != nil { 258 return nil, err 259 } 260 261 if strings.ToLower(req.Type) == "idemix" { 262 return c.handleIdemixEnroll(req) 263 } 264 return c.handleX509Enroll(req) 265 } 266 267 // Convert from network to local CA information 268 func (c *Client) net2LocalCAInfo(net *common.CAInfoResponseNet, local *GetCAInfoResponse) error { 269 caChain, err := util.B64Decode(net.CAChain) 270 if err != nil { 271 return errors.WithMessage(err, "Failed to decode CA chain") 272 } 273 if net.IssuerPublicKey != "" { 274 ipk, err := util.B64Decode(net.IssuerPublicKey) 275 if err != nil { 276 return errors.WithMessage(err, "Failed to decode issuer public key") 277 } 278 local.IssuerPublicKey = ipk 279 } 280 if net.IssuerRevocationPublicKey != "" { 281 rpk, err := util.B64Decode(net.IssuerRevocationPublicKey) 282 if err != nil { 283 return errors.WithMessage(err, "Failed to decode issuer revocation key") 284 } 285 local.IssuerRevocationPublicKey = rpk 286 } 287 local.CAName = net.CAName 288 local.CAChain = caChain 289 local.Version = net.Version 290 return nil 291 } 292 293 func (c *Client) handleX509Enroll(req *api.EnrollmentRequest) (*EnrollmentResponse, error) { 294 // Generate the CSR 295 csrPEM, key, err := c.GenCSR(req.CSR, req.Name) 296 if err != nil { 297 return nil, errors.WithMessage(err, "Failure generating CSR") 298 } 299 300 reqNet := &api.EnrollmentRequestNet{ 301 CAName: req.CAName, 302 AttrReqs: req.AttrReqs, 303 } 304 305 if req.CSR != nil { 306 reqNet.SignRequest.Hosts = req.CSR.Hosts 307 } 308 reqNet.SignRequest.Request = string(csrPEM) 309 reqNet.SignRequest.Profile = req.Profile 310 reqNet.SignRequest.Label = req.Label 311 312 body, err := util.Marshal(reqNet, "SignRequest") 313 if err != nil { 314 return nil, err 315 } 316 317 // Send the CSR to the fabric-ca server with basic auth header 318 post, err := c.newPost("enroll", body) 319 if err != nil { 320 return nil, err 321 } 322 post.SetBasicAuth(req.Name, req.Secret) 323 var result common.EnrollmentResponseNet 324 err = c.SendReq(post, &result) 325 if err != nil { 326 return nil, err 327 } 328 329 // Create the enrollment response 330 return c.newEnrollmentResponse(&result, req.Name, key) 331 } 332 333 // Handles enrollment request for an Idemix credential 334 // 1. Sends a request with empty body to the /api/v1/idemix/credentail REST endpoint 335 // of the server to get a Nonce from the CA 336 // 2. Constructs a credential request using the nonce, CA's idemix public key 337 // 3. Sends a request with the CredentialRequest object in the body to the 338 // /api/v1/idemix/credentail REST endpoint to get a credential 339 func (c *Client) handleIdemixEnroll(req *api.EnrollmentRequest) (*EnrollmentResponse, error) { 340 log.Debugf("Getting nonce from CA %s", req.CAName) 341 reqNet := &api.IdemixEnrollmentRequestNet{ 342 CAName: req.CAName, 343 } 344 var identity *Identity 345 346 // Get nonce from the CA 347 body, err := util.Marshal(reqNet, "NonceRequest") 348 if err != nil { 349 return nil, errors.WithMessage(err, "Failed to marshal nonce request") 350 } 351 post, err := c.newPost("idemix/credential", body) 352 if err != nil { 353 return nil, errors.WithMessage(err, "Failed to create HTTP request for getting a nonce") 354 } 355 err = c.addAuthHeaderForIdemixEnroll(req, identity, body, post) 356 if err != nil { 357 return nil, errors.WithMessage(err, 358 "Either username/password or X509 enrollment certificate is required to request an Idemix credential") 359 } 360 361 // Send the request and process the response 362 var result common.IdemixEnrollmentResponseNet 363 err = c.SendReq(post, &result) 364 if err != nil { 365 return nil, err 366 } 367 nonceBytes, err := util.B64Decode(result.Nonce) 368 369 if err != nil { 370 return nil, errors.WithMessage(err, 371 fmt.Sprintf("Failed to decode nonce that was returned by CA %s", req.CAName)) 372 } 373 nonce := fp256bn.FromBytes(nonceBytes) 374 log.Infof("Successfully got nonce from CA %s", req.CAName) 375 376 ipkBytes := []byte{} 377 ipkBytes, err = util.B64Decode(result.CAInfo.IssuerPublicKey) 378 if err != nil { 379 return nil, errors.WithMessage(err, fmt.Sprintf("Failed to decode issuer public key that was returned by CA %s", req.CAName)) 380 } 381 // Create credential request 382 credReq, sk, err := c.newIdemixCredentialRequest(nonce, ipkBytes) 383 if err != nil { 384 return nil, errors.WithMessage(err, "Failed to create an Idemix credential request") 385 } 386 reqNet.CredRequest = credReq 387 log.Info("Successfully created an Idemix credential request") 388 389 body, err = util.Marshal(reqNet, "CredentialRequest") 390 if err != nil { 391 return nil, errors.WithMessage(err, "Failed to marshal Idemix credential request") 392 } 393 394 // Send the cred request to the CA 395 post, err = c.newPost("idemix/credential", body) 396 if err != nil { 397 return nil, errors.WithMessage(err, "Failed to create HTTP request for getting Idemix credential") 398 } 399 err = c.addAuthHeaderForIdemixEnroll(req, identity, body, post) 400 if err != nil { 401 return nil, errors.WithMessage(err, 402 "Either username/password or X509 enrollment certificate is required to request idemix credential") 403 } 404 err = c.SendReq(post, &result) 405 if err != nil { 406 return nil, err 407 } 408 log.Infof("Successfully received Idemix credential from CA %s", req.CAName) 409 return c.newIdemixEnrollmentResponse(identity, &result, sk, req.Name) 410 } 411 412 // addAuthHeaderForIdemixEnroll adds authenticate header to the specified HTTP request 413 // It adds basic authentication header if userName and password are specified in the 414 // specified EnrollmentRequest object. Else, checks if a X509 credential in the client's 415 // MSP directory, if so, loads the identity, creates an oauth token based on the loaded 416 // identity's X509 credential, and adds the token to the HTTP request. The loaded 417 // identity is passed back to the caller. 418 func (c *Client) addAuthHeaderForIdemixEnroll(req *api.EnrollmentRequest, id *Identity, 419 body []byte, post *http.Request) error { 420 if req.Name != "" && req.Secret != "" { 421 post.SetBasicAuth(req.Name, req.Secret) 422 return nil 423 } 424 if id == nil { 425 err := c.checkX509Enrollment() 426 if err != nil { 427 return err 428 } 429 id, err = c.LoadMyIdentity() 430 if err != nil { 431 return err 432 } 433 } 434 err := id.addTokenAuthHdr(post, body) 435 if err != nil { 436 return err 437 } 438 return nil 439 } 440 441 // newEnrollmentResponse creates a client enrollment response from a network response 442 // @param result The result from server 443 // @param id Name of identity being enrolled or reenrolled 444 // @param key The private key which was used to sign the request 445 func (c *Client) newEnrollmentResponse(result *common.EnrollmentResponseNet, id string, key bccsp.Key) (*EnrollmentResponse, error) { 446 log.Debugf("newEnrollmentResponse %s", id) 447 certByte, err := util.B64Decode(result.Cert) 448 if err != nil { 449 return nil, errors.WithMessage(err, "Invalid response format from server") 450 } 451 signer, err := x509cred.NewSigner(key, certByte) 452 if err != nil { 453 return nil, err 454 } 455 x509Cred := x509cred.NewCredential(c.certFile, c.keyFile, c) 456 err = x509Cred.SetVal(signer) 457 if err != nil { 458 return nil, err 459 } 460 resp := &EnrollmentResponse{ 461 Identity: NewIdentity(c, id, []credential.Credential{x509Cred}), 462 } 463 err = c.net2LocalCAInfo(&result.ServerInfo, &resp.CAInfo) 464 if err != nil { 465 return nil, err 466 } 467 return resp, nil 468 } 469 470 // newIdemixEnrollmentResponse creates a client idemix enrollment response from a network response 471 func (c *Client) newIdemixEnrollmentResponse(identity *Identity, result *common.IdemixEnrollmentResponseNet, 472 sk *fp256bn.BIG, id string) (*EnrollmentResponse, error) { 473 log.Debugf("newIdemixEnrollmentResponse %s", id) 474 credBytes, err := util.B64Decode(result.Credential) 475 if err != nil { 476 return nil, errors.WithMessage(err, "Invalid response format from server") 477 } 478 479 criBytes, err := util.B64Decode(result.CRI) 480 if err != nil { 481 return nil, errors.WithMessage(err, "Invalid response format from server") 482 } 483 484 // Create SignerConfig object with credential bytes from the response 485 // and secret key 486 role, _ := result.Attrs["Role"].(int) 487 ou, _ := result.Attrs["OU"].(string) 488 enrollmentID, _ := result.Attrs["EnrollmentID"].(string) 489 signerConfig := &idemixcred.SignerConfig{ 490 Cred: credBytes, 491 Sk: idemix.BigToBytes(sk), 492 Role: role, 493 OrganizationalUnitIdentifier: ou, 494 EnrollmentID: enrollmentID, 495 CredentialRevocationInformation: criBytes, 496 } 497 498 // Create IdemixCredential object 499 cred := idemixcred.NewCredential(c.idemixCredFile, c) 500 err = cred.SetVal(signerConfig) 501 if err != nil { 502 return nil, err 503 } 504 if identity == nil { 505 identity = NewIdentity(c, id, []credential.Credential{cred}) 506 } else { 507 identity.creds = append(identity.creds, cred) 508 } 509 510 resp := &EnrollmentResponse{ 511 Identity: identity, 512 } 513 err = c.net2LocalCAInfo(&result.CAInfo, &resp.CAInfo) 514 if err != nil { 515 return nil, err 516 } 517 log.Infof("Successfully processed response from the CA") 518 return resp, nil 519 } 520 521 // newCertificateRequest creates a certificate request which is used to generate 522 // a CSR (Certificate Signing Request) 523 func (c *Client) newCertificateRequest(req *api.CSRInfo) *csr.CertificateRequest { 524 cr := csr.CertificateRequest{} 525 if req != nil && req.Names != nil { 526 cr.Names = req.Names 527 } 528 if req != nil && req.Hosts != nil { 529 cr.Hosts = req.Hosts 530 } else { 531 // Default requested hosts are local hostname 532 hostname, _ := os.Hostname() 533 if hostname != "" { 534 cr.Hosts = make([]string, 1) 535 cr.Hosts[0] = hostname 536 } 537 } 538 if req != nil && req.KeyRequest != nil { 539 cr.KeyRequest = newCfsslBasicKeyRequest(req.KeyRequest) 540 } 541 if req != nil { 542 cr.CA = req.CA 543 cr.SerialNumber = req.SerialNumber 544 } 545 return &cr 546 } 547 548 // newIdemixCredentialRequest returns CredentialRequest object, a secret key, and a random number used in 549 // the creation of credential request. 550 func (c *Client) newIdemixCredentialRequest(nonce *fp256bn.BIG, ipkBytes []byte) (*idemix.CredRequest, *fp256bn.BIG, error) { 551 rng, err := idemix.GetRand() 552 if err != nil { 553 return nil, nil, err 554 } 555 sk := idemix.RandModOrder(rng) 556 557 issuerPubKey, err := c.getIssuerPubKey(ipkBytes) 558 if err != nil { 559 return nil, nil, err 560 } 561 return idemix.NewCredRequest(sk, idemix.BigToBytes(nonce), issuerPubKey, rng), sk, nil 562 } 563 564 func (c *Client) getIssuerPubKey(ipkBytes []byte) (*idemix.IssuerPublicKey, error) { 565 var err error 566 if ipkBytes == nil || len(ipkBytes) == 0 { 567 ipkBytes, err = ioutil.ReadFile(c.ipkFile) 568 if err != nil { 569 return nil, errors.Wrapf(err, "Error reading CA's Idemix public key at '%s'", c.ipkFile) 570 } 571 } 572 pubKey := &idemix.IssuerPublicKey{} 573 err = proto.Unmarshal(ipkBytes, pubKey) 574 if err != nil { 575 return nil, err 576 } 577 c.issuerPublicKey = pubKey 578 return c.issuerPublicKey, nil 579 } 580 581 // LoadMyIdentity loads the client's identity from disk 582 func (c *Client) LoadMyIdentity() (*Identity, error) { 583 err := c.Init() 584 if err != nil { 585 return nil, err 586 } 587 return c.LoadIdentity(c.keyFile, c.certFile, c.idemixCredFile) 588 } 589 590 // LoadIdentity loads an identity from disk 591 func (c *Client) LoadIdentity(keyFile, certFile, idemixCredFile string) (*Identity, error) { 592 log.Debugf("Loading identity: keyFile=%s, certFile=%s", keyFile, certFile) 593 err := c.Init() 594 if err != nil { 595 return nil, err 596 } 597 598 var creds []credential.Credential 599 var x509Found, idemixFound bool 600 x509Cred := x509cred.NewCredential(certFile, keyFile, c) 601 err = x509Cred.Load() 602 if err == nil { 603 x509Found = true 604 creds = append(creds, x509Cred) 605 } else { 606 log.Debugf("No X509 credential found at %s, %s", keyFile, certFile) 607 } 608 609 idemixCred := idemixcred.NewCredential(idemixCredFile, c) 610 err = idemixCred.Load() 611 if err == nil { 612 idemixFound = true 613 creds = append(creds, idemixCred) 614 } else { 615 log.Debugf("No Idemix credential found at %s", idemixCredFile) 616 } 617 618 if !x509Found && !idemixFound { 619 return nil, errors.New("Identity does not posses any enrollment credentials") 620 } 621 622 return c.NewIdentity(creds) 623 } 624 625 // NewIdentity creates a new identity 626 func (c *Client) NewIdentity(creds []credential.Credential) (*Identity, error) { 627 if len(creds) == 0 { 628 return nil, errors.New("No credentials spcified. Atleast one credential must be specified") 629 } 630 name, err := creds[0].EnrollmentID() 631 if err != nil { 632 return nil, err 633 } 634 if len(creds) == 1 { 635 return NewIdentity(c, name, creds), nil 636 } 637 638 //TODO: Get the enrollment ID from the creds...they all should return same value 639 // for i := 1; i < len(creds); i++ { 640 // localid, err := creds[i].EnrollmentID() 641 // if err != nil { 642 // return nil, err 643 // } 644 // if localid != name { 645 // return nil, errors.New("Specified credentials belong to different identities, they should be long to same identity") 646 // } 647 // } 648 return NewIdentity(c, name, creds), nil 649 } 650 651 // NewX509Identity creates a new identity 652 func (c *Client) NewX509Identity(name string, creds []credential.Credential) x509cred.Identity { 653 return NewIdentity(c, name, creds) 654 } 655 656 // LoadCSRInfo reads CSR (Certificate Signing Request) from a file 657 // @parameter path The path to the file contains CSR info in JSON format 658 func (c *Client) LoadCSRInfo(path string) (*api.CSRInfo, error) { 659 csrJSON, err := ioutil.ReadFile(path) 660 if err != nil { 661 return nil, err 662 } 663 var csrInfo api.CSRInfo 664 err = util.Unmarshal(csrJSON, &csrInfo, "LoadCSRInfo") 665 if err != nil { 666 return nil, err 667 } 668 return &csrInfo, nil 669 } 670 671 // GetCertFilePath returns the path to the certificate file for this client 672 func (c *Client) GetCertFilePath() string { 673 return c.certFile 674 } 675 676 // GetCSP returns BCCSP instance associated with this client 677 func (c *Client) GetCSP() bccsp.BCCSP { 678 return c.csp 679 } 680 681 // GetIssuerPubKey returns issuer public key associated with this client 682 func (c *Client) GetIssuerPubKey() (*idemix.IssuerPublicKey, error) { 683 if c.issuerPublicKey == nil { 684 return c.getIssuerPubKey(nil) 685 } 686 return c.issuerPublicKey, nil 687 } 688 689 // newGet create a new GET request 690 func (c *Client) newGet(endpoint string) (*http.Request, error) { 691 curl, err := c.getURL(endpoint) 692 if err != nil { 693 return nil, err 694 } 695 req, err := http.NewRequest("GET", curl, bytes.NewReader([]byte{})) 696 if err != nil { 697 return nil, errors.Wrapf(err, "Failed creating GET request for %s", curl) 698 } 699 return req, nil 700 } 701 702 // newPut create a new PUT request 703 func (c *Client) newPut(endpoint string, reqBody []byte) (*http.Request, error) { 704 curl, err := c.getURL(endpoint) 705 if err != nil { 706 return nil, err 707 } 708 req, err := http.NewRequest("PUT", curl, bytes.NewReader(reqBody)) 709 if err != nil { 710 return nil, errors.Wrapf(err, "Failed creating PUT request for %s", curl) 711 } 712 return req, nil 713 } 714 715 // newDelete create a new DELETE request 716 func (c *Client) newDelete(endpoint string) (*http.Request, error) { 717 curl, err := c.getURL(endpoint) 718 if err != nil { 719 return nil, err 720 } 721 req, err := http.NewRequest("DELETE", curl, bytes.NewReader([]byte{})) 722 if err != nil { 723 return nil, errors.Wrapf(err, "Failed creating DELETE request for %s", curl) 724 } 725 return req, nil 726 } 727 728 // NewPost create a new post request 729 func (c *Client) newPost(endpoint string, reqBody []byte) (*http.Request, error) { 730 curl, err := c.getURL(endpoint) 731 if err != nil { 732 return nil, err 733 } 734 req, err := http.NewRequest("POST", curl, bytes.NewReader(reqBody)) 735 if err != nil { 736 return nil, errors.Wrapf(err, "Failed posting to %s", curl) 737 } 738 return req, nil 739 } 740 741 // SendReq sends a request to the fabric-ca-server and fills in the result 742 func (c *Client) SendReq(req *http.Request, result interface{}) (err error) { 743 744 reqStr := util.HTTPRequestToString(req) 745 log.Debugf("Sending request\n%s", reqStr) 746 747 err = c.Init() 748 if err != nil { 749 return err 750 } 751 752 resp, err := c.httpClient.Do(req) 753 if err != nil { 754 return errors.Wrapf(err, "%s failure of request: %s", req.Method, reqStr) 755 } 756 var respBody []byte 757 if resp.Body != nil { 758 respBody, err = ioutil.ReadAll(resp.Body) 759 defer func() { 760 err := resp.Body.Close() 761 if err != nil { 762 log.Debugf("Failed to close the response body: %s", err.Error()) 763 } 764 }() 765 if err != nil { 766 return errors.Wrapf(err, "Failed to read response of request: %s", reqStr) 767 } 768 log.Debugf("Received response\n%s", util.HTTPResponseToString(resp)) 769 } 770 var body *cfsslapi.Response 771 if respBody != nil && len(respBody) > 0 { 772 body = new(cfsslapi.Response) 773 err = json.Unmarshal(respBody, body) 774 if err != nil { 775 return errors.Wrapf(err, "Failed to parse response: %s", respBody) 776 } 777 if len(body.Errors) > 0 { 778 var errorMsg string 779 for _, err := range body.Errors { 780 msg := fmt.Sprintf("Response from server: Error Code: %d - %s\n", err.Code, err.Message) 781 if errorMsg == "" { 782 errorMsg = msg 783 } else { 784 errorMsg = errorMsg + fmt.Sprintf("\n%s", msg) 785 } 786 } 787 return errors.Errorf(errorMsg) 788 } 789 } 790 scode := resp.StatusCode 791 if scode >= 400 { 792 return errors.Errorf("Failed with server status code %d for request:\n%s", scode, reqStr) 793 } 794 if body == nil { 795 return errors.Errorf("Empty response body:\n%s", reqStr) 796 } 797 if !body.Success { 798 return errors.Errorf("Server returned failure for request:\n%s", reqStr) 799 } 800 log.Debugf("Response body result: %+v", body.Result) 801 if result != nil { 802 return mapstructure.Decode(body.Result, result) 803 } 804 return nil 805 } 806 807 // StreamResponse reads the response as it comes back from the server 808 func (c *Client) StreamResponse(req *http.Request, stream string, cb func(*json.Decoder) error) (err error) { 809 810 reqStr := util.HTTPRequestToString(req) 811 log.Debugf("Sending request\n%s", reqStr) 812 813 err = c.Init() 814 if err != nil { 815 return err 816 } 817 818 resp, err := c.httpClient.Do(req) 819 if err != nil { 820 return errors.Wrapf(err, "%s failure of request: %s", req.Method, reqStr) 821 } 822 defer resp.Body.Close() 823 824 dec := json.NewDecoder(resp.Body) 825 results, err := streamer.StreamJSONArray(dec, stream, cb) 826 if err != nil { 827 return err 828 } 829 if !results { 830 fmt.Println("No results returned") 831 } 832 return nil 833 } 834 835 func (c *Client) getURL(endpoint string) (string, error) { 836 nurl, err := NormalizeURL(c.Config.URL) 837 if err != nil { 838 return "", err 839 } 840 rtn := fmt.Sprintf("%s/%s", nurl, endpoint) 841 return rtn, nil 842 } 843 844 // CheckEnrollment returns an error if this client is not enrolled 845 func (c *Client) CheckEnrollment() error { 846 err := c.Init() 847 if err != nil { 848 return err 849 } 850 var x509Enrollment, idemixEnrollment bool 851 err = c.checkX509Enrollment() 852 if err == nil { 853 x509Enrollment = true 854 } 855 err = c.checkIdemixEnrollment() 856 if err == nil { 857 idemixEnrollment = true 858 } 859 if x509Enrollment || idemixEnrollment { 860 return nil 861 } 862 log.Errorf("Enrollment check failed: %s", err.Error()) 863 return errors.New("Enrollment information does not exist. Please execute enroll command first. Example: fabric-ca-client enroll -u http://user:userpw@serverAddr:serverPort") 864 } 865 866 func (c *Client) checkX509Enrollment() error { 867 keyFileExists := util.FileExists(c.keyFile) 868 certFileExists := util.FileExists(c.certFile) 869 if keyFileExists && certFileExists { 870 return nil 871 } 872 // If key file does not exist, but certFile does, key file is probably 873 // stored by bccsp, so check to see if this is the case 874 if certFileExists { 875 _, _, _, err := util.GetSignerFromCertFile(c.certFile, c.csp) 876 if err == nil { 877 // Yes, the key is stored by BCCSP 878 return nil 879 } 880 } 881 return errors.New("X509 enrollment information does not exist") 882 } 883 884 // checkIdemixEnrollment returns an error if CA's Idemix public key and user's 885 // Idemix credential does not exist and if they exist and credential verification 886 // fails. Returns nil if the credential verification suucceeds 887 func (c *Client) checkIdemixEnrollment() error { 888 log.Debugf("CheckIdemixEnrollment - ipkFile: %s, idemixCredFrile: %s", c.ipkFile, c.idemixCredFile) 889 890 idemixIssuerPubKeyExists := util.FileExists(c.ipkFile) 891 idemixCredExists := util.FileExists(c.idemixCredFile) 892 if idemixIssuerPubKeyExists && idemixCredExists { 893 err := c.verifyIdemixCredential() 894 if err != nil { 895 return errors.WithMessage(err, "Idemix enrollment check failed") 896 } 897 return nil 898 } 899 return errors.New("Idemix enrollment information does not exist") 900 } 901 902 func (c *Client) verifyIdemixCredential() error { 903 ipk, err := c.getIssuerPubKey(nil) 904 if err != nil { 905 return err 906 } 907 credfileBytes, err := util.ReadFile(c.idemixCredFile) 908 if err != nil { 909 return errors.Wrapf(err, "Failed to read %s", c.idemixCredFile) 910 } 911 signerConfig := &idemixcred.SignerConfig{} 912 err = json.Unmarshal(credfileBytes, signerConfig) 913 if err != nil { 914 return errors.Wrapf(err, "Failed to unmarshal signer config from %s", c.idemixCredFile) 915 } 916 917 cred := new(idemix.Credential) 918 err = proto.Unmarshal(signerConfig.GetCred(), cred) 919 if err != nil { 920 return errors.Wrap(err, "Failed to unmarshal Idemix credential from signer config") 921 } 922 sk := fp256bn.FromBytes(signerConfig.GetSk()) 923 924 // Verify that the credential is cryptographically valid 925 err = cred.Ver(sk, ipk) 926 if err != nil { 927 return errors.Wrap(err, "Idemix credential is not cryptographically valid") 928 } 929 return nil 930 } 931 932 func newCfsslBasicKeyRequest(bkr *api.BasicKeyRequest) *csr.BasicKeyRequest { 933 return &csr.BasicKeyRequest{A: bkr.Algo, S: bkr.Size} 934 } 935 936 // NormalizeURL normalizes a URL (from cfssl) 937 func NormalizeURL(addr string) (*url.URL, error) { 938 addr = strings.TrimSpace(addr) 939 u, err := url.Parse(addr) 940 if err != nil { 941 return nil, err 942 } 943 if u.Opaque != "" { 944 u.Host = net.JoinHostPort(u.Scheme, u.Opaque) 945 u.Opaque = "" 946 } else if u.Path != "" && !strings.Contains(u.Path, ":") { 947 u.Host = net.JoinHostPort(u.Path, util.GetServerPort()) 948 u.Path = "" 949 } else if u.Scheme == "" { 950 u.Host = u.Path 951 u.Path = "" 952 } 953 if u.Scheme != "https" { 954 u.Scheme = "http" 955 } 956 _, port, err := net.SplitHostPort(u.Host) 957 if err != nil { 958 _, port, err = net.SplitHostPort(u.Host + ":" + util.GetServerPort()) 959 if err != nil { 960 return nil, err 961 } 962 } 963 if port != "" { 964 _, err = strconv.Atoi(port) 965 if err != nil { 966 return nil, err 967 } 968 } 969 return u, nil 970 }