github.com/ali-iotechsys/cli@v20.10.0+incompatible/cli/command/trust/key_load.go (about)

     1  package trust
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/pem"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"runtime"
    10  
    11  	"github.com/docker/cli/cli"
    12  	"github.com/docker/cli/cli/command"
    13  	"github.com/docker/cli/cli/trust"
    14  	"github.com/pkg/errors"
    15  	"github.com/spf13/cobra"
    16  	"github.com/theupdateframework/notary"
    17  	"github.com/theupdateframework/notary/storage"
    18  	"github.com/theupdateframework/notary/trustmanager"
    19  	tufutils "github.com/theupdateframework/notary/tuf/utils"
    20  )
    21  
    22  const (
    23  	nonOwnerReadWriteMask = 0077
    24  )
    25  
    26  type keyLoadOptions struct {
    27  	keyName string
    28  }
    29  
    30  func newKeyLoadCommand(dockerCli command.Streams) *cobra.Command {
    31  	var options keyLoadOptions
    32  	cmd := &cobra.Command{
    33  		Use:   "load [OPTIONS] KEYFILE",
    34  		Short: "Load a private key file for signing",
    35  		Args:  cli.ExactArgs(1),
    36  		RunE: func(cmd *cobra.Command, args []string) error {
    37  			return loadPrivKey(dockerCli, args[0], options)
    38  		},
    39  	}
    40  	flags := cmd.Flags()
    41  	flags.StringVar(&options.keyName, "name", "signer", "Name for the loaded key")
    42  	return cmd
    43  }
    44  
    45  func loadPrivKey(streams command.Streams, keyPath string, options keyLoadOptions) error {
    46  	// validate the key name if provided
    47  	if options.keyName != "" && !validKeyName(options.keyName) {
    48  		return fmt.Errorf("key name \"%s\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", options.keyName)
    49  	}
    50  	trustDir := trust.GetTrustDirectory()
    51  	keyFileStore, err := storage.NewPrivateKeyFileStorage(trustDir, notary.KeyExtension)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	privKeyImporters := []trustmanager.Importer{keyFileStore}
    56  
    57  	fmt.Fprintf(streams.Out(), "Loading key from \"%s\"...\n", keyPath)
    58  
    59  	// Always use a fresh passphrase retriever for each import
    60  	passRet := trust.GetPassphraseRetriever(streams.In(), streams.Out())
    61  	keyBytes, err := getPrivKeyBytesFromPath(keyPath)
    62  	if err != nil {
    63  		return errors.Wrapf(err, "refusing to load key from %s", keyPath)
    64  	}
    65  	if err := loadPrivKeyBytesToStore(keyBytes, privKeyImporters, keyPath, options.keyName, passRet); err != nil {
    66  		return errors.Wrapf(err, "error importing key from %s", keyPath)
    67  	}
    68  	fmt.Fprintf(streams.Out(), "Successfully imported key from %s\n", keyPath)
    69  	return nil
    70  }
    71  
    72  func getPrivKeyBytesFromPath(keyPath string) ([]byte, error) {
    73  	if runtime.GOOS != "windows" {
    74  		fileInfo, err := os.Stat(keyPath)
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  		if fileInfo.Mode()&nonOwnerReadWriteMask != 0 {
    79  			return nil, fmt.Errorf("private key file %s must not be readable or writable by others", keyPath)
    80  		}
    81  	}
    82  
    83  	from, err := os.OpenFile(keyPath, os.O_RDONLY, notary.PrivExecPerms)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	defer from.Close()
    88  
    89  	return ioutil.ReadAll(from)
    90  }
    91  
    92  func loadPrivKeyBytesToStore(privKeyBytes []byte, privKeyImporters []trustmanager.Importer, keyPath, keyName string, passRet notary.PassRetriever) error {
    93  	var err error
    94  	if _, _, err = tufutils.ExtractPrivateKeyAttributes(privKeyBytes); err != nil {
    95  		return fmt.Errorf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", keyPath)
    96  	}
    97  	if privKeyBytes, err = decodePrivKeyIfNecessary(privKeyBytes, passRet); err != nil {
    98  		return errors.Wrapf(err, "cannot load key from provided file %s", keyPath)
    99  	}
   100  	// Make a reader, rewind the file pointer
   101  	return trustmanager.ImportKeys(bytes.NewReader(privKeyBytes), privKeyImporters, keyName, "", passRet)
   102  }
   103  
   104  func decodePrivKeyIfNecessary(privPemBytes []byte, passRet notary.PassRetriever) ([]byte, error) {
   105  	pemBlock, _ := pem.Decode(privPemBytes)
   106  	_, containsDEKInfo := pemBlock.Headers["DEK-Info"]
   107  	if containsDEKInfo || pemBlock.Type == "ENCRYPTED PRIVATE KEY" {
   108  		// if we do not have enough information to properly import, try to decrypt the key
   109  		if _, ok := pemBlock.Headers["path"]; !ok {
   110  			privKey, _, err := trustmanager.GetPasswdDecryptBytes(passRet, privPemBytes, "", "encrypted")
   111  			if err != nil {
   112  				return []byte{}, fmt.Errorf("could not decrypt key")
   113  			}
   114  			privPemBytes = privKey.Private()
   115  		}
   116  	}
   117  	return privPemBytes, nil
   118  }