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