github.com/vmware/govmomi@v0.37.2/govc/extension/setcert.go (about)

     1  /*
     2  Copyright (c) 2015 VMware, Inc. 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 extension
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/rand"
    23  	"crypto/rsa"
    24  	"crypto/x509"
    25  	"crypto/x509/pkix"
    26  	"encoding/pem"
    27  	"flag"
    28  	"fmt"
    29  	"io"
    30  	"math/big"
    31  	"os"
    32  	"strings"
    33  	"time"
    34  
    35  	"github.com/vmware/govmomi/govc/cli"
    36  	"github.com/vmware/govmomi/govc/flags"
    37  	"github.com/vmware/govmomi/object"
    38  )
    39  
    40  type setcert struct {
    41  	*flags.ClientFlag
    42  
    43  	cert string
    44  	org  string
    45  
    46  	encodedCert bytes.Buffer
    47  }
    48  
    49  func init() {
    50  	cli.Register("extension.setcert", &setcert{})
    51  }
    52  
    53  func (cmd *setcert) Register(ctx context.Context, f *flag.FlagSet) {
    54  	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
    55  	cmd.ClientFlag.Register(ctx, f)
    56  
    57  	f.StringVar(&cmd.cert, "cert-pem", "-", "PEM encoded certificate")
    58  	f.StringVar(&cmd.org, "org", "VMware", "Organization for generated certificate")
    59  }
    60  
    61  func (cmd *setcert) Process(ctx context.Context) error {
    62  	if err := cmd.ClientFlag.Process(ctx); err != nil {
    63  		return err
    64  	}
    65  	return nil
    66  }
    67  
    68  func (cmd *setcert) Usage() string {
    69  	return "ID"
    70  }
    71  
    72  func (cmd *setcert) Description() string {
    73  	return `Set certificate for the extension ID.
    74  
    75  The '-cert-pem' option can be one of the following:
    76  '-' : Read the certificate from stdin
    77  '+' : Generate a new key pair and save locally to ID.crt and ID.key
    78  ... : Any other value is passed as-is to ExtensionManager.SetCertificate
    79  
    80  Examples:
    81    govc extension.setcert -cert-pem + -org Example com.example.extname`
    82  }
    83  
    84  func (cmd *setcert) create(id string) error {
    85  	certFile, err := os.Create(id + ".crt")
    86  	if err != nil {
    87  		return err
    88  	}
    89  	defer certFile.Close()
    90  
    91  	keyFile, err := os.Create(id + ".key")
    92  	if err != nil {
    93  		return err
    94  	}
    95  	defer keyFile.Close()
    96  
    97  	priv, err := rsa.GenerateKey(rand.Reader, 2048)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	notBefore := time.Now()
   103  	notAfter := notBefore.Add(5 * 365 * 24 * time.Hour) // 5 years
   104  
   105  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
   106  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	template := x509.Certificate{
   112  		SerialNumber: serialNumber,
   113  		Subject: pkix.Name{
   114  			Organization: []string{cmd.org},
   115  		},
   116  		NotBefore:             notBefore,
   117  		NotAfter:              notAfter,
   118  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
   119  		BasicConstraintsValid: true,
   120  	}
   121  
   122  	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	err = pem.Encode(&cmd.encodedCert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	_, err = certFile.Write(cmd.encodedCert.Bytes())
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	err = pem.Encode(keyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  func (cmd *setcert) Run(ctx context.Context, f *flag.FlagSet) error {
   146  	if f.NArg() != 1 {
   147  		return flag.ErrHelp
   148  	}
   149  
   150  	key := f.Arg(0)
   151  
   152  	if cmd.cert == "-" {
   153  		b, err := io.ReadAll(os.Stdin)
   154  		if err != nil {
   155  			return err
   156  		}
   157  		cmd.cert = string(b)
   158  	} else if strings.HasPrefix(cmd.cert, "+") {
   159  		if err := cmd.create(key); err != nil {
   160  			return fmt.Errorf("creating certificate: %s", err)
   161  		}
   162  		if cmd.cert == "++" {
   163  			return nil // just generate a cert, useful for testing
   164  		}
   165  		cmd.cert = cmd.encodedCert.String()
   166  	}
   167  
   168  	c, err := cmd.Client()
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	m, err := object.GetExtensionManager(c)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	return m.SetCertificate(ctx, key, cmd.cert)
   179  }