github.com/canhui/fabric_ca2_2@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  }