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