From 9d06b4478c9c629478315adcd4d54bbae35c3ea6 Mon Sep 17 00:00:00 2001 From: Slawek Koszewski Date: Sun, 27 Jul 2025 12:49:04 +0200 Subject: [PATCH] Added a simple issue subcommand prototype. --- main.go | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/main.go b/main.go index 1089492..61e129f 100644 --- a/main.go +++ b/main.go @@ -133,6 +133,7 @@ func (c *CAConfig) Validate() error { func main() { var configPath string var overwrite bool + var subject string var rootCmd = &cobra.Command{ Use: "lab-ca", @@ -154,7 +155,21 @@ func main() { initcaCmd.Flags().StringVar(&configPath, "config", "ca_config.hcl", "Path to CA configuration file") initcaCmd.Flags().BoolVar(&overwrite, "overwrite", false, "Allow overwriting existing files") + var issueCmd = &cobra.Command{ + Use: "issue", + Short: "Issue a new client/server certificate", + Run: func(cmd *cobra.Command, args []string) { + handleIssue(configPath, subject, overwrite) + }, + } + + issueCmd.Flags().StringVar(&configPath, "config", "ca_config.hcl", "Path to CA configuration file") + issueCmd.Flags().StringVar(&subject, "subject", "", "Subject Common Name for the certificate (required)") + issueCmd.Flags().BoolVar(&overwrite, "overwrite", false, "Allow overwriting existing files") + issueCmd.MarkFlagRequired("subject") + rootCmd.AddCommand(initcaCmd) + rootCmd.AddCommand(issueCmd) if err := rootCmd.Execute(); err != nil { os.Exit(1) @@ -183,6 +198,84 @@ func handleInitCA(configPath string, overwrite bool) { fmt.Println("CA certificate and key generated.") } +func handleIssue(configPath, subject string, overwrite bool) { + // Load existing CA certificate and key from files + caCertPEM, err := os.ReadFile("ca_cert.pem") + if err != nil { + fmt.Println("Error reading CA certificate file:", err) + return + } + caKeyPEM, err := os.ReadFile("ca_key.pem") + if err != nil { + fmt.Println("Error reading CA key file:", err) + return + } + + // Parse CA cert and key + caCertBlock, _ := pem.Decode(caCertPEM) + if caCertBlock == nil { + fmt.Println("Failed to parse CA certificate PEM") + return + } + caCert, err := x509.ParseCertificate(caCertBlock.Bytes) + if err != nil { + fmt.Println("Failed to parse CA certificate:", err) + return + } + caKeyBlock, _ := pem.Decode(caKeyPEM) + if caKeyBlock == nil { + fmt.Println("Failed to parse CA key PEM") + return + } + caKey, err := x509.ParsePKCS1PrivateKey(caKeyBlock.Bytes) + if err != nil { + fmt.Println("Failed to parse CA private key:", err) + return + } + + // Generate client/server key + priv, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + fmt.Println("Failed to generate private key:", err) + return + } + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + fmt.Println("Failed to generate serial number:", err) + return + } + certTmpl := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: subject, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), // 1 year + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + } + certDER, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, &priv.PublicKey, caKey) + if err != nil { + fmt.Println("Failed to create certificate:", err) + return + } + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) + + certFile := subject + ".crt.pem" + keyFile := subject + ".key.pem" + if err := SavePEM(certFile, certPEM, false, overwrite); err != nil { + fmt.Println("Error saving certificate:", err) + return + } + if err := SavePEM(keyFile, keyPEM, true, overwrite); err != nil { + fmt.Println("Error saving key:", err) + return + } + fmt.Printf("Certificate and key for '%s' generated.\n", subject) +} + func printMainHelp() { fmt.Println("lab-ca - Certificate Authority Utility") fmt.Println() @@ -191,6 +284,7 @@ func printMainHelp() { fmt.Println() fmt.Println("Available commands:") fmt.Println(" initca Generate a new CA certificate and key") + fmt.Println(" issue Issue a new client/server certificate") fmt.Println() fmt.Println("Use 'lab-ca --help' for more information about a command.") }