github.com/dddengyunjie/fabric-ca@v0.0.0-20190606043049-92df60ae2f0f/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 } 210 211 key, cspSigner, err := util.BCCSPKeyRequestGenerate(cr, c.csp) 212 if err != nil { 213 log.Debugf("failed generating BCCSP key: %s", err) 214 return nil, nil, err 215 } 216 217 var csrPEM []byte 218 if IsGMConfig() { 219 csrPEM, err = generate(cspSigner, cr, key) 220 221 } else { 222 csrPEM, err = csr.Generate(cspSigner, cr) 223 } 224 225 if err != nil { 226 log.Debugf("failed generating CSR: %s", err) 227 return nil, nil, err 228 } 229 230 return csrPEM, key, nil 231 } 232 233 // Enroll enrolls a new identity 234 // @param req The enrollment request 235 func (c *Client) Enroll(req *api.EnrollmentRequest) (*EnrollmentResponse, error) { 236 log.Debugf("Enrolling %+v", req) 237 238 err := c.Init() 239 if err != nil { 240 return nil, err 241 } 242 243 if strings.ToLower(req.Type) == "idemix" { 244 return c.handleIdemixEnroll(req) 245 } 246 return c.handleX509Enroll(req) 247 } 248 249 // Convert from network to local CA information 250 func (c *Client) net2LocalCAInfo(net *common.CAInfoResponseNet, local *GetCAInfoResponse) error { 251 caChain, err := util.B64Decode(net.CAChain) 252 if err != nil { 253 return errors.WithMessage(err, "Failed to decode CA chain") 254 } 255 if net.IssuerPublicKey != "" { 256 ipk, err := util.B64Decode(net.IssuerPublicKey) 257 if err != nil { 258 return errors.WithMessage(err, "Failed to decode issuer public key") 259 } 260 local.IssuerPublicKey = ipk 261 } 262 if net.IssuerRevocationPublicKey != "" { 263 rpk, err := util.B64Decode(net.IssuerRevocationPublicKey) 264 if err != nil { 265 return errors.WithMessage(err, "Failed to decode issuer revocation key") 266 } 267 local.IssuerRevocationPublicKey = rpk 268 } 269 local.CAName = net.CAName 270 local.CAChain = caChain 271 local.Version = net.Version 272 return nil 273 } 274 275 func (c *Client) handleX509Enroll(req *api.EnrollmentRequest) (*EnrollmentResponse, error) { 276 // Generate the CSR 277 csrPEM, key, err := c.GenCSR(req.CSR, req.Name) 278 if err != nil { 279 return nil, errors.WithMessage(err, "Failure generating CSR") 280 } 281 282 reqNet := &api.EnrollmentRequestNet{ 283 CAName: req.CAName, 284 AttrReqs: req.AttrReqs, 285 } 286 287 if req.CSR != nil { 288 reqNet.SignRequest.Hosts = req.CSR.Hosts 289 } 290 reqNet.SignRequest.Request = string(csrPEM) 291 reqNet.SignRequest.Profile = req.Profile 292 reqNet.SignRequest.Label = req.Label 293 294 body, err := util.Marshal(reqNet, "SignRequest") 295 if err != nil { 296 return nil, err 297 } 298 299 // Send the CSR to the fabric-ca server with basic auth header 300 post, err := c.newPost("enroll", body) 301 if err != nil { 302 return nil, err 303 } 304 post.SetBasicAuth(req.Name, req.Secret) 305 var result common.EnrollmentResponseNet 306 err = c.SendReq(post, &result) 307 if err != nil { 308 return nil, err 309 } 310 311 // Create the enrollment response 312 return c.newEnrollmentResponse(&result, req.Name, key) 313 } 314 315 // Handles enrollment request for an Idemix credential 316 // 1. Sends a request with empty body to the /api/v1/idemix/credentail REST endpoint 317 // of the server to get a Nonce from the CA 318 // 2. Constructs a credential request using the nonce, CA's idemix public key 319 // 3. Sends a request with the CredentialRequest object in the body to the 320 // /api/v1/idemix/credentail REST endpoint to get a credential 321 func (c *Client) handleIdemixEnroll(req *api.EnrollmentRequest) (*EnrollmentResponse, error) { 322 log.Debugf("Getting nonce from CA %s", req.CAName) 323 reqNet := &api.IdemixEnrollmentRequestNet{ 324 CAName: req.CAName, 325 } 326 var identity *Identity 327 328 // Get nonce from the CA 329 body, err := util.Marshal(reqNet, "NonceRequest") 330 if err != nil { 331 return nil, errors.WithMessage(err, "Failed to marshal nonce request") 332 } 333 post, err := c.newPost("idemix/credential", body) 334 if err != nil { 335 return nil, errors.WithMessage(err, "Failed to create HTTP request for getting a nonce") 336 } 337 err = c.addAuthHeaderForIdemixEnroll(req, identity, body, post) 338 if err != nil { 339 return nil, errors.WithMessage(err, 340 "Either username/password or X509 enrollment certificate is required to request an Idemix credential") 341 } 342 343 // Send the request and process the response 344 var result common.IdemixEnrollmentResponseNet 345 err = c.SendReq(post, &result) 346 if err != nil { 347 return nil, err 348 } 349 nonceBytes, err := util.B64Decode(result.Nonce) 350 351 if err != nil { 352 return nil, errors.WithMessage(err, 353 fmt.Sprintf("Failed to decode nonce that was returned by CA %s", req.CAName)) 354 } 355 nonce := fp256bn.FromBytes(nonceBytes) 356 log.Infof("Successfully got nonce from CA %s", req.CAName) 357 358 ipkBytes := []byte{} 359 ipkBytes, err = util.B64Decode(result.CAInfo.IssuerPublicKey) 360 if err != nil { 361 return nil, errors.WithMessage(err, fmt.Sprintf("Failed to decode issuer public key that was returned by CA %s", req.CAName)) 362 } 363 // Create credential request 364 credReq, sk, err := c.newIdemixCredentialRequest(nonce, ipkBytes) 365 if err != nil { 366 return nil, errors.WithMessage(err, "Failed to create an Idemix credential request") 367 } 368 reqNet.CredRequest = credReq 369 log.Info("Successfully created an Idemix credential request") 370 371 body, err = util.Marshal(reqNet, "CredentialRequest") 372 if err != nil { 373 return nil, errors.WithMessage(err, "Failed to marshal Idemix credential request") 374 } 375 376 // Send the cred request to the CA 377 post, err = c.newPost("idemix/credential", body) 378 if err != nil { 379 return nil, errors.WithMessage(err, "Failed to create HTTP request for getting Idemix credential") 380 } 381 err = c.addAuthHeaderForIdemixEnroll(req, identity, body, post) 382 if err != nil { 383 return nil, errors.WithMessage(err, 384 "Either username/password or X509 enrollment certificate is required to request idemix credential") 385 } 386 err = c.SendReq(post, &result) 387 if err != nil { 388 return nil, err 389 } 390 log.Infof("Successfully received Idemix credential from CA %s", req.CAName) 391 return c.newIdemixEnrollmentResponse(identity, &result, sk, req.Name) 392 } 393 394 // addAuthHeaderForIdemixEnroll adds authenticate header to the specified HTTP request 395 // It adds basic authentication header if userName and password are specified in the 396 // specified EnrollmentRequest object. Else, checks if a X509 credential in the client's 397 // MSP directory, if so, loads the identity, creates an oauth token based on the loaded 398 // identity's X509 credential, and adds the token to the HTTP request. The loaded 399 // identity is passed back to the caller. 400 func (c *Client) addAuthHeaderForIdemixEnroll(req *api.EnrollmentRequest, id *Identity, 401 body []byte, post *http.Request) error { 402 if req.Name != "" && req.Secret != "" { 403 post.SetBasicAuth(req.Name, req.Secret) 404 return nil 405 } 406 if id == nil { 407 err := c.checkX509Enrollment() 408 if err != nil { 409 return err 410 } 411 id, err = c.LoadMyIdentity() 412 if err != nil { 413 return err 414 } 415 } 416 err := id.addTokenAuthHdr(post, body) 417 if err != nil { 418 return err 419 } 420 return nil 421 } 422 423 // newEnrollmentResponse creates a client enrollment response from a network response 424 // @param result The result from server 425 // @param id Name of identity being enrolled or reenrolled 426 // @param key The private key which was used to sign the request 427 func (c *Client) newEnrollmentResponse(result *common.EnrollmentResponseNet, id string, key bccsp.Key) (*EnrollmentResponse, error) { 428 log.Debugf("newEnrollmentResponse %s", id) 429 certByte, err := util.B64Decode(result.Cert) 430 if err != nil { 431 return nil, errors.WithMessage(err, "Invalid response format from server") 432 } 433 signer, err := x509cred.NewSigner(key, certByte) 434 if err != nil { 435 return nil, err 436 } 437 x509Cred := x509cred.NewCredential(c.certFile, c.keyFile, c) 438 err = x509Cred.SetVal(signer) 439 if err != nil { 440 return nil, err 441 } 442 resp := &EnrollmentResponse{ 443 Identity: NewIdentity(c, id, []credential.Credential{x509Cred}), 444 } 445 err = c.net2LocalCAInfo(&result.ServerInfo, &resp.CAInfo) 446 if err != nil { 447 return nil, err 448 } 449 return resp, nil 450 } 451 452 // newIdemixEnrollmentResponse creates a client idemix enrollment response from a network response 453 func (c *Client) newIdemixEnrollmentResponse(identity *Identity, result *common.IdemixEnrollmentResponseNet, 454 sk *fp256bn.BIG, id string) (*EnrollmentResponse, error) { 455 log.Debugf("newIdemixEnrollmentResponse %s", id) 456 credBytes, err := util.B64Decode(result.Credential) 457 if err != nil { 458 return nil, errors.WithMessage(err, "Invalid response format from server") 459 } 460 461 criBytes, err := util.B64Decode(result.CRI) 462 if err != nil { 463 return nil, errors.WithMessage(err, "Invalid response format from server") 464 } 465 466 // Create SignerConfig object with credential bytes from the response 467 // and secret key 468 role, _ := result.Attrs["Role"].(int) 469 ou, _ := result.Attrs["OU"].(string) 470 enrollmentID, _ := result.Attrs["EnrollmentID"].(string) 471 signerConfig := &idemixcred.SignerConfig{ 472 Cred: credBytes, 473 Sk: idemix.BigToBytes(sk), 474 Role: role, 475 OrganizationalUnitIdentifier: ou, 476 EnrollmentID: enrollmentID, 477 CredentialRevocationInformation: criBytes, 478 } 479 480 // Create IdemixCredential object 481 cred := idemixcred.NewCredential(c.idemixCredFile, c) 482 err = cred.SetVal(signerConfig) 483 if err != nil { 484 return nil, err 485 } 486 if identity == nil { 487 identity = NewIdentity(c, id, []credential.Credential{cred}) 488 } else { 489 identity.creds = append(identity.creds, cred) 490 } 491 492 resp := &EnrollmentResponse{ 493 Identity: identity, 494 } 495 err = c.net2LocalCAInfo(&result.CAInfo, &resp.CAInfo) 496 if err != nil { 497 return nil, err 498 } 499 log.Infof("Successfully processed response from the CA") 500 return resp, nil 501 } 502 503 // newCertificateRequest creates a certificate request which is used to generate 504 // a CSR (Certificate Signing Request) 505 func (c *Client) newCertificateRequest(req *api.CSRInfo) *csr.CertificateRequest { 506 cr := csr.CertificateRequest{} 507 if req != nil && req.Names != nil { 508 cr.Names = req.Names 509 } 510 if req != nil && req.Hosts != nil { 511 cr.Hosts = req.Hosts 512 } else { 513 // Default requested hosts are local hostname 514 hostname, _ := os.Hostname() 515 if hostname != "" { 516 cr.Hosts = make([]string, 1) 517 cr.Hosts[0] = hostname 518 } 519 } 520 if req != nil && req.KeyRequest != nil { 521 cr.KeyRequest = newCfsslBasicKeyRequest(req.KeyRequest) 522 } 523 if req != nil { 524 cr.CA = req.CA 525 cr.SerialNumber = req.SerialNumber 526 } 527 return &cr 528 } 529 530 // newIdemixCredentialRequest returns CredentialRequest object, a secret key, and a random number used in 531 // the creation of credential request. 532 func (c *Client) newIdemixCredentialRequest(nonce *fp256bn.BIG, ipkBytes []byte) (*idemix.CredRequest, *fp256bn.BIG, error) { 533 rng, err := idemix.GetRand() 534 if err != nil { 535 return nil, nil, err 536 } 537 sk := idemix.RandModOrder(rng) 538 539 issuerPubKey, err := c.getIssuerPubKey(ipkBytes) 540 if err != nil { 541 return nil, nil, err 542 } 543 return idemix.NewCredRequest(sk, idemix.BigToBytes(nonce), issuerPubKey, rng), sk, nil 544 } 545 546 func (c *Client) getIssuerPubKey(ipkBytes []byte) (*idemix.IssuerPublicKey, error) { 547 var err error 548 if ipkBytes == nil || len(ipkBytes) == 0 { 549 ipkBytes, err = ioutil.ReadFile(c.ipkFile) 550 if err != nil { 551 return nil, errors.Wrapf(err, "Error reading CA's Idemix public key at '%s'", c.ipkFile) 552 } 553 } 554 pubKey := &idemix.IssuerPublicKey{} 555 err = proto.Unmarshal(ipkBytes, pubKey) 556 if err != nil { 557 return nil, err 558 } 559 c.issuerPublicKey = pubKey 560 return c.issuerPublicKey, nil 561 } 562 563 // LoadMyIdentity loads the client's identity from disk 564 func (c *Client) LoadMyIdentity() (*Identity, error) { 565 err := c.Init() 566 if err != nil { 567 return nil, err 568 } 569 return c.LoadIdentity(c.keyFile, c.certFile, c.idemixCredFile) 570 } 571 572 // LoadIdentity loads an identity from disk 573 func (c *Client) LoadIdentity(keyFile, certFile, idemixCredFile string) (*Identity, error) { 574 log.Debugf("Loading identity: keyFile=%s, certFile=%s", keyFile, certFile) 575 err := c.Init() 576 if err != nil { 577 return nil, err 578 } 579 580 var creds []credential.Credential 581 var x509Found, idemixFound bool 582 x509Cred := x509cred.NewCredential(certFile, keyFile, c) 583 err = x509Cred.Load() 584 if err == nil { 585 x509Found = true 586 creds = append(creds, x509Cred) 587 } else { 588 log.Debugf("No X509 credential found at %s, %s", keyFile, certFile) 589 } 590 591 idemixCred := idemixcred.NewCredential(idemixCredFile, c) 592 err = idemixCred.Load() 593 if err == nil { 594 idemixFound = true 595 creds = append(creds, idemixCred) 596 } else { 597 log.Debugf("No Idemix credential found at %s", idemixCredFile) 598 } 599 600 if !x509Found && !idemixFound { 601 return nil, errors.New("Identity does not posses any enrollment credentials") 602 } 603 604 return c.NewIdentity(creds) 605 } 606 607 // NewIdentity creates a new identity 608 func (c *Client) NewIdentity(creds []credential.Credential) (*Identity, error) { 609 if len(creds) == 0 { 610 return nil, errors.New("No credentials spcified. Atleast one credential must be specified") 611 } 612 name, err := creds[0].EnrollmentID() 613 if err != nil { 614 return nil, err 615 } 616 if len(creds) == 1 { 617 return NewIdentity(c, name, creds), nil 618 } 619 620 //TODO: Get the enrollment ID from the creds...they all should return same value 621 // for i := 1; i < len(creds); i++ { 622 // localid, err := creds[i].EnrollmentID() 623 // if err != nil { 624 // return nil, err 625 // } 626 // if localid != name { 627 // return nil, errors.New("Specified credentials belong to different identities, they should be long to same identity") 628 // } 629 // } 630 return NewIdentity(c, name, creds), nil 631 } 632 633 // NewX509Identity creates a new identity 634 func (c *Client) NewX509Identity(name string, creds []credential.Credential) x509cred.Identity { 635 return NewIdentity(c, name, creds) 636 } 637 638 // LoadCSRInfo reads CSR (Certificate Signing Request) from a file 639 // @parameter path The path to the file contains CSR info in JSON format 640 func (c *Client) LoadCSRInfo(path string) (*api.CSRInfo, error) { 641 csrJSON, err := ioutil.ReadFile(path) 642 if err != nil { 643 return nil, err 644 } 645 var csrInfo api.CSRInfo 646 err = util.Unmarshal(csrJSON, &csrInfo, "LoadCSRInfo") 647 if err != nil { 648 return nil, err 649 } 650 return &csrInfo, nil 651 } 652 653 // GetCertFilePath returns the path to the certificate file for this client 654 func (c *Client) GetCertFilePath() string { 655 return c.certFile 656 } 657 658 // GetCSP returns BCCSP instance associated with this client 659 func (c *Client) GetCSP() bccsp.BCCSP { 660 return c.csp 661 } 662 663 // GetIssuerPubKey returns issuer public key associated with this client 664 func (c *Client) GetIssuerPubKey() (*idemix.IssuerPublicKey, error) { 665 if c.issuerPublicKey == nil { 666 return c.getIssuerPubKey(nil) 667 } 668 return c.issuerPublicKey, nil 669 } 670 671 // newGet create a new GET request 672 func (c *Client) newGet(endpoint string) (*http.Request, error) { 673 curl, err := c.getURL(endpoint) 674 if err != nil { 675 return nil, err 676 } 677 req, err := http.NewRequest("GET", curl, bytes.NewReader([]byte{})) 678 if err != nil { 679 return nil, errors.Wrapf(err, "Failed creating GET request for %s", curl) 680 } 681 return req, nil 682 } 683 684 // newPut create a new PUT request 685 func (c *Client) newPut(endpoint string, reqBody []byte) (*http.Request, error) { 686 curl, err := c.getURL(endpoint) 687 if err != nil { 688 return nil, err 689 } 690 req, err := http.NewRequest("PUT", curl, bytes.NewReader(reqBody)) 691 if err != nil { 692 return nil, errors.Wrapf(err, "Failed creating PUT request for %s", curl) 693 } 694 return req, nil 695 } 696 697 // newDelete create a new DELETE request 698 func (c *Client) newDelete(endpoint string) (*http.Request, error) { 699 curl, err := c.getURL(endpoint) 700 if err != nil { 701 return nil, err 702 } 703 req, err := http.NewRequest("DELETE", curl, bytes.NewReader([]byte{})) 704 if err != nil { 705 return nil, errors.Wrapf(err, "Failed creating DELETE request for %s", curl) 706 } 707 return req, nil 708 } 709 710 // NewPost create a new post request 711 func (c *Client) newPost(endpoint string, reqBody []byte) (*http.Request, error) { 712 curl, err := c.getURL(endpoint) 713 if err != nil { 714 return nil, err 715 } 716 req, err := http.NewRequest("POST", curl, bytes.NewReader(reqBody)) 717 if err != nil { 718 return nil, errors.Wrapf(err, "Failed posting to %s", curl) 719 } 720 return req, nil 721 } 722 723 // SendReq sends a request to the fabric-ca-server and fills in the result 724 func (c *Client) SendReq(req *http.Request, result interface{}) (err error) { 725 726 reqStr := util.HTTPRequestToString(req) 727 log.Debugf("Sending request\n%s", reqStr) 728 729 err = c.Init() 730 if err != nil { 731 return err 732 } 733 734 resp, err := c.httpClient.Do(req) 735 if err != nil { 736 return errors.Wrapf(err, "%s failure of request: %s", req.Method, reqStr) 737 } 738 var respBody []byte 739 if resp.Body != nil { 740 respBody, err = ioutil.ReadAll(resp.Body) 741 defer func() { 742 err := resp.Body.Close() 743 if err != nil { 744 log.Debugf("Failed to close the response body: %s", err.Error()) 745 } 746 }() 747 if err != nil { 748 return errors.Wrapf(err, "Failed to read response of request: %s", reqStr) 749 } 750 log.Debugf("Received response\n%s", util.HTTPResponseToString(resp)) 751 } 752 var body *cfsslapi.Response 753 if respBody != nil && len(respBody) > 0 { 754 body = new(cfsslapi.Response) 755 err = json.Unmarshal(respBody, body) 756 if err != nil { 757 return errors.Wrapf(err, "Failed to parse response: %s", respBody) 758 } 759 if len(body.Errors) > 0 { 760 var errorMsg string 761 for _, err := range body.Errors { 762 msg := fmt.Sprintf("Response from server: Error Code: %d - %s\n", err.Code, err.Message) 763 if errorMsg == "" { 764 errorMsg = msg 765 } else { 766 errorMsg = errorMsg + fmt.Sprintf("\n%s", msg) 767 } 768 } 769 return errors.Errorf(errorMsg) 770 } 771 } 772 scode := resp.StatusCode 773 if scode >= 400 { 774 return errors.Errorf("Failed with server status code %d for request:\n%s", scode, reqStr) 775 } 776 if body == nil { 777 return errors.Errorf("Empty response body:\n%s", reqStr) 778 } 779 if !body.Success { 780 return errors.Errorf("Server returned failure for request:\n%s", reqStr) 781 } 782 log.Debugf("Response body result: %+v", body.Result) 783 if result != nil { 784 return mapstructure.Decode(body.Result, result) 785 } 786 return nil 787 } 788 789 // StreamResponse reads the response as it comes back from the server 790 func (c *Client) StreamResponse(req *http.Request, stream string, cb func(*json.Decoder) error) (err error) { 791 792 reqStr := util.HTTPRequestToString(req) 793 log.Debugf("Sending request\n%s", reqStr) 794 795 err = c.Init() 796 if err != nil { 797 return err 798 } 799 800 resp, err := c.httpClient.Do(req) 801 if err != nil { 802 return errors.Wrapf(err, "%s failure of request: %s", req.Method, reqStr) 803 } 804 defer resp.Body.Close() 805 806 dec := json.NewDecoder(resp.Body) 807 results, err := streamer.StreamJSONArray(dec, stream, cb) 808 if err != nil { 809 return err 810 } 811 if !results { 812 fmt.Println("No results returned") 813 } 814 return nil 815 } 816 817 func (c *Client) getURL(endpoint string) (string, error) { 818 nurl, err := NormalizeURL(c.Config.URL) 819 if err != nil { 820 return "", err 821 } 822 rtn := fmt.Sprintf("%s/%s", nurl, endpoint) 823 return rtn, nil 824 } 825 826 // CheckEnrollment returns an error if this client is not enrolled 827 func (c *Client) CheckEnrollment() error { 828 err := c.Init() 829 if err != nil { 830 return err 831 } 832 var x509Enrollment, idemixEnrollment bool 833 err = c.checkX509Enrollment() 834 if err == nil { 835 x509Enrollment = true 836 } 837 err = c.checkIdemixEnrollment() 838 if err == nil { 839 idemixEnrollment = true 840 } 841 if x509Enrollment || idemixEnrollment { 842 return nil 843 } 844 log.Errorf("Enrollment check failed: %s", err.Error()) 845 return errors.New("Enrollment information does not exist. Please execute enroll command first. Example: fabric-ca-client enroll -u http://user:userpw@serverAddr:serverPort") 846 } 847 848 func (c *Client) checkX509Enrollment() error { 849 keyFileExists := util.FileExists(c.keyFile) 850 certFileExists := util.FileExists(c.certFile) 851 if keyFileExists && certFileExists { 852 return nil 853 } 854 // If key file does not exist, but certFile does, key file is probably 855 // stored by bccsp, so check to see if this is the case 856 if certFileExists { 857 _, _, _, err := util.GetSignerFromCertFile(c.certFile, c.csp) 858 if err == nil { 859 // Yes, the key is stored by BCCSP 860 return nil 861 } 862 } 863 return errors.New("X509 enrollment information does not exist") 864 } 865 866 // checkIdemixEnrollment returns an error if CA's Idemix public key and user's 867 // Idemix credential does not exist and if they exist and credential verification 868 // fails. Returns nil if the credential verification suucceeds 869 func (c *Client) checkIdemixEnrollment() error { 870 log.Debugf("CheckIdemixEnrollment - ipkFile: %s, idemixCredFrile: %s", c.ipkFile, c.idemixCredFile) 871 872 idemixIssuerPubKeyExists := util.FileExists(c.ipkFile) 873 idemixCredExists := util.FileExists(c.idemixCredFile) 874 if idemixIssuerPubKeyExists && idemixCredExists { 875 err := c.verifyIdemixCredential() 876 if err != nil { 877 return errors.WithMessage(err, "Idemix enrollment check failed") 878 } 879 return nil 880 } 881 return errors.New("Idemix enrollment information does not exist") 882 } 883 884 func (c *Client) verifyIdemixCredential() error { 885 ipk, err := c.getIssuerPubKey(nil) 886 if err != nil { 887 return err 888 } 889 credfileBytes, err := util.ReadFile(c.idemixCredFile) 890 if err != nil { 891 return errors.Wrapf(err, "Failed to read %s", c.idemixCredFile) 892 } 893 signerConfig := &idemixcred.SignerConfig{} 894 err = json.Unmarshal(credfileBytes, signerConfig) 895 if err != nil { 896 return errors.Wrapf(err, "Failed to unmarshal signer config from %s", c.idemixCredFile) 897 } 898 899 cred := new(idemix.Credential) 900 err = proto.Unmarshal(signerConfig.GetCred(), cred) 901 if err != nil { 902 return errors.Wrap(err, "Failed to unmarshal Idemix credential from signer config") 903 } 904 sk := fp256bn.FromBytes(signerConfig.GetSk()) 905 906 // Verify that the credential is cryptographically valid 907 err = cred.Ver(sk, ipk) 908 if err != nil { 909 return errors.Wrap(err, "Idemix credential is not cryptographically valid") 910 } 911 return nil 912 } 913 914 func newCfsslBasicKeyRequest(bkr *api.BasicKeyRequest) *csr.BasicKeyRequest { 915 return &csr.BasicKeyRequest{A: bkr.Algo, S: bkr.Size} 916 } 917 918 // NormalizeURL normalizes a URL (from cfssl) 919 func NormalizeURL(addr string) (*url.URL, error) { 920 addr = strings.TrimSpace(addr) 921 u, err := url.Parse(addr) 922 if err != nil { 923 return nil, err 924 } 925 if u.Opaque != "" { 926 u.Host = net.JoinHostPort(u.Scheme, u.Opaque) 927 u.Opaque = "" 928 } else if u.Path != "" && !strings.Contains(u.Path, ":") { 929 u.Host = net.JoinHostPort(u.Path, util.GetServerPort()) 930 u.Path = "" 931 } else if u.Scheme == "" { 932 u.Host = u.Path 933 u.Path = "" 934 } 935 if u.Scheme != "https" { 936 u.Scheme = "http" 937 } 938 _, port, err := net.SplitHostPort(u.Host) 939 if err != nil { 940 _, port, err = net.SplitHostPort(u.Host + ":" + util.GetServerPort()) 941 if err != nil { 942 return nil, err 943 } 944 } 945 if port != "" { 946 _, err = strconv.Atoi(port) 947 if err != nil { 948 return nil, err 949 } 950 } 951 return u, nil 952 }