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