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