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