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 OutDir string
|
||||
|
||||
// Needed by all subcommands that generate certificates.
|
||||
// Needed by subcommands that generate certificates.
|
||||
var CommonName string
|
||||
|
||||
// Needed by subcommands that generate leaf certificates.
|
||||
var CAFile string
|
||||
var CAKeyFile string
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(generateCmd)
|
||||
|
||||
@@ -31,7 +35,6 @@ func init() {
|
||||
".",
|
||||
"output directory for artifacts produced by this command, defaults to the current directory",
|
||||
)
|
||||
generateCmd.MarkPersistentFlagRequired("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