Skip to content

Commit

Permalink
self signed cert done
Browse files Browse the repository at this point in the history
  • Loading branch information
kayra1 committed Apr 5, 2024
1 parent 1554c53 commit 9ffefc2
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 56 deletions.
60 changes: 14 additions & 46 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,36 @@ package server

import (
"crypto/tls"
"encoding/pem"
"errors"
"net/http"
"time"

"github.com/canonical/gocert/internal/certificates"
)

// decodeCertificateAndPK takes in two PEM strings and decodes them into bytes
func decodeCertificateAndPK(certificate, key string) ([]byte, []byte, error) {
block, _ := pem.Decode([]byte(certificate))
if block == nil {
return nil, nil, errors.New("PEM Certificate string not found or malformed")
}
if block.Type != "CERTIFICATE" {
return nil, nil, errors.New("given PEM string not a certificate")
}
certBytes := block.Bytes

block, _ = pem.Decode([]byte(key))
if block == nil {
return nil, nil, errors.New("PEM Private Key string not found or malformed")
}
if block.Type != "RSA PRIVATE KEY" {
return nil, nil, errors.New("given PEM string not a private key")
}
pkBytes := block.Bytes
return certBytes, pkBytes, nil
}

// formatServerCertificates takes in a certificate and a private key and converts it into a
// format usable by net/http
func formatServerCertificates(certificate, key string) (tls.Certificate, error) {
var serverCerts tls.Certificate
var serverCert []byte
var serverPK []byte
var err error
if certificate != "" && key != "" {
serverCert, serverPK, err = decodeCertificateAndPK(certificate, key)
// loadServerCertificates takes in a certificate and a private key and determines
// whether to use self signed or given certificates, then returns it in a format
// expected by the server
func loadServerCertificates(certificate, key string) (*tls.Certificate, error) {
if certificate == "" || key == "" {
caCertPEM, caPrivateKeyPEM, err := certificates.GenerateCACertificate()
if err != nil {
return serverCerts, err
return nil, err
}
} else {
caCert, caPK, err := certificates.GenerateCACertificate()
certificate, key, err = certificates.GenerateSelfSignedCertificate(caCertPEM, caPrivateKeyPEM)
if err != nil {
return serverCerts, err
}
serverCert, serverPK, err = certificates.GenerateSelfSignedCertificate(caCert, caPK)
if err != nil {
return serverCerts, err
return nil, err
}
}
serverCerts, err = tls.X509KeyPair(serverCert, serverPK)
serverCerts, err := tls.X509KeyPair([]byte(certificate), []byte(key))
if err != nil {
return serverCerts, err
return nil, err
}
return serverCerts, nil
return &serverCerts, nil
}

// NewServer creates a new http server with handlers that Go can start listening to
func NewServer(version int, certificate, key string) (*http.Server, error) {
serverCerts, err := formatServerCertificates(certificate, key)
serverCerts, err := loadServerCertificates(certificate, key)
if err != nil {
return nil, err
}
Expand All @@ -75,7 +43,7 @@ func NewServer(version int, certificate, key string) (*http.Server, error) {
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{serverCerts},
Certificates: []tls.Certificate{*serverCerts},
},
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/gocert/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func main() {
os.Exit(1)
}
fmt.Fprintf(os.Stdout, "Starting server at %s", srv.Addr)
if err := srv.ListenAndServe(); err != nil {
if err := srv.ListenAndServeTLS("", ""); err != nil {
fmt.Fprintf(os.Stderr, "Server ran into error: %s", err)
os.Exit(1)
}
Expand Down
92 changes: 83 additions & 9 deletions internal/certificates/certificates.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package certificates

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"math/big"
"net"
"time"
)

// GenerateCACertificate generates a one time discardable root CA for the running GoCert webserver
func GenerateCACertificate() (*x509.Certificate, *rsa.PrivateKey, error) {
func GenerateCACertificate() (string, string, error) {
caCert := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{
Expand All @@ -27,16 +30,42 @@ func GenerateCACertificate() (*x509.Certificate, *rsa.PrivateKey, error) {
}
caPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
return "", "", err
}
return caCert, caPrivateKey, nil

caBytes, err := x509.CreateCertificate(rand.Reader, caCert, caCert, &caPrivateKey.PublicKey, caPrivateKey)
if err != nil {
return "", "", err
}

caCertPEM := new(bytes.Buffer)
pem.Encode(caCertPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
})

caPrivateKeyPEM := new(bytes.Buffer)
pem.Encode(caPrivateKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(caPrivateKey),
})
return caCertPEM.String(), caPrivateKeyPEM.String(), nil
}

// GenerateSelfSignedCertificate will create a certificate and pk for GoCert coming from a rootCA
// This certificate and PK is not saved anywhere.
// This certificate and PK should either be saved somewhere, or a real certificate should be provided to GoCert
func GenerateSelfSignedCertificate(caCert *x509.Certificate, caPrivateKey *rsa.PrivateKey) ([]byte, []byte, error) {
cert := &x509.Certificate{
func GenerateSelfSignedCertificate(caCertPEM, caPrivateKeyPEM string) (string, string, error) {
caCert, err := ParseCertificate(caCertPEM)
if err != nil {
return "", "", nil
}
caPrivateKey, err := ParsePKCS1PrivateKey(caPrivateKeyPEM)
if err != nil {
return "", "", nil
}

certTemplate := &x509.Certificate{
SerialNumber: big.NewInt(1658),
Subject: pkix.Name{
Organization: []string{"Canonical, INC."},
Expand All @@ -51,11 +80,56 @@ func GenerateSelfSignedCertificate(caCert *x509.Certificate, caPrivateKey *rsa.P
}
certPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
return "", "", err
}
certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, &caCert, &certPrivateKey.PublicKey, &caPrivateKey)
if err != nil {
return "", "", err
}
certPEM := new(bytes.Buffer)
pem.Encode(certPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
})

certPrivateKeyPEM := new(bytes.Buffer)
pem.Encode(certPrivateKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(certPrivateKey),
})
return certPEM.String(), certPrivateKeyPEM.String(), nil
}

// ParseCertificate parses a PEM string into a native x509.Certificate object
func ParseCertificate(certPEM string) (x509.Certificate, error) {
cert := &x509.Certificate{}
block, _ := pem.Decode([]byte(certPEM))
if block == nil {
return *cert, errors.New("PEM Certificate string not found or malformed")
}
if block.Type != "CERTIFICATE" {
return *cert, errors.New("given PEM string not a certificate")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return *cert, err
}
return *cert, nil
}

// ParsePrivateKey parses a PEM private key string into a native rsa.PrivateKey object
func ParsePKCS1PrivateKey(pkPEM string) (rsa.PrivateKey, error) {
pk := &rsa.PrivateKey{}
block, _ := pem.Decode([]byte(pkPEM))
if block == nil {
return *pk, errors.New("PEM private key string not found or malformed")
}
if block.Type != "RSA PRIVATE KEY" {
return *pk, errors.New("given PEM string not an rsa private key")
}
certBytes, err := x509.CreateCertificate(rand.Reader, cert, caCert, &certPrivateKey.PublicKey, caPrivateKey)
pk, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, nil, err
return *pk, err
}
return certBytes, x509.MarshalPKCS1PrivateKey(certPrivateKey), nil
return *pk, nil
}

0 comments on commit 9ffefc2

Please sign in to comment.