github.com/blockchain-gm/fabric-ca@v0.0.0-20200423072702-b2c40c7ac69c/cmd/fabric-ca-client/command/getcainfo.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 command
    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/cloudflare/cfssl/log"
    31  	"github.com/hyperledger/fabric-ca/api"
    32  	"github.com/hyperledger/fabric-ca/lib"
    33  	"github.com/hyperledger/fabric-ca/util"
    34  	"github.com/pkg/errors"
    35  	"github.com/spf13/cobra"
    36  	"github.com/tjfoc/gmsm/sm2"
    37  )
    38  
    39  const (
    40  	// GetCAInfoCmdUsage is the usage text for getCACert command
    41  	GetCAInfoCmdUsage = "getcainfo -u http://serverAddr:serverPort -M <MSP-directory>"
    42  	// GetCAInfoCmdShortDesc is the short description for getCACert command
    43  	GetCAInfoCmdShortDesc = "Get CA certificate chain and Idemix public key"
    44  )
    45  
    46  type getCAInfoCmd struct {
    47  	Command
    48  }
    49  
    50  func newGetCAInfoCmd(c Command) *getCAInfoCmd {
    51  	getcacertcmd := &getCAInfoCmd{c}
    52  	return getcacertcmd
    53  }
    54  
    55  func (c *getCAInfoCmd) getCommand() *cobra.Command {
    56  	cmd := &cobra.Command{
    57  		Use:     GetCAInfoCmdUsage,
    58  		Short:   GetCAInfoCmdShortDesc,
    59  		Aliases: []string{"getcacert"},
    60  		PreRunE: c.preRunGetCACert,
    61  		RunE:    c.runGetCACert,
    62  	}
    63  	return cmd
    64  }
    65  
    66  func (c *getCAInfoCmd) preRunGetCACert(cmd *cobra.Command, args []string) error {
    67  	if len(args) > 0 {
    68  		return errors.Errorf(extraArgsError, args, cmd.UsageString())
    69  	}
    70  
    71  	err := c.ConfigInit()
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	log.Debugf("Client configuration settings: %+v", c.GetClientCfg())
    77  
    78  	return nil
    79  }
    80  
    81  func (c *getCAInfoCmd) runGetCACert(cmd *cobra.Command, args []string) error {
    82  	log.Debug("Entered runGetCACert")
    83  
    84  	client := &lib.Client{
    85  		HomeDir: filepath.Dir(c.GetCfgFileName()),
    86  		Config:  c.GetClientCfg(),
    87  	}
    88  
    89  	req := &api.GetCAInfoRequest{
    90  		CAName: c.GetClientCfg().CAName,
    91  	}
    92  
    93  	si, err := client.GetCAInfo(req)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	err = storeCAChain(client.Config, si)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	err = storeIssuerPublicKey(client.Config, si)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	return storeIssuerRevocationPublicKey(client.Config, si)
   107  }
   108  
   109  // Store the CAChain in the CACerts folder of MSP (Membership Service Provider)
   110  // The root cert in the chain goes into MSP 'cacerts' directory.
   111  // The others (if any) go into the MSP 'intermediatecerts' directory.
   112  func storeCAChain(config *lib.ClientConfig, si *lib.GetCAInfoResponse) error {
   113  	mspDir := config.MSPDir
   114  	// Get a unique name to use for filenames
   115  	serverURL, err := url.Parse(config.URL)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	fname := serverURL.Host
   120  	if config.CAName != "" {
   121  		fname = fmt.Sprintf("%s-%s", fname, config.CAName)
   122  	}
   123  	fname = strings.Replace(fname, ":", "-", -1)
   124  	fname = strings.Replace(fname, ".", "-", -1) + ".pem"
   125  	tlsfname := fmt.Sprintf("tls-%s", fname)
   126  
   127  	rootCACertsDir := path.Join(mspDir, "cacerts")
   128  	intCACertsDir := path.Join(mspDir, "intermediatecerts")
   129  	tlsRootCACertsDir := path.Join(mspDir, "tlscacerts")
   130  	tlsIntCACertsDir := path.Join(mspDir, "tlsintermediatecerts")
   131  
   132  	var rootBlks [][]byte
   133  	var intBlks [][]byte
   134  	chain := si.CAChain
   135  	for len(chain) > 0 {
   136  		var block *pem.Block
   137  		block, chain = pem.Decode(chain)
   138  		if block == nil {
   139  			break
   140  		}
   141  
   142  		log.Debug("chain----------------", string(chain))
   143  		cert, err := sm2.ParseCertificate(block.Bytes)
   144  		if err != nil {
   145  			return errors.Wrap(err, "Failed to parse certificate in the CA chain")
   146  		}
   147  
   148  		if !cert.IsCA {
   149  			return errors.New("A certificate in the CA chain is not a CA certificate")
   150  		}
   151  
   152  		// If authority key id is not present or if it is present and equal to subject key id,
   153  		// then it is a root certificate
   154  		if len(cert.AuthorityKeyId) == 0 || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) {
   155  			rootBlks = append(rootBlks, pem.EncodeToMemory(block))
   156  		} else {
   157  			intBlks = append(intBlks, pem.EncodeToMemory(block))
   158  		}
   159  	}
   160  
   161  	// Store the root certificates in the "cacerts" msp folder
   162  	certBytes := bytes.Join(rootBlks, []byte(""))
   163  	if len(certBytes) > 0 {
   164  		if config.Enrollment.Profile == "tls" {
   165  			err := storeToFile("TLS root CA certificate", tlsRootCACertsDir, tlsfname, certBytes)
   166  			if err != nil {
   167  				return err
   168  			}
   169  		} else {
   170  			err = storeToFile("root CA certificate", rootCACertsDir, fname, certBytes)
   171  			if err != nil {
   172  				return err
   173  			}
   174  		}
   175  	}
   176  
   177  	// Store the intermediate certificates in the "intermediatecerts" msp folder
   178  	certBytes = bytes.Join(intBlks, []byte(""))
   179  	if len(certBytes) > 0 {
   180  		if config.Enrollment.Profile == "tls" {
   181  			err = storeToFile("TLS intermediate certificates", tlsIntCACertsDir, tlsfname, certBytes)
   182  			if err != nil {
   183  				return err
   184  			}
   185  		} else {
   186  			err = storeToFile("intermediate CA certificates", intCACertsDir, fname, certBytes)
   187  			if err != nil {
   188  				return err
   189  			}
   190  		}
   191  	}
   192  	return nil
   193  }
   194  
   195  func storeIssuerPublicKey(config *lib.ClientConfig, si *lib.GetCAInfoResponse) error {
   196  	if len(si.IssuerPublicKey) > 0 {
   197  		err := storeToFile("Issuer public key", config.MSPDir, "IssuerPublicKey", si.IssuerPublicKey)
   198  		if err != nil {
   199  			return err
   200  		}
   201  	}
   202  	return nil
   203  }
   204  
   205  func storeIssuerRevocationPublicKey(config *lib.ClientConfig, si *lib.GetCAInfoResponse) error {
   206  	if len(si.IssuerRevocationPublicKey) > 0 {
   207  		err := storeToFile("Issuer revocation public key", config.MSPDir, "IssuerRevocationPublicKey", si.IssuerRevocationPublicKey)
   208  		if err != nil {
   209  			return err
   210  		}
   211  	}
   212  	return nil
   213  }
   214  
   215  func storeToFile(what, dir, fname string, contents []byte) error {
   216  	err := os.MkdirAll(dir, 0755)
   217  	if err != nil {
   218  		return errors.Wrapf(err, "Failed to create directory for %s at '%s'", what, dir)
   219  	}
   220  	fpath := path.Join(dir, fname)
   221  	err = util.WriteFile(fpath, contents, 0644)
   222  	if err != nil {
   223  		return errors.WithMessage(err, fmt.Sprintf("Failed to store %s at '%s'", what, fpath))
   224  	}
   225  	log.Infof("Stored %s at %s", what, fpath)
   226  	return nil
   227  }