devroxy

VHost Proxy Server for localhost
git clone git://git.lair.cx/devroxy
Log | Files | Refs | README

ca.go (2685B)


      1 package certificates
      2 
      3 import (
      4 	"crypto/rsa"
      5 	"crypto/tls"
      6 	"crypto/x509"
      7 	"fmt"
      8 	"os"
      9 	"path/filepath"
     10 
     11 	"github.com/rs/zerolog/log"
     12 )
     13 
     14 const (
     15 	certFile = "cert.pem"
     16 	keyFile  = "key.pem"
     17 )
     18 
     19 // CA contains root certificate and key, and generates certificate.
     20 type CA struct {
     21 	wd       string
     22 	rootCert *x509.Certificate
     23 	rootKey  *rsa.PrivateKey
     24 }
     25 
     26 func NewCA(wd, cert, key string) (*CA, error) {
     27 	var (
     28 		rootKey  *rsa.PrivateKey
     29 		rootCert *x509.Certificate
     30 		err      error
     31 	)
     32 
     33 	if _, err = os.Stat(key); err != nil {
     34 		if !os.IsNotExist(err) {
     35 			return nil, err
     36 		}
     37 
     38 		rootKey, err = generateKey(key)
     39 		if err != nil {
     40 			return nil, fmt.Errorf("failed to generate private key: %w", err)
     41 		}
     42 	} else {
     43 		rootKey, err = readKey(key)
     44 		if err != nil {
     45 			return nil, fmt.Errorf("failed to load private key: %w", err)
     46 		}
     47 	}
     48 
     49 	if _, err = os.Stat(cert); err != nil {
     50 		if !os.IsNotExist(err) {
     51 			return nil, err
     52 		}
     53 
     54 		rootCert, err = generateRootCertificate(cert, rootKey)
     55 		if err != nil {
     56 			return nil, fmt.Errorf("failed to generate private key: %w", err)
     57 		}
     58 	} else {
     59 		rootCert, err = readCertificate(cert)
     60 		if err != nil {
     61 			return nil, fmt.Errorf("certificate load error: %w", err)
     62 		}
     63 	}
     64 
     65 	if err := checkCertificate(rootCert, rootKey); err != nil {
     66 		return nil, fmt.Errorf("invalid certificate: %w", err)
     67 	}
     68 
     69 	return &CA{
     70 		wd:       wd,
     71 		rootCert: rootCert,
     72 		rootKey:  rootKey,
     73 	}, nil
     74 }
     75 
     76 // GenerateCertificate generates new certificate, writes to `(wd)/certs/(host)/`
     77 func (ca *CA) GenerateCertificate(name string) (tls.Certificate, error) {
     78 	err := os.MkdirAll(filepath.Join(ca.wd, name), 0755)
     79 	if err != nil {
     80 		return tls.Certificate{}, err
     81 	}
     82 
     83 	key, err := generateKey(filepath.Join(ca.wd, name, keyFile))
     84 	if err != nil {
     85 		return tls.Certificate{}, err
     86 	}
     87 
     88 	cert, err := generateCertificate(
     89 		filepath.Join(ca.wd, name, certFile),
     90 		name,
     91 		key,
     92 		ca.rootCert,
     93 		ca.rootKey,
     94 	)
     95 	if err != nil {
     96 		return tls.Certificate{}, err
     97 	}
     98 
     99 	return tls.Certificate{
    100 		Certificate: [][]byte{cert},
    101 		PrivateKey:  key,
    102 	}, nil
    103 }
    104 
    105 // GetCertificate returns SSL certificate if exists, otherwise generate new one.
    106 func (ca *CA) GetCertificate(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
    107 	cert, err := tls.LoadX509KeyPair(
    108 		filepath.Join(ca.wd, info.ServerName, certFile),
    109 		filepath.Join(ca.wd, info.ServerName, keyFile),
    110 	)
    111 	if os.IsNotExist(err) {
    112 		log.Info().
    113 			Str("host", info.ServerName).
    114 			Msg("certificate not found; generating new one...")
    115 		cert, err = ca.GenerateCertificate(info.ServerName)
    116 	}
    117 	if err != nil {
    118 		log.Err(err).
    119 			Str("host", info.ServerName).
    120 			Msg("failed to get certificate")
    121 		return nil, err
    122 	}
    123 
    124 	return &cert, nil
    125 }