feat: Commands to generate server and client certs.
This commit is contained in:
1 parent
abecfc3708
commit
e8503ab141
3 files changed
+244
-2
No files matched your search
@@ -8,9 +8,13 @@ import (
|
|||||||
var Name string
|
var Name string
|
||||||
var OutDir string
|
var OutDir string
|
||||||
|
|
||||||
// Needed by all subcommands that generate certificates.
|
// Needed by subcommands that generate certificates.
|
||||||
var CommonName string
|
var CommonName string
|
||||||
|
|
||||||
|
// Needed by subcommands that generate leaf certificates.
|
||||||
|
var CAFile string
|
||||||
|
var CAKeyFile string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(generateCmd)
|
rootCmd.AddCommand(generateCmd)
|
||||||
|
|
||||||
@@ -31,7 +35,6 @@ func init() {
|
|||||||
".",
|
".",
|
||||||
"output directory for artifacts produced by this command, defaults to the current directory",
|
"output directory for artifacts produced by this command, defaults to the current directory",
|
||||||
)
|
)
|
||||||
generateCmd.MarkPersistentFlagRequired("output-dir")
|
|
||||||
generateCmd.MarkPersistentFlagDirname("output-dir")
|
generateCmd.MarkPersistentFlagDirname("output-dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package hmpki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.handmadecities.com/HMC/shhh/pkg/pki"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generateCmd.AddCommand(generateClientCertCmd)
|
||||||
|
|
||||||
|
generateClientCertCmd.Flags().StringVar(
|
||||||
|
&CAFile,
|
||||||
|
"ca",
|
||||||
|
"",
|
||||||
|
"File for the CA that will sign the leaf",
|
||||||
|
)
|
||||||
|
generateClientCertCmd.MarkFlagRequired("ca")
|
||||||
|
generateClientCertCmd.MarkFlagFilename("ca")
|
||||||
|
|
||||||
|
generateClientCertCmd.Flags().StringVar(
|
||||||
|
&CAKeyFile,
|
||||||
|
"ca-key",
|
||||||
|
"",
|
||||||
|
"File for the CA private key that will sign the leaf",
|
||||||
|
)
|
||||||
|
generateClientCertCmd.MarkFlagRequired("ca-key")
|
||||||
|
generateClientCertCmd.MarkFlagFilename("ca-key")
|
||||||
|
|
||||||
|
generateClientCertCmd.Flags().StringVar(
|
||||||
|
&CommonName,
|
||||||
|
"common-name",
|
||||||
|
"",
|
||||||
|
"Common Name for the CA",
|
||||||
|
)
|
||||||
|
generateClientCertCmd.MarkFlagRequired("common-name")
|
||||||
|
}
|
||||||
|
|
||||||
|
var generateClientCertCmd = &cobra.Command{
|
||||||
|
Use: "client-cert",
|
||||||
|
Short: "Generate a leaf client 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
|
||||||
|
}
|
||||||
|
|
||||||
|
certKey := pki.NewKey()
|
||||||
|
caTmpl := &x509.Certificate{
|
||||||
|
SerialNumber: pki.NewCertSerialNumber(),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: CommonName,
|
||||||
|
},
|
||||||
|
// Leaf certs valid for 1 year.
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().AddDate(1, 0, 0),
|
||||||
|
// This cert is a CA.
|
||||||
|
IsCA: false,
|
||||||
|
// Disambiguate `IsCA`
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||||
|
}
|
||||||
|
|
||||||
|
caBytes, err := x509.CreateCertificate(
|
||||||
|
rand.Reader,
|
||||||
|
caTmpl,
|
||||||
|
// CA is the parent.
|
||||||
|
ca,
|
||||||
|
// The cert embeds its own public key.
|
||||||
|
&certKey.PublicKey,
|
||||||
|
// The CA's private key is the one signing the leaf cert.
|
||||||
|
caKey,
|
||||||
|
)
|
||||||
|
caPEMBytes := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: pki.PEMTypeCertificate,
|
||||||
|
Bytes: caBytes,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Write cert to disk.
|
||||||
|
cafn := path.Join(OutDir, Name+".crt")
|
||||||
|
err = createFileWith(cafn, caPEMBytes, pki.CertFilePerms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Write cert private key to disk.
|
||||||
|
keyfn := path.Join(OutDir, Name+".key")
|
||||||
|
err = createFileWith(keyfn, pki.MarshalKeyToPEM(caKey), pki.PrivateKeyFilePerms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
certKey := pki.NewKey()
|
||||||
|
caTmpl := &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 CA.
|
||||||
|
IsCA: false,
|
||||||
|
// Disambiguate `IsCA`
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
}
|
||||||
|
|
||||||
|
caBytes, err := x509.CreateCertificate(
|
||||||
|
rand.Reader,
|
||||||
|
caTmpl,
|
||||||
|
// CA is the parent.
|
||||||
|
ca,
|
||||||
|
// The cert embeds its own public key.
|
||||||
|
&certKey.PublicKey,
|
||||||
|
// The CA's private key is the one signing the leaf cert.
|
||||||
|
caKey,
|
||||||
|
)
|
||||||
|
caPEMBytes := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: pki.PEMTypeCertificate,
|
||||||
|
Bytes: caBytes,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Write cert to disk.
|
||||||
|
cafn := path.Join(OutDir, Name+".crt")
|
||||||
|
err = createFileWith(cafn, caPEMBytes, pki.CertFilePerms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Write cert private key to disk.
|
||||||
|
keyfn := path.Join(OutDir, Name+".key")
|
||||||
|
err = createFileWith(keyfn, pki.MarshalKeyToPEM(caKey), pki.PrivateKeyFilePerms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in new issue
Block a user