108 lines
2.6 KiB
Go
108 lines
2.6 KiB
Go
package hmpki
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"errors"
|
|
"math"
|
|
"math/big"
|
|
"os"
|
|
"time"
|
|
|
|
"git.handmadecities.com/HMC/shhh/pkg/pki"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func init() {
|
|
generateCmd.AddCommand(generateCACmd)
|
|
|
|
generateCACmd.Flags().StringVar(
|
|
&KeyFile,
|
|
"key-file",
|
|
"",
|
|
"File that contains the private key that will be associated to the CA",
|
|
)
|
|
generateCACmd.MarkFlagRequired("key-file")
|
|
generateCACmd.MarkFlagFilename("key-file")
|
|
}
|
|
|
|
var generateCACmd = &cobra.Command{
|
|
Use: "ca",
|
|
Short: "Generate a Certificate Authority",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
// Read key from KeyFile.
|
|
keyPemBytes, err := os.ReadFile(KeyFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
privBlock, _ := pem.Decode(keyPemBytes)
|
|
if privBlock == nil || privBlock.Type != pki.PEMTypePrivateKey {
|
|
return errors.New("Invalid pem key file, or first block is not a private key")
|
|
}
|
|
rawKey, err := x509.ParsePKCS8PrivateKey(privBlock.Bytes)
|
|
|
|
key, ok := rawKey.(*ecdsa.PrivateKey)
|
|
if !ok {
|
|
return errors.New("key file did not contain an ecdsa private key")
|
|
}
|
|
|
|
// Cryptographically random 64bit number as the serial.
|
|
sn, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
|
|
if err != nil {
|
|
errors.New("Could not generate a ca serial number")
|
|
}
|
|
caTmpl := &x509.Certificate{
|
|
SerialNumber: sn,
|
|
Subject: pkix.Name{
|
|
// TODO: Accept common name as a flag.
|
|
CommonName: "Some Common Name",
|
|
},
|
|
// Valid starting now and for 10 years.
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().AddDate(10, 0, 0),
|
|
// This cert is a CA.
|
|
IsCA: true,
|
|
// No intermeddiate CAs, just one root that can issue leaf certs.
|
|
MaxPathLen: 0,
|
|
MaxPathLenZero: true,
|
|
// Disambiguate `IsCA` and path len settings from their zero value.
|
|
BasicConstraintsValid: true,
|
|
// Only allow to sign new certs, and cert revokation lists.
|
|
// Can't be used as a server or client cert itself.
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
}
|
|
caBytes, err := x509.CreateCertificate(
|
|
rand.Reader,
|
|
// Same template and parent, i.e. self-signed.
|
|
caTmpl,
|
|
caTmpl,
|
|
&key.PublicKey,
|
|
key,
|
|
)
|
|
caPEMBytes := pem.EncodeToMemory(&pem.Block{
|
|
Type: pki.PEMTypeCertificate,
|
|
Bytes: caBytes,
|
|
})
|
|
|
|
caf, err := os.OpenFile(
|
|
Output,
|
|
// Write only, and fail if it alreay exists.
|
|
os.O_WRONLY|os.O_CREATE|os.O_EXCL,
|
|
pki.CertFilePerms,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer caf.Close()
|
|
if _, err = caf.Write(caPEMBytes); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write ca.crt to Output
|
|
return nil
|
|
},
|
|
}
|