Files
shhh/internal/clis/hmpki/generate_server_cert.go
jmug 9bdbc79458 fix: Bug where the ca key was being persisted to disk instead of client/server
Also fixes naming of some variables leftover from bad copy-paste.
2026-10-24 00:25:35 -07:00

136 lines
3.1 KiB
Go

package hmpki
import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"net"
"path"
"time"
"git.handmadecities.com/HMC/shhh/pkg/pki"
"github.com/spf13/cobra"
)
var StringIPs []string
var DNSNames []string
func init() {
generateCmd.AddCommand(generateServerCertCmd)
generateServerCertCmd.Flags().StringVar(
&CAFile,
"ca",
"",
"File for the CA that will sign the leaf",
)
generateServerCertCmd.MarkFlagRequired("ca")
generateServerCertCmd.MarkFlagFilename("ca")
generateServerCertCmd.Flags().StringVar(
&CAKeyFile,
"ca-key",
"",
"File for the CA private key that will sign the leaf",
)
generateServerCertCmd.MarkFlagRequired("ca-key")
generateServerCertCmd.MarkFlagFilename("ca-key")
generateServerCertCmd.Flags().StringVar(
&CommonName,
"common-name",
"",
"Common Name for the CA",
)
generateServerCertCmd.MarkFlagRequired("common-name")
generateServerCertCmd.Flags().StringArrayVar(
&StringIPs,
"ips",
nil,
"IP Addresses that the cert will bind",
)
// TODO: Validate them to make sure they're valid.
generateServerCertCmd.Flags().StringArrayVar(
&DNSNames,
"dns-names",
nil,
"DNS names that the cert will bind",
)
// At least one IP or DNS name should be provided.
generateServerCertCmd.MarkFlagsOneRequired("ips", "dns-names")
}
var generateServerCertCmd = &cobra.Command{
Use: "server-cert",
Short: "Generate a leaf server certificate and private key",
RunE: func(cmd *cobra.Command, args []string) error {
ca, err := pki.ParseCertFromFile(CAFile)
if err != nil {
return err
}
caKey, err := pki.ParseKeyFromFile(CAKeyFile)
if err != nil {
return err
}
var ips = make([]net.IP, 0, len(StringIPs))
for _, sip := range StringIPs {
ip := net.ParseIP(sip)
if ip == nil {
return fmt.Errorf("ip %s is invalid", sip)
}
ips = append(ips, ip)
}
crtKey := pki.NewKey()
serverCrtTmpl := &x509.Certificate{
SerialNumber: pki.NewCertSerialNumber(),
Subject: pkix.Name{
CommonName: CommonName,
},
DNSNames: DNSNames,
IPAddresses: ips,
// Leaf certs valid for 1 year.
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0),
// This cert is a leaf, and not a CA.
IsCA: false,
// Disambiguate `IsCA`
BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
serverCrtBytes, err := x509.CreateCertificate(
rand.Reader,
serverCrtTmpl,
// CA is the parent.
ca,
// The cert embeds its own public key.
&crtKey.PublicKey,
// The CA's private key is the one signing the leaf cert.
caKey,
)
serverCrtPEMBytes := pem.EncodeToMemory(&pem.Block{
Type: pki.PEMTypeCertificate,
Bytes: serverCrtBytes,
})
// Write cert to disk.
crtfn := path.Join(OutDir, Name+".crt")
err = createFileWith(crtfn, serverCrtPEMBytes, pki.CertFilePerms)
if err != nil {
return err
}
// Write cert private key to disk.
keyfn := path.Join(OutDir, Name+".key")
err = createFileWith(keyfn, pki.MarshalKeyToPEM(crtKey), pki.PrivateKeyFilePerms)
if err != nil {
return err
}
return nil
},
}