github.com/simranvc/fabric-ca@v0.0.0-20191030094829-acc364294dde/util/util.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"crypto/rsa"
    23  	"crypto/x509"
    24  	"encoding/base64"
    25  	"encoding/json"
    26  	"encoding/pem"
    27  	"fmt"
    28  	"io"
    29  	"io/ioutil"
    30  	"math/big"
    31  	mrand "math/rand"
    32  	"net/http"
    33  	"net/url"
    34  	"os"
    35  	"path"
    36  	"path/filepath"
    37  	"reflect"
    38  	"regexp"
    39  	"strings"
    40  	"testing"
    41  	"time"
    42  
    43  	"github.com/cloudflare/cfssl/log"
    44  	"github.com/hyperledger/fabric/bccsp"
    45  	"github.com/pkg/errors"
    46  	"github.com/spf13/viper"
    47  	"github.com/stretchr/testify/assert"
    48  	"golang.org/x/crypto/ocsp"
    49  )
    50  
    51  var (
    52  	rnd = mrand.NewSource(time.Now().UnixNano())
    53  	// ErrNotImplemented used to return errors for functions not implemented
    54  	ErrNotImplemented = errors.New("NOT YET IMPLEMENTED")
    55  )
    56  
    57  const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    58  const (
    59  	letterIdxBits = 6                    // 6 bits to represent a letter index
    60  	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    61  	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
    62  )
    63  
    64  // RevocationReasonCodes is a map between string reason codes to integers as defined in RFC 5280
    65  var RevocationReasonCodes = map[string]int{
    66  	"unspecified":          ocsp.Unspecified,
    67  	"keycompromise":        ocsp.KeyCompromise,
    68  	"cacompromise":         ocsp.CACompromise,
    69  	"affiliationchanged":   ocsp.AffiliationChanged,
    70  	"superseded":           ocsp.Superseded,
    71  	"cessationofoperation": ocsp.CessationOfOperation,
    72  	"certificatehold":      ocsp.CertificateHold,
    73  	"removefromcrl":        ocsp.RemoveFromCRL,
    74  	"privilegewithdrawn":   ocsp.PrivilegeWithdrawn,
    75  	"aacompromise":         ocsp.AACompromise,
    76  }
    77  
    78  // SecretTag to tag a field as secret as in password, token
    79  const SecretTag = "mask"
    80  
    81  // URLRegex is the regular expression to check if a value is an URL
    82  var URLRegex = regexp.MustCompile("(ldap|http)s*://(\\S+):(\\S+)@")
    83  
    84  //ECDSASignature forms the structure for R and S value for ECDSA
    85  type ECDSASignature struct {
    86  	R, S *big.Int
    87  }
    88  
    89  // RandomString returns a random string
    90  func RandomString(n int) string {
    91  	b := make([]byte, n)
    92  
    93  	for i, cache, remain := n-1, rnd.Int63(), letterIdxMax; i >= 0; {
    94  		if remain == 0 {
    95  			cache, remain = rnd.Int63(), letterIdxMax
    96  		}
    97  		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
    98  			b[i] = letterBytes[idx]
    99  			i--
   100  		}
   101  		cache >>= letterIdxBits
   102  		remain--
   103  	}
   104  
   105  	return string(b)
   106  }
   107  
   108  // RemoveQuotes removes outer quotes from a string if necessary
   109  func RemoveQuotes(str string) string {
   110  	if str == "" {
   111  		return str
   112  	}
   113  	if (strings.HasPrefix(str, "'") && strings.HasSuffix(str, "'")) ||
   114  		(strings.HasPrefix(str, "\"") && strings.HasSuffix(str, "\"")) {
   115  		str = str[1 : len(str)-1]
   116  	}
   117  	return str
   118  }
   119  
   120  // ReadFile reads a file
   121  func ReadFile(file string) ([]byte, error) {
   122  	return ioutil.ReadFile(file)
   123  }
   124  
   125  // WriteFile writes a file
   126  func WriteFile(file string, buf []byte, perm os.FileMode) error {
   127  	dir := path.Dir(file)
   128  	// Create the directory if it doesn't exist
   129  	if _, err := os.Stat(dir); os.IsNotExist(err) {
   130  		err = os.MkdirAll(dir, 0755)
   131  		if err != nil {
   132  			return errors.Wrapf(err, "Failed to create directory '%s' for file '%s'", dir, file)
   133  		}
   134  	}
   135  	return ioutil.WriteFile(file, buf, perm)
   136  }
   137  
   138  // FileExists checks to see if a file exists
   139  func FileExists(name string) bool {
   140  	if _, err := os.Stat(name); err != nil {
   141  		if os.IsNotExist(err) {
   142  			return false
   143  		}
   144  	}
   145  	return true
   146  }
   147  
   148  // Marshal to bytes
   149  func Marshal(from interface{}, what string) ([]byte, error) {
   150  	buf, err := json.Marshal(from)
   151  	if err != nil {
   152  		return nil, errors.Wrapf(err, "Failed to marshal %s", what)
   153  	}
   154  	return buf, nil
   155  }
   156  
   157  // Unmarshal from bytes
   158  func Unmarshal(from []byte, to interface{}, what string) error {
   159  	err := json.Unmarshal(from, to)
   160  	if err != nil {
   161  		return errors.Wrapf(err, "Failed to unmarshal %s", what)
   162  	}
   163  	return nil
   164  }
   165  
   166  // CreateToken creates a JWT-like token.
   167  // In a normal JWT token, the format of the token created is:
   168  //      <algorithm,claims,signature>
   169  // where each part is base64-encoded string separated by a period.
   170  // In this JWT-like token, there are two differences:
   171  // 1) the claims section is a certificate, so the format is:
   172  //      <certificate,signature>
   173  // 2) the signature uses the private key associated with the certificate,
   174  //    and the signature is across both the certificate and the "body" argument,
   175  //    which is the body of an HTTP request, though could be any arbitrary bytes.
   176  // @param cert The pem-encoded certificate
   177  // @param key The pem-encoded key
   178  // @param method http method of the request
   179  // @param uri URI of the request
   180  // @param body The body of an HTTP request
   181  func CreateToken(csp bccsp.BCCSP, cert []byte, key bccsp.Key, method, uri string, body []byte) (string, error) {
   182  	x509Cert, err := GetX509CertificateFromPEM(cert)
   183  	if err != nil {
   184  		return "", err
   185  	}
   186  	publicKey := x509Cert.PublicKey
   187  
   188  	var token string
   189  
   190  	//The RSA Key Gen is commented right now as there is bccsp does
   191  	switch publicKey.(type) {
   192  	/*
   193  		case *rsa.PublicKey:
   194  			token, err = GenRSAToken(csp, cert, key, body)
   195  			if err != nil {
   196  				return "", err
   197  			}
   198  	*/
   199  	case *ecdsa.PublicKey:
   200  		token, err = GenECDSAToken(csp, cert, key, method, uri, body)
   201  		if err != nil {
   202  			return "", err
   203  		}
   204  	}
   205  	return token, nil
   206  }
   207  
   208  //GenRSAToken signs the http body and cert with RSA using RSA private key
   209  // @csp : BCCSP instance
   210  /*
   211  func GenRSAToken(csp bccsp.BCCSP, cert []byte, key []byte, body []byte) (string, error) {
   212  	privKey, err := GetRSAPrivateKey(key)
   213  	if err != nil {
   214  		return "", err
   215  	}
   216  	b64body := B64Encode(body)
   217  	b64cert := B64Encode(cert)
   218  	bodyAndcert := b64body + "." + b64cert
   219  	hash := sha512.New384()
   220  	hash.Write([]byte(bodyAndcert))
   221  	h := hash.Sum(nil)
   222  	RSAsignature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA384, h[:])
   223  	if err != nil {
   224  		return "", errors.Wrap(err, "Failed to rsa.SignPKCS1v15")
   225  	}
   226  	b64sig := B64Encode(RSAsignature)
   227  	token := b64cert + "." + b64sig
   228  
   229  	return  token, nil
   230  }
   231  */
   232  
   233  //GenECDSAToken signs the http body and cert with ECDSA using EC private key
   234  func GenECDSAToken(csp bccsp.BCCSP, cert []byte, key bccsp.Key, method, uri string, body []byte) (string, error) {
   235  	b64body := B64Encode(body)
   236  	b64cert := B64Encode(cert)
   237  	b64uri := B64Encode([]byte(uri))
   238  	payload := method + "." + b64uri + "." + b64body + "." + b64cert
   239  
   240  	return genECDSAToken(csp, key, b64cert, payload)
   241  }
   242  
   243  func genECDSAToken(csp bccsp.BCCSP, key bccsp.Key, b64cert, payload string) (string, error) {
   244  	digest, digestError := csp.Hash([]byte(payload), &bccsp.SHAOpts{})
   245  	if digestError != nil {
   246  		return "", errors.WithMessage(digestError, fmt.Sprintf("Hash failed on '%s'", payload))
   247  	}
   248  
   249  	ecSignature, err := csp.Sign(key, digest, nil)
   250  	if err != nil {
   251  		return "", errors.WithMessage(err, "BCCSP signature generation failure")
   252  	}
   253  	if len(ecSignature) == 0 {
   254  		return "", errors.New("BCCSP signature creation failed. Signature must be different than nil")
   255  	}
   256  
   257  	b64sig := B64Encode(ecSignature)
   258  	token := b64cert + "." + b64sig
   259  
   260  	return token, nil
   261  
   262  }
   263  
   264  // VerifyToken verifies token signed by either ECDSA or RSA and
   265  // returns the associated user ID
   266  func VerifyToken(csp bccsp.BCCSP, token string, method, uri string, body []byte, compMode1_3 bool) (*x509.Certificate, error) {
   267  
   268  	if csp == nil {
   269  		return nil, errors.New("BCCSP instance is not present")
   270  	}
   271  	x509Cert, b64Cert, b64Sig, err := DecodeToken(token)
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  	sig, err := B64Decode(b64Sig)
   276  	if err != nil {
   277  		return nil, errors.WithMessage(err, "Invalid base64 encoded signature in token")
   278  	}
   279  	b64Body := B64Encode(body)
   280  	b64uri := B64Encode([]byte(uri))
   281  	sigString := method + "." + b64uri + "." + b64Body + "." + b64Cert
   282  
   283  	pk2, err := csp.KeyImport(x509Cert, &bccsp.X509PublicKeyImportOpts{Temporary: true})
   284  	if err != nil {
   285  		return nil, errors.WithMessage(err, "Public Key import into BCCSP failed with error")
   286  	}
   287  	if pk2 == nil {
   288  		return nil, errors.New("Public Key Cannot be imported into BCCSP")
   289  	}
   290  
   291  	//bccsp.X509PublicKeyImportOpts
   292  	//Using default hash algo
   293  	digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{})
   294  	if digestError != nil {
   295  		return nil, errors.WithMessage(digestError, "Message digest failed")
   296  	}
   297  
   298  	valid, validErr := csp.Verify(pk2, sig, digest, nil)
   299  	if compMode1_3 && !valid {
   300  		log.Debugf("Failed to verify token based on new authentication header requirements: %s", err)
   301  		sigString := b64Body + "." + b64Cert
   302  		digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{})
   303  		if digestError != nil {
   304  			return nil, errors.WithMessage(digestError, "Message digest failed")
   305  		}
   306  		valid, validErr = csp.Verify(pk2, sig, digest, nil)
   307  	}
   308  
   309  	if validErr != nil {
   310  		return nil, errors.WithMessage(validErr, "Token signature validation failure")
   311  	}
   312  	if !valid {
   313  		return nil, errors.New("Token signature validation failed")
   314  	}
   315  
   316  	return x509Cert, nil
   317  }
   318  
   319  // DecodeToken extracts an X509 certificate and base64 encoded signature from a token
   320  func DecodeToken(token string) (*x509.Certificate, string, string, error) {
   321  	if token == "" {
   322  		return nil, "", "", errors.New("Invalid token; it is empty")
   323  	}
   324  	parts := strings.Split(token, ".")
   325  	if len(parts) != 2 {
   326  		return nil, "", "", errors.New("Invalid token format; expecting 2 parts separated by '.'")
   327  	}
   328  	b64cert := parts[0]
   329  	certDecoded, err := B64Decode(b64cert)
   330  	if err != nil {
   331  		return nil, "", "", errors.WithMessage(err, "Failed to decode base64 encoded x509 cert")
   332  	}
   333  	x509Cert, err := GetX509CertificateFromPEM(certDecoded)
   334  	if err != nil {
   335  		return nil, "", "", errors.WithMessage(err, "Error in parsing x509 certificate given block bytes")
   336  	}
   337  	return x509Cert, b64cert, parts[1], nil
   338  }
   339  
   340  //GetECPrivateKey get *ecdsa.PrivateKey from key pem
   341  func GetECPrivateKey(raw []byte) (*ecdsa.PrivateKey, error) {
   342  	decoded, _ := pem.Decode(raw)
   343  	if decoded == nil {
   344  		return nil, errors.New("Failed to decode the PEM-encoded ECDSA key")
   345  	}
   346  	ECprivKey, err := x509.ParseECPrivateKey(decoded.Bytes)
   347  	if err == nil {
   348  		return ECprivKey, nil
   349  	}
   350  	key, err2 := x509.ParsePKCS8PrivateKey(decoded.Bytes)
   351  	if err2 == nil {
   352  		switch key.(type) {
   353  		case *ecdsa.PrivateKey:
   354  			return key.(*ecdsa.PrivateKey), nil
   355  		case *rsa.PrivateKey:
   356  			return nil, errors.New("Expecting EC private key but found RSA private key")
   357  		default:
   358  			return nil, errors.New("Invalid private key type in PKCS#8 wrapping")
   359  		}
   360  	}
   361  	return nil, errors.Wrap(err2, "Failed parsing EC private key")
   362  }
   363  
   364  //GetRSAPrivateKey get *rsa.PrivateKey from key pem
   365  func GetRSAPrivateKey(raw []byte) (*rsa.PrivateKey, error) {
   366  	decoded, _ := pem.Decode(raw)
   367  	if decoded == nil {
   368  		return nil, errors.New("Failed to decode the PEM-encoded RSA key")
   369  	}
   370  	RSAprivKey, err := x509.ParsePKCS1PrivateKey(decoded.Bytes)
   371  	if err == nil {
   372  		return RSAprivKey, nil
   373  	}
   374  	key, err2 := x509.ParsePKCS8PrivateKey(decoded.Bytes)
   375  	if err2 == nil {
   376  		switch key.(type) {
   377  		case *ecdsa.PrivateKey:
   378  			return nil, errors.New("Expecting RSA private key but found EC private key")
   379  		case *rsa.PrivateKey:
   380  			return key.(*rsa.PrivateKey), nil
   381  		default:
   382  			return nil, errors.New("Invalid private key type in PKCS#8 wrapping")
   383  		}
   384  	}
   385  	return nil, errors.Wrap(err, "Failed parsing RSA private key")
   386  }
   387  
   388  // B64Encode base64 encodes bytes
   389  func B64Encode(buf []byte) string {
   390  	return base64.StdEncoding.EncodeToString(buf)
   391  }
   392  
   393  // B64Decode base64 decodes a string
   394  func B64Decode(str string) (buf []byte, err error) {
   395  	return base64.StdEncoding.DecodeString(str)
   396  }
   397  
   398  // StrContained returns true if 'str' is in 'strs'; otherwise return false
   399  func StrContained(str string, strs []string) bool {
   400  	for _, s := range strs {
   401  		if strings.ToLower(s) == strings.ToLower(str) {
   402  			return true
   403  		}
   404  	}
   405  	return false
   406  }
   407  
   408  // IsSubsetOf returns an error if there is something in 'small' that
   409  // is not in 'big'.  Both small and big are assumed to be comma-separated
   410  // strings.  All string comparisons are case-insensitive.
   411  // Examples:
   412  // 1) IsSubsetOf('a,B', 'A,B,C') returns nil
   413  // 2) IsSubsetOf('A,B,C', 'B,C') returns an error because A is not in the 2nd set.
   414  func IsSubsetOf(small, big string) error {
   415  	bigSet := strings.Split(big, ",")
   416  	smallSet := strings.Split(small, ",")
   417  	for _, s := range smallSet {
   418  		if s != "" && !StrContained(s, bigSet) {
   419  			return errors.Errorf("'%s' is not a member of '%s'", s, big)
   420  		}
   421  	}
   422  	return nil
   423  }
   424  
   425  // HTTPRequestToString returns a string for an HTTP request for debuggging
   426  func HTTPRequestToString(req *http.Request) string {
   427  	body, _ := ioutil.ReadAll(req.Body)
   428  	req.Body = ioutil.NopCloser(bytes.NewReader(body))
   429  	return fmt.Sprintf("%s %s\n%s",
   430  		req.Method, req.URL, string(body))
   431  }
   432  
   433  // HTTPResponseToString returns a string for an HTTP response for debuggging
   434  func HTTPResponseToString(resp *http.Response) string {
   435  	body, _ := ioutil.ReadAll(resp.Body)
   436  	resp.Body = ioutil.NopCloser(bytes.NewReader(body))
   437  	return fmt.Sprintf("statusCode=%d (%s)\n%s",
   438  		resp.StatusCode, resp.Status, string(body))
   439  }
   440  
   441  // CreateClientHome will create a home directory if it does not exist
   442  func CreateClientHome() (string, error) {
   443  	log.Debug("CreateHome")
   444  	home := filepath.Dir(GetDefaultConfigFile("fabric-ca-client"))
   445  
   446  	if _, err := os.Stat(home); err != nil {
   447  		if os.IsNotExist(err) {
   448  			err := os.MkdirAll(home, 0755)
   449  			if err != nil {
   450  				return "", err
   451  			}
   452  		}
   453  	}
   454  	return home, nil
   455  }
   456  
   457  // GetDefaultConfigFile gets the default path for the config file to display in usage message
   458  func GetDefaultConfigFile(cmdName string) string {
   459  	if cmdName == "fabric-ca-server" {
   460  		var fname = fmt.Sprintf("%s-config.yaml", cmdName)
   461  		// First check home env variables
   462  		home := "."
   463  		envs := []string{"FABRIC_CA_SERVER_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH"}
   464  		for _, env := range envs {
   465  			envVal := os.Getenv(env)
   466  			if envVal != "" {
   467  				home = envVal
   468  				break
   469  			}
   470  		}
   471  		return path.Join(home, fname)
   472  	}
   473  
   474  	var fname = fmt.Sprintf("%s-config.yaml", cmdName)
   475  	// First check home env variables
   476  	var home string
   477  	envs := []string{"FABRIC_CA_CLIENT_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH"}
   478  	for _, env := range envs {
   479  		envVal := os.Getenv(env)
   480  		if envVal != "" {
   481  			home = envVal
   482  			return path.Join(home, fname)
   483  		}
   484  	}
   485  
   486  	return path.Join(os.Getenv("HOME"), ".fabric-ca-client", fname)
   487  }
   488  
   489  // GetX509CertificateFromPEMFile gets an X509 certificate from a file
   490  func GetX509CertificateFromPEMFile(file string) (*x509.Certificate, error) {
   491  	pemBytes, err := ReadFile(file)
   492  	if err != nil {
   493  		return nil, err
   494  	}
   495  	x509Cert, err := GetX509CertificateFromPEM(pemBytes)
   496  	if err != nil {
   497  		return nil, errors.Wrapf(err, "Invalid certificate in '%s'", file)
   498  	}
   499  	return x509Cert, nil
   500  }
   501  
   502  // GetX509CertificateFromPEM get an X509 certificate from bytes in PEM format
   503  func GetX509CertificateFromPEM(cert []byte) (*x509.Certificate, error) {
   504  	block, _ := pem.Decode(cert)
   505  	if block == nil {
   506  		return nil, errors.New("Failed to PEM decode certificate")
   507  	}
   508  	x509Cert, err := x509.ParseCertificate(block.Bytes)
   509  	if err != nil {
   510  		return nil, errors.Wrap(err, "Error parsing certificate")
   511  	}
   512  	return x509Cert, nil
   513  }
   514  
   515  // GetX509CertificatesFromPEM returns X509 certificates from bytes in PEM format
   516  func GetX509CertificatesFromPEM(pemBytes []byte) ([]*x509.Certificate, error) {
   517  	chain := pemBytes
   518  	var certs []*x509.Certificate
   519  	for len(chain) > 0 {
   520  		var block *pem.Block
   521  		block, chain = pem.Decode(chain)
   522  		if block == nil {
   523  			break
   524  		}
   525  
   526  		cert, err := x509.ParseCertificate(block.Bytes)
   527  		if err != nil {
   528  			return nil, errors.Wrap(err, "Error parsing certificate")
   529  		}
   530  		certs = append(certs, cert)
   531  	}
   532  	return certs, nil
   533  }
   534  
   535  // GetCertificateDurationFromFile returns the validity duration for a certificate
   536  // in a file.
   537  func GetCertificateDurationFromFile(file string) (time.Duration, error) {
   538  	cert, err := GetX509CertificateFromPEMFile(file)
   539  	if err != nil {
   540  		return 0, err
   541  	}
   542  	return GetCertificateDuration(cert), nil
   543  }
   544  
   545  // GetCertificateDuration returns the validity duration for a certificate
   546  func GetCertificateDuration(cert *x509.Certificate) time.Duration {
   547  	return cert.NotAfter.Sub(cert.NotBefore)
   548  }
   549  
   550  // GetEnrollmentIDFromPEM returns the EnrollmentID from a PEM buffer
   551  func GetEnrollmentIDFromPEM(cert []byte) (string, error) {
   552  	x509Cert, err := GetX509CertificateFromPEM(cert)
   553  	if err != nil {
   554  		return "", err
   555  	}
   556  	return GetEnrollmentIDFromX509Certificate(x509Cert), nil
   557  }
   558  
   559  // GetEnrollmentIDFromX509Certificate returns the EnrollmentID from the X509 certificate
   560  func GetEnrollmentIDFromX509Certificate(cert *x509.Certificate) string {
   561  	return cert.Subject.CommonName
   562  }
   563  
   564  // MakeFileAbs makes 'file' absolute relative to 'dir' if not already absolute
   565  func MakeFileAbs(file, dir string) (string, error) {
   566  	if file == "" {
   567  		return "", nil
   568  	}
   569  	if filepath.IsAbs(file) {
   570  		return file, nil
   571  	}
   572  	path, err := filepath.Abs(filepath.Join(dir, file))
   573  	if err != nil {
   574  		return "", errors.Wrapf(err, "Failed making '%s' absolute based on '%s'", file, dir)
   575  	}
   576  	return path, nil
   577  }
   578  
   579  // MakeFileNamesAbsolute makes all file names in the list absolute, relative to home
   580  func MakeFileNamesAbsolute(files []*string, home string) error {
   581  	for _, filePtr := range files {
   582  		abs, err := MakeFileAbs(*filePtr, home)
   583  		if err != nil {
   584  			return err
   585  		}
   586  		*filePtr = abs
   587  	}
   588  	return nil
   589  }
   590  
   591  // Fatal logs a fatal message and exits
   592  func Fatal(format string, v ...interface{}) {
   593  	log.Fatalf(format, v...)
   594  	os.Exit(1)
   595  }
   596  
   597  // GetUser returns username and password from CLI input
   598  func GetUser(v *viper.Viper) (string, string, error) {
   599  	var fabricCAServerURL string
   600  	fabricCAServerURL = v.GetString("url")
   601  
   602  	URL, err := url.Parse(fabricCAServerURL)
   603  	if err != nil {
   604  		return "", "", err
   605  	}
   606  
   607  	user := URL.User
   608  	if user == nil {
   609  		return "", "", errors.New("No username and password provided as part of the Fabric CA server URL")
   610  	}
   611  
   612  	eid := user.Username()
   613  	if eid == "" {
   614  		return "", "", errors.New("No username provided as part of URL")
   615  	}
   616  
   617  	pass, _ := user.Password()
   618  	if pass == "" {
   619  		return "", "", errors.New("No password provided as part of URL")
   620  	}
   621  
   622  	return eid, pass, nil
   623  }
   624  
   625  // GetSerialAsHex returns the serial number from certificate as hex format
   626  func GetSerialAsHex(serial *big.Int) string {
   627  	hex := fmt.Sprintf("%x", serial)
   628  	return hex
   629  }
   630  
   631  // StructToString converts a struct to a string. If a field
   632  // has a 'secret' tag, it is masked in the returned string
   633  func StructToString(si interface{}) string {
   634  	rval := reflect.ValueOf(si).Elem()
   635  	tipe := rval.Type()
   636  	var buffer bytes.Buffer
   637  	buffer.WriteString("{ ")
   638  	for i := 0; i < rval.NumField(); i++ {
   639  		tf := tipe.Field(i)
   640  		if !rval.FieldByName(tf.Name).CanSet() {
   641  			continue // skip unexported fields
   642  		}
   643  		var fStr string
   644  		tagv := tf.Tag.Get(SecretTag)
   645  		if tagv == "password" || tagv == "username" {
   646  			fStr = fmt.Sprintf("%s:**** ", tf.Name)
   647  		} else if tagv == "url" {
   648  			val, ok := rval.Field(i).Interface().(string)
   649  			if ok {
   650  				val = GetMaskedURL(val)
   651  				fStr = fmt.Sprintf("%s:%v ", tf.Name, val)
   652  			} else {
   653  				fStr = fmt.Sprintf("%s:%v ", tf.Name, rval.Field(i).Interface())
   654  			}
   655  		} else {
   656  			fStr = fmt.Sprintf("%s:%v ", tf.Name, rval.Field(i).Interface())
   657  		}
   658  		buffer.WriteString(fStr)
   659  	}
   660  	buffer.WriteString(" }")
   661  	return buffer.String()
   662  }
   663  
   664  // GetMaskedURL returns masked URL. It masks username and password from the URL
   665  // if present
   666  func GetMaskedURL(url string) string {
   667  	matches := URLRegex.FindStringSubmatch(url)
   668  
   669  	// If there is a match, there should be four entries: 1 for
   670  	// the match and 3 for submatches
   671  	if len(matches) == 4 {
   672  		matchIdxs := URLRegex.FindStringSubmatchIndex(url)
   673  		matchStr := url[matchIdxs[0]:matchIdxs[1]]
   674  		for idx := 2; idx < len(matches); idx++ {
   675  			if matches[idx] != "" {
   676  				matchStr = strings.Replace(matchStr, matches[idx], "****", 1)
   677  			}
   678  		}
   679  		url = url[:matchIdxs[0]] + matchStr + url[matchIdxs[1]:len(url)]
   680  	}
   681  	return url
   682  }
   683  
   684  // NormalizeStringSlice checks for seperators
   685  func NormalizeStringSlice(slice []string) []string {
   686  	var normalizedSlice []string
   687  
   688  	for _, item := range slice {
   689  		// Remove surrounding brackets "[]" if specified
   690  		if strings.HasPrefix(item, "[") && strings.HasSuffix(item, "]") {
   691  			item = item[1 : len(item)-1]
   692  		}
   693  		// Split elements based on comma and add to normalized slice
   694  		elems := strings.Split(item, ",")
   695  		for _, elem := range elems {
   696  			normalizedSlice = append(normalizedSlice, strings.TrimSpace(elem))
   697  		}
   698  	}
   699  	return normalizedSlice
   700  }
   701  
   702  // NormalizeFileList provides absolute pathing for the list of files
   703  func NormalizeFileList(files []string, homeDir string) ([]string, error) {
   704  	var err error
   705  
   706  	files = NormalizeStringSlice(files)
   707  
   708  	for i, file := range files {
   709  		files[i], err = MakeFileAbs(file, homeDir)
   710  		if err != nil {
   711  			return nil, err
   712  		}
   713  	}
   714  
   715  	return files, nil
   716  }
   717  
   718  // CheckHostsInCert checks to see if host correctly inserted into certificate
   719  func CheckHostsInCert(certFile string, hosts ...string) error {
   720  	certBytes, err := ioutil.ReadFile(certFile)
   721  	if err != nil {
   722  		return errors.Wrapf(err, "Failed to read certificate file at '%s'", certFile)
   723  	}
   724  
   725  	cert, err := GetX509CertificateFromPEM(certBytes)
   726  	if err != nil {
   727  		return errors.Wrap(err, "Failed to get certificate")
   728  	}
   729  
   730  	// combine DNSNames and IPAddresses from cert
   731  	sans := cert.DNSNames
   732  	for _, ip := range cert.IPAddresses {
   733  		sans = append(sans, ip.String())
   734  	}
   735  	for _, host := range hosts {
   736  		if !containsString(sans, host) {
   737  			return errors.Errorf("Host '%s' was not found in the certificate in file '%s'", host, certFile)
   738  		}
   739  	}
   740  	return nil
   741  }
   742  
   743  func containsString(list []string, item string) bool {
   744  	for _, elem := range list {
   745  		if elem == item {
   746  			return true
   747  		}
   748  	}
   749  	return false
   750  }
   751  
   752  // Read reads from Reader into a byte array
   753  func Read(r io.Reader, data []byte) ([]byte, error) {
   754  	j := 0
   755  	for {
   756  		n, err := r.Read(data[j:])
   757  		j = j + n
   758  		if err != nil {
   759  			if err == io.EOF {
   760  				break
   761  			}
   762  			return nil, errors.Wrapf(err, "Read failure")
   763  		}
   764  
   765  		if (n == 0 && j == len(data)) || j > len(data) {
   766  			return nil, errors.New("Size of requested data is too large")
   767  		}
   768  	}
   769  
   770  	return data[:j], nil
   771  }
   772  
   773  // Hostname name returns the hostname of the machine
   774  func Hostname() string {
   775  	hostname, _ := os.Hostname()
   776  	if hostname == "" {
   777  		hostname = "localhost"
   778  	}
   779  	return hostname
   780  }
   781  
   782  // ValidateAndReturnAbsConf checks to see that there are no conflicts between the
   783  // configuration file path and home directory. If no conflicts, returns back the absolute
   784  // path for the configuration file and home directory.
   785  func ValidateAndReturnAbsConf(configFilePath, homeDir, cmdName string) (string, string, error) {
   786  	var err error
   787  	var homeDirSet bool
   788  	var configFileSet bool
   789  
   790  	defaultConfig := GetDefaultConfigFile(cmdName) // Get the default configuration
   791  
   792  	if configFilePath == "" {
   793  		configFilePath = defaultConfig // If no config file path specified, use the default configuration file
   794  	} else {
   795  		configFileSet = true
   796  	}
   797  
   798  	if homeDir == "" {
   799  		homeDir = filepath.Dir(defaultConfig) // If no home directory specified, use the default directory
   800  	} else {
   801  		homeDirSet = true
   802  	}
   803  
   804  	// Make the home directory absolute
   805  	homeDir, err = filepath.Abs(homeDir)
   806  	if err != nil {
   807  		return "", "", errors.Wrap(err, "Failed to get full path of config file")
   808  	}
   809  	homeDir = strings.TrimRight(homeDir, "/")
   810  
   811  	if configFileSet && homeDirSet {
   812  		log.Warning("Using both --config and --home CLI flags; --config will take precedence")
   813  	}
   814  
   815  	if configFileSet {
   816  		configFilePath, err = filepath.Abs(configFilePath)
   817  		if err != nil {
   818  			return "", "", errors.Wrap(err, "Failed to get full path of configuration file")
   819  		}
   820  		return configFilePath, filepath.Dir(configFilePath), nil
   821  	}
   822  
   823  	configFile := filepath.Join(homeDir, filepath.Base(defaultConfig)) // Join specified home directory with default config file name
   824  	return configFile, homeDir, nil
   825  }
   826  
   827  // GetSliceFromList will return a slice from a list
   828  func GetSliceFromList(split string, delim string) []string {
   829  	return strings.Split(strings.Replace(split, " ", "", -1), delim)
   830  }
   831  
   832  // ListContains looks through a comma separated list to see if a string exists
   833  func ListContains(list, find string) bool {
   834  	items := strings.Split(list, ",")
   835  	for _, item := range items {
   836  		item = strings.TrimPrefix(item, " ")
   837  		if item == find {
   838  			return true
   839  		}
   840  	}
   841  	return false
   842  }
   843  
   844  //TODO:  move these out of production code
   845  
   846  // FatalError will check to see if an error occured if so it will cause the test cases exit
   847  func FatalError(t *testing.T, err error, msg string, args ...interface{}) {
   848  	if len(args) > 0 {
   849  		msg = fmt.Sprintf(msg, args)
   850  	}
   851  	if !assert.NoError(t, err, msg) {
   852  		t.Fatal(msg)
   853  	}
   854  }
   855  
   856  // ErrorContains will check to see if an error occurred, if so it will check that it contains
   857  // the appropriate error message
   858  func ErrorContains(t *testing.T, err error, contains, msg string, args ...interface{}) {
   859  	if len(args) > 0 {
   860  		msg = fmt.Sprintf(msg, args)
   861  	}
   862  	if assert.Error(t, err, msg) {
   863  		assert.Contains(t, err.Error(), contains)
   864  	}
   865  }