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 }, }