github.com/49746628/fabric-ca-gm@v2.0.0-alpha.0.20200822143404-8a07eefa7452+incompatible/lib/client.go (about)

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