github.com/cactusblossom/fabric-ca@v0.0.0-20200611062428-0082fc643826/lib/util.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  	"crypto/tls"
    11  	"crypto/x509"
    12  	"encoding/hex"
    13  	"encoding/json"
    14  	"encoding/pem"
    15  	"fmt"
    16  	"io/ioutil"
    17  	"net/http"
    18  	"os"
    19  	"path/filepath"
    20  
    21  	"github.com/cloudflare/cfssl/log"
    22  	"github.com/grantae/certinfo"
    23  	"github.com/hyperledger/fabric-ca/api"
    24  	"github.com/hyperledger/fabric-ca/util"
    25  	"github.com/pkg/errors"
    26  	"github.com/spf13/viper"
    27  )
    28  
    29  var clientAuthTypes = map[string]tls.ClientAuthType{
    30  	"noclientcert":               tls.NoClientCert,
    31  	"requestclientcert":          tls.RequestClientCert,
    32  	"requireanyclientcert":       tls.RequireAnyClientCert,
    33  	"verifyclientcertifgiven":    tls.VerifyClientCertIfGiven,
    34  	"requireandverifyclientcert": tls.RequireAndVerifyClientCert,
    35  }
    36  
    37  // GetCertID returns both the serial number and AKI (Authority Key ID) for the certificate
    38  func GetCertID(bytes []byte) (string, string, error) {
    39  	cert, err := BytesToX509Cert(bytes)
    40  	if err != nil {
    41  		return "", "", err
    42  	}
    43  	serial := util.GetSerialAsHex(cert.SerialNumber)
    44  	aki := hex.EncodeToString(cert.AuthorityKeyId)
    45  	return serial, aki, nil
    46  }
    47  
    48  // BytesToX509Cert converts bytes (PEM or DER) to an X509 certificate
    49  func BytesToX509Cert(bytes []byte) (*x509.Certificate, error) {
    50  	dcert, _ := pem.Decode(bytes)
    51  	if dcert != nil {
    52  		bytes = dcert.Bytes
    53  	}
    54  	cert, err := x509.ParseCertificate(bytes)
    55  	if err != nil {
    56  		return nil, errors.Wrap(err, "Buffer was neither PEM nor DER encoding")
    57  	}
    58  	return cert, err
    59  }
    60  
    61  // LoadPEMCertPool loads a pool of PEM certificates from list of files
    62  func LoadPEMCertPool(certFiles []string) (*x509.CertPool, error) {
    63  	certPool := x509.NewCertPool()
    64  
    65  	if len(certFiles) > 0 {
    66  		for _, cert := range certFiles {
    67  			log.Debugf("Reading cert file: %s", cert)
    68  			pemCerts, err := ioutil.ReadFile(cert)
    69  			if err != nil {
    70  				return nil, err
    71  			}
    72  
    73  			log.Debugf("Appending cert %s to pool", cert)
    74  			if !certPool.AppendCertsFromPEM(pemCerts) {
    75  				return nil, errors.New("Failed to load cert pool")
    76  			}
    77  		}
    78  	}
    79  
    80  	return certPool, nil
    81  }
    82  
    83  // UnmarshalConfig unmarshals a configuration file
    84  func UnmarshalConfig(config interface{}, vp *viper.Viper, configFile string,
    85  	server bool) error {
    86  
    87  	vp.SetConfigFile(configFile)
    88  	err := vp.ReadInConfig()
    89  	if err != nil {
    90  		return errors.Wrapf(err, "Failed to read config file '%s'", configFile)
    91  	}
    92  
    93  	err = vp.Unmarshal(config)
    94  	if err != nil {
    95  		return errors.Wrapf(err, "Incorrect format in file '%s'", configFile)
    96  	}
    97  	if server {
    98  		serverCfg := config.(*ServerConfig)
    99  		err = vp.Unmarshal(&serverCfg.CAcfg)
   100  		if err != nil {
   101  			return errors.Wrapf(err, "Incorrect format in file '%s'", configFile)
   102  		}
   103  	}
   104  	return nil
   105  }
   106  
   107  func getMaxEnrollments(userMaxEnrollments int, caMaxEnrollments int) (int, error) {
   108  	log.Debugf("Max enrollment value verification - User specified max enrollment: %d, CA max enrollment: %d", userMaxEnrollments, caMaxEnrollments)
   109  	if userMaxEnrollments < -1 {
   110  		return 0, errors.Errorf("Max enrollment in registration request may not be less than -1, but was %d", userMaxEnrollments)
   111  	}
   112  	switch caMaxEnrollments {
   113  	case -1:
   114  		if userMaxEnrollments == 0 {
   115  			// The user is requesting the matching limit of the CA, so gets infinite
   116  			return caMaxEnrollments, nil
   117  		}
   118  		// There is no CA max enrollment limit, so simply use the user requested value
   119  		return userMaxEnrollments, nil
   120  	case 0:
   121  		// The CA max enrollment is 0, so registration is disabled.
   122  		return 0, errors.New("Registration is disabled")
   123  	default:
   124  		switch userMaxEnrollments {
   125  		case -1:
   126  			// User requested infinite enrollments is not allowed
   127  			return 0, errors.New("Registration for infinite enrollments is not allowed")
   128  		case 0:
   129  			// User is requesting the current CA maximum
   130  			return caMaxEnrollments, nil
   131  		default:
   132  			// User is requesting a specific positive value; make sure it doesn't exceed the CA maximum.
   133  			if userMaxEnrollments > caMaxEnrollments {
   134  				return 0, errors.Errorf("Requested enrollments (%d) exceeds maximum allowable enrollments (%d)",
   135  					userMaxEnrollments, caMaxEnrollments)
   136  			}
   137  			// otherwise, use the requested maximum
   138  			return userMaxEnrollments, nil
   139  		}
   140  	}
   141  }
   142  
   143  func addQueryParm(req *http.Request, name, value string) {
   144  	url := req.URL.Query()
   145  	url.Add(name, value)
   146  	req.URL.RawQuery = url.Encode()
   147  }
   148  
   149  // IdentityDecoder decodes streams of data coming from the server into an Identity object
   150  func IdentityDecoder(decoder *json.Decoder) error {
   151  	var id api.IdentityInfo
   152  	err := decoder.Decode(&id)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	fmt.Printf("Name: %s, Type: %s, Affiliation: %s, Max Enrollments: %d, Attributes: %+v\n", id.ID, id.Type, id.Affiliation, id.MaxEnrollments, id.Attributes)
   157  	return nil
   158  }
   159  
   160  // AffiliationDecoder decodes streams of data coming from the server into an Affiliation object
   161  func AffiliationDecoder(decoder *json.Decoder) error {
   162  	var aff api.AffiliationInfo
   163  	err := decoder.Decode(&aff)
   164  	if err != nil {
   165  		return err
   166  	}
   167  	fmt.Printf("%s\n", aff.Name)
   168  	return nil
   169  }
   170  
   171  // CertificateDecoder is needed to keep track of state, to see how many certificates
   172  // have been returned for each enrollment ID.
   173  type CertificateDecoder struct {
   174  	certIDCount map[string]int
   175  	storePath   string
   176  }
   177  
   178  // NewCertificateDecoder returns decoder for certificates
   179  func NewCertificateDecoder(storePath string) *CertificateDecoder {
   180  	cd := &CertificateDecoder{}
   181  	cd.certIDCount = make(map[string]int)
   182  	cd.storePath = storePath
   183  	return cd
   184  }
   185  
   186  // CertificateDecoder decodes streams of data coming from the server
   187  func (cd *CertificateDecoder) CertificateDecoder(decoder *json.Decoder) error {
   188  	var cert certPEM
   189  	err := decoder.Decode(&cert)
   190  	if err != nil {
   191  		return err
   192  	}
   193  	block, rest := pem.Decode([]byte(cert.PEM))
   194  	if block == nil || len(rest) > 0 {
   195  		return errors.New("Certificate decoding error")
   196  	}
   197  	certificate, err := x509.ParseCertificate(block.Bytes)
   198  	if err != nil {
   199  		return err
   200  	}
   201  	enrollmentID := certificate.Subject.CommonName
   202  	if cd.storePath != "" {
   203  		err = cd.StoreCert(enrollmentID, cd.storePath, []byte(cert.PEM))
   204  		if err != nil {
   205  			return err
   206  		}
   207  	}
   208  
   209  	result, err := certinfo.CertificateText(certificate)
   210  	if err != nil {
   211  		return err
   212  	}
   213  	fmt.Printf(result)
   214  	return nil
   215  }
   216  
   217  // StoreCert stores the certificate on the file system
   218  func (cd *CertificateDecoder) StoreCert(enrollmentID, storePath string, cert []byte) error {
   219  	cd.certIDCount[enrollmentID] = cd.certIDCount[enrollmentID] + 1
   220  
   221  	err := os.MkdirAll(storePath, os.ModePerm)
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	var filePath string
   227  	singleCertName := fmt.Sprintf("%s.pem", enrollmentID)
   228  	switch cd.certIDCount[enrollmentID] {
   229  	case 1: // Only one certificate returned, don't need to append number to certificate file name
   230  		filePath = filepath.Join(storePath, singleCertName)
   231  	case 2: // Two certificates returned, rename the old certificate to have number at the end
   232  		err := os.Rename(filepath.Join(storePath, singleCertName), filepath.Join(storePath, fmt.Sprintf("%s-1.pem", enrollmentID)))
   233  		if err != nil {
   234  			return errors.WithMessage(err, fmt.Sprintf("Failed to rename certificate: %s", singleCertName))
   235  		}
   236  		filePath = filepath.Join(storePath, fmt.Sprintf("%s-%d.pem", enrollmentID, cd.certIDCount[enrollmentID]))
   237  	default:
   238  		filePath = filepath.Join(storePath, fmt.Sprintf("%s-%d.pem", enrollmentID, cd.certIDCount[enrollmentID]))
   239  	}
   240  
   241  	err = ioutil.WriteFile(filePath, cert, 0644)
   242  	if err != nil {
   243  		return errors.WithMessage(err, fmt.Sprintf("Failed to store certificate at: %s", storePath))
   244  	}
   245  
   246  	return nil
   247  }