Skip to content

Commit

Permalink
refactor certs code
Browse files Browse the repository at this point in the history
  • Loading branch information
cbodonnell committed Jan 7, 2024
1 parent ebd3350 commit 868ec7c
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 104 deletions.
102 changes: 1 addition & 101 deletions cmd/tfarm/commands/server/certs-client.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
package server

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io"
"math/big"
"os"
"path"
"path/filepath"
"time"

"github.com/cbodonnell/tfarm/pkg/certs"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -47,95 +37,5 @@ func CertsClient(name string) error {
workDir = pwd
}

fmt.Println("Generating client certificate...")

// read the ca.key and ca.crt files from the workDir/tls directory
caKeyFile, err := os.Open(path.Join(workDir, "tls", "ca.key"))
if err != nil {
return fmt.Errorf("error opening CA key file: %s", err)
}
defer caKeyFile.Close()
caKeyPEM, err := io.ReadAll(caKeyFile)
if err != nil {
return fmt.Errorf("error reading CA key file: %s", err)
}
caKeyBlock, _ := pem.Decode(caKeyPEM)
caKey, err := x509.ParsePKCS1PrivateKey(caKeyBlock.Bytes)
if err != nil {
return fmt.Errorf("error parsing CA key: %s", err)
}

caFile, err := os.Open(path.Join(workDir, "tls", "ca.crt"))
if err != nil {
return fmt.Errorf("error opening CA file: %s", err)
}
defer caFile.Close()
caCertPEM, err := io.ReadAll(caFile)
if err != nil {
return fmt.Errorf("error reading CA file: %s", err)
}
caCertBlock, _ := pem.Decode(caCertPEM)
caCert, err := x509.ParseCertificate(caCertBlock.Bytes)
if err != nil {
return fmt.Errorf("error parsing CA certificate: %s", err)
}

// Generate a new client certificate/key pair
clientTemplate := x509.Certificate{
SerialNumber: big.NewInt(3),
Subject: pkix.Name{
CommonName: name,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0), // Valid for 1 year
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
SignatureAlgorithm: x509.SHA256WithRSA,
Issuer: caCert.Subject,
BasicConstraintsValid: true,
}
clientKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return fmt.Errorf("error generating client key: %s", err)
}

clientCert, err := x509.CreateCertificate(rand.Reader, &clientTemplate, caCert, &clientKey.PublicKey, caKey)
if err != nil {
return fmt.Errorf("error generating client certificate: %s", err)
}

// make sure the tls/clients directory exists
err = os.MkdirAll(path.Join(workDir, "tls", "clients", name), 0755)
if err != nil {
return fmt.Errorf("error creating clients directory: %s", err)
}

certBuff := bytes.NewBuffer(nil)
if err := pem.Encode(certBuff, &pem.Block{Type: "CERTIFICATE", Bytes: clientCert}); err != nil {
return fmt.Errorf("error pem encoding client certificate: %s", err)
}

keyBuff := bytes.NewBuffer(nil)
if err := pem.Encode(keyBuff, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientKey)}); err != nil {
return fmt.Errorf("error pem encoding client key: %s", err)
}

client := &certs.Client{
CA: caCertPEM,
Cert: certBuff.Bytes(),
Key: keyBuff.Bytes(),
}
if err := client.SaveToFile(path.Join(workDir, "tls", "clients", name, "client.json")); err != nil {
return fmt.Errorf("error saving client to file: %s", err)
}

absPath, err := filepath.Abs(path.Join(workDir, "tls", "clients", name))
if err != nil {
return fmt.Errorf("error getting absolute path of client certificate: %s", err)
}

fmt.Println("Client certificate and key written to:")
fmt.Println(" ", path.Join(absPath, "client.json"))

return nil
return certs.GenerateClientCerts(path.Join(workDir, "tls"), name)
}
2 changes: 1 addition & 1 deletion cmd/tfarm/commands/server/certs-regenerate.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ func CertsRegenerate() error {
workDir = pwd
}

