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