github.com/silveraid/fabric-ca@v1.1.0-preview.0.20180127000700-71974f53ab08/cmd/fabric-ca-client/getcacert.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 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 main
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/x509"
    22  	"encoding/pem"
    23  	"fmt"
    24  	"net/url"
    25  	"os"
    26  	"path"
    27  	"path/filepath"
    28  	"strings"
    29  
    30  	"github.com/pkg/errors"
    31  
    32  	"github.com/cloudflare/cfssl/log"
    33  	"github.com/hyperledger/fabric-ca/api"
    34  	"github.com/hyperledger/fabric-ca/lib"
    35  	"github.com/hyperledger/fabric-ca/util"
    36  	"github.com/spf13/cobra"
    37  )
    38  
    39  func (c *ClientCmd) newGetCACertCommand() *cobra.Command {
    40  	getCACertCmd := &cobra.Command{
    41  		Use:   "getcacert -u http://serverAddr:serverPort -M <MSP-directory>",
    42  		Short: "Get CA certificate chain",
    43  		// PreRunE block for this command will load client configuration
    44  		// before running the command
    45  		PreRunE: func(cmd *cobra.Command, args []string) error {
    46  			if len(args) > 0 {
    47  				return errors.Errorf(extraArgsError, args, cmd.UsageString())
    48  			}
    49  
    50  			err := c.configInit()
    51  			if err != nil {
    52  				return err
    53  			}
    54  
    55  			log.Debugf("Client configuration settings: %+v", c.clientCfg)
    56  
    57  			return nil
    58  		},
    59  		RunE: func(cmd *cobra.Command, args []string) error {
    60  			err := c.runGetCACert()
    61  			if err != nil {
    62  				return err
    63  			}
    64  			return nil
    65  		},
    66  	}
    67  	return getCACertCmd
    68  }
    69  
    70  // The client "getcacert" main logic
    71  func (c *ClientCmd) runGetCACert() error {
    72  	log.Debug("Entered runGetCACert")
    73  
    74  	client := &lib.Client{
    75  		HomeDir: filepath.Dir(c.cfgFileName),
    76  		Config:  c.clientCfg,
    77  	}
    78  
    79  	req := &api.GetCAInfoRequest{
    80  		CAName: c.clientCfg.CAName,
    81  	}
    82  
    83  	si, err := client.GetCAInfo(req)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	return storeCAChain(client.Config, si)
    89  }
    90  
    91  // Store the CAChain in the CACerts folder of MSP (Membership Service Provider)
    92  // The root cert in the chain goes into MSP 'cacerts' directory.
    93  // The others (if any) go into the MSP 'intermediatecerts' directory.
    94  func storeCAChain(config *lib.ClientConfig, si *lib.GetServerInfoResponse) error {
    95  	mspDir := config.MSPDir
    96  	// Get a unique name to use for filenames
    97  	serverURL, err := url.Parse(config.URL)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	fname := serverURL.Host
   102  	if config.CAName != "" {
   103  		fname = fmt.Sprintf("%s-%s", fname, config.CAName)
   104  	}
   105  	fname = strings.Replace(fname, ":", "-", -1)
   106  	fname = strings.Replace(fname, ".", "-", -1) + ".pem"
   107  	tlsfname := fmt.Sprintf("tls-%s", fname)
   108  
   109  	rootCACertsDir := path.Join(mspDir, "cacerts")
   110  	intCACertsDir := path.Join(mspDir, "intermediatecerts")
   111  	tlsRootCACertsDir := path.Join(mspDir, "tlscacerts")
   112  	tlsIntCACertsDir := path.Join(mspDir, "tlsintermediatecerts")
   113  
   114  	var rootBlks [][]byte
   115  	var intBlks [][]byte
   116  	chain := si.CAChain
   117  	for len(chain) > 0 {
   118  		var block *pem.Block
   119  		block, chain = pem.Decode(chain)
   120  		if block == nil {
   121  			break
   122  		}
   123  
   124  		cert, err := x509.ParseCertificate(block.Bytes)
   125  		if err != nil {
   126  			return errors.Wrap(err, "Failed to parse certificate in the CA chain")
   127  		}
   128  
   129  		if !cert.IsCA {
   130  			return errors.New("A certificate in the CA chain is not a CA certificate")
   131  		}
   132  
   133  		// If authority key id is not present or if it is present and equal to subject key id,
   134  		// then it is a root certificate
   135  		if len(cert.AuthorityKeyId) == 0 || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) {
   136  			rootBlks = append(rootBlks, pem.EncodeToMemory(block))
   137  		} else {
   138  			intBlks = append(intBlks, pem.EncodeToMemory(block))
   139  		}
   140  	}
   141  
   142  	// Store the root certificates in the "cacerts" msp folder
   143  	certBytes := bytes.Join(rootBlks, []byte(""))
   144  	if config.Enrollment.Profile == "tls" {
   145  		err := storeCert("TLS root CA certificate", tlsRootCACertsDir, tlsfname, certBytes)
   146  		if err != nil {
   147  			return err
   148  		}
   149  	} else {
   150  		err = storeCert("root CA certificate", rootCACertsDir, fname, certBytes)
   151  		if err != nil {
   152  			return err
   153  		}
   154  	}
   155  
   156  	// Store the intermediate certificates in the "intermediatecerts" msp folder
   157  	certBytes = bytes.Join(intBlks, []byte(""))
   158  	if config.Enrollment.Profile == "tls" {
   159  		err = storeCert("TLS intermediate certificates", tlsIntCACertsDir, tlsfname, certBytes)
   160  		if err != nil {
   161  			return err
   162  		}
   163  	} else {
   164  		err = storeCert("intermediate CA certificates", intCACertsDir, fname, certBytes)
   165  		if err != nil {
   166  			return err
   167  		}
   168  	}
   169  	return nil
   170  }
   171  
   172  func storeCert(what, dir, fname string, contents []byte) error {
   173  	err := os.MkdirAll(dir, 0755)
   174  	if err != nil {
   175  		return errors.Wrapf(err, "Failed to create directory for %s at '%s'", what, dir)
   176  	}
   177  	fpath := path.Join(dir, fname)
   178  	err = util.WriteFile(fpath, contents, 0644)
   179  	if err != nil {
   180  		return errors.WithMessage(err, fmt.Sprintf("Failed to store %s at '%s'", what, fpath))
   181  	}
   182  	log.Infof("Stored %s at %s", what, fpath)
   183  	return nil
   184  }