return certs.GenerateCerts(path.Join(workDir, "tls"))
return certs.GenerateServerCerts(path.Join(workDir, "tls"))
}
2 changes: 1 addition & 1 deletion cmd/tfarm/commands/server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func Start(port int, cfg config.ClientCommonConf) error {
if _, err := os.Stat(tlsDir); err != nil {
if os.IsNotExist(err) {
log.Println("tls directory not found, generating certificates")
if err := certs.GenerateCerts(tlsDir); err != nil {
if err := certs.GenerateServerCerts(tlsDir); err != nil {
return fmt.Errorf("error generating tls certificates: %s", err)
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion pkg/certs/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

// TODO: Split this into a CA, server, and client cert generator
func GenerateCerts(dir string) error {
func GenerateServerCerts(dir string) error {
fmt.Println("Generating CA certificate...")

// Generate a new CA certificate
Expand Down
104 changes: 104 additions & 0 deletions pkg/certs/client.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package certs

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"math/big"
"os"
"path"
"path/filepath"
"time"
)

type Client struct {
Expand Down Expand Up @@ -67,3 +77,97 @@ func (c *Client) SaveToFile(path string) error {
}
return nil
}

func GenerateClientCerts(dir string, name string) error {
fmt.Println("Generating client certificate...")

// read the ca.key and ca.crt files from the workDir/tls directory
caKeyFile, err := os.Open(path.Join(dir, "ca.key"))
if err != nil {
return fmt.Errorf("error opening CA key file: %s", err)
}
defer caKeyFile.Close()
caKeyPEM, err := io.ReadAll(caKeyFile)
if err != nil {
return fmt.Errorf("error reading CA key file: %s", err)
}
caKeyBlock, _ := pem.Decode(caKeyPEM)
caKey, err := x509.ParsePKCS1PrivateKey(caKeyBlock.Bytes)
if err != nil {
return fmt.Errorf("error parsing CA key: %s", err)
}

caFile, err := os.Open(path.Join(dir, "ca.crt"))
if err != nil {
return fmt.Errorf("error opening CA file: %s", err)
}
defer caFile.Close()
caCertPEM, err := io.ReadAll(caFile)
if err != nil {
return fmt.Errorf("error reading CA file: %s", err)
}
caCertBlock, _ := pem.Decode(caCertPEM)
caCert, err := x509.ParseCertificate(caCertBlock.Bytes)
if err != nil {
return fmt.Errorf("error parsing CA certificate: %s", err)
}

// Generate a new client certificate/key pair
clientTemplate := x509.Certificate{
SerialNumber: big.NewInt(3),
Subject: pkix.Name{
CommonName: name,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0), // Valid for 1 year
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
SignatureAlgorithm: x509.SHA256WithRSA,
Issuer: caCert.Subject,
BasicConstraintsValid: true,
}
clientKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return fmt.Errorf("error generating client key: %s", err)
}

clientCert, err := x509.CreateCertificate(rand.Reader, &clientTemplate, caCert, &clientKey.PublicKey, caKey)
if err != nil {
return fmt.Errorf("error generating client certificate: %s", err)
}

// make sure the tls/clients directory exists
err = os.MkdirAll(path.Join(dir, "clients", name), 0755)
if err != nil {
return fmt.Errorf("error creating clients directory: %s", err)
}

certBuff := bytes.NewBuffer(nil)
if err := pem.Encode(certBuff, &pem.Block{Type: "CERTIFICATE", Bytes: clientCert}); err != nil {
return fmt.Errorf("error pem encoding client certificate: %s", err)
}

keyBuff := bytes.NewBuffer(nil)
if err := pem.Encode(keyBuff, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientKey)}); err != nil {
return fmt.Errorf("error pem encoding client key: %s", err)
}

client := &Client{
CA: caCertPEM,
Cert: certBuff.Bytes(),
Key: keyBuff.Bytes(),
}
if err := client.SaveToFile(path.Join(dir, "clients", name, "client.json")); err != nil {
return fmt.Errorf("error saving client to file: %s", err)
}

absPath, err := filepath.Abs(path.Join(dir, "clients", name))
if err != nil {
return fmt.Errorf("error getting absolute path of client certificate: %s", err)
}

fmt.Println("Client certificate and key written to:")
fmt.Println(" ", path.Join(absPath, "client.json"))

return nil
}

0 comments on commit 868ec7c

Please sign in to comment.