136 lines
3.1 KiB
Go
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
|
|
},
|
|
}
|