devroxy

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

utils.go (4208B)


      1 package certificates
      2 
      3 import (
      4 	"bytes"
      5 	"crypto/rand"
      6 	"crypto/rsa"
      7 	"crypto/x509"
      8 	"crypto/x509/pkix"
      9 	"encoding/pem"
     10 	"errors"
     11 	"fmt"
     12 	"math"
     13 	"math/big"
     14 	"os"
     15 	"time"
     16 
     17 	"github.com/valyala/bytebufferpool"
     18 )
     19 
     20 func readCertificate(filename string) (*x509.Certificate, error) {
     21 	data, err := os.ReadFile(filename)
     22 	if err != nil {
     23 		return nil, err
     24 	}
     25 
     26 	block, _ := pem.Decode(data)
     27 	if block == nil || block.Type != "CERTIFICATE" {
     28 		return nil, errors.New("invalid certificate")
     29 	}
     30 
     31 	cert, err := x509.ParseCertificate(block.Bytes)
     32 	if err != nil {
     33 		return nil, err
     34 	}
     35 
     36 	return cert, nil
     37 }
     38 
     39 func readKey(file string) (*rsa.PrivateKey, error) {
     40 	data, err := os.ReadFile(file)
     41 	if err != nil {
     42 		return nil, err
     43 	}
     44 
     45 	block, _ := pem.Decode(data)
     46 	if block.Type != "RSA PRIVATE KEY" {
     47 		return nil, errors.New("only rsa private key is supported")
     48 	}
     49 
     50 	return x509.ParsePKCS1PrivateKey(block.Bytes)
     51 }
     52 
     53 func generateCertificateImpl(
     54 	filename string,
     55 	template *x509.Certificate,
     56 	key *rsa.PrivateKey,
     57 	parent *x509.Certificate,
     58 	rootKey *rsa.PrivateKey,
     59 ) ([]byte, error) {
     60 	if parent == nil {
     61 		parent = template
     62 	}
     63 
     64 	data, err := x509.CreateCertificate(
     65 		rand.Reader,
     66 		template,
     67 		parent,
     68 		key.Public(),
     69 		rootKey,
     70 	)
     71 	if err != nil {
     72 		return nil, err
     73 	}
     74 
     75 	buf := bytebufferpool.Get()
     76 	defer bytebufferpool.Put(buf)
     77 
     78 	err = pem.Encode(buf, &pem.Block{
     79 		Type:  "CERTIFICATE",
     80 		Bytes: data,
     81 	})
     82 	if err != nil {
     83 		return nil, fmt.Errorf("failed to encode certificate: %w", err)
     84 	}
     85 
     86 	err = os.WriteFile(filename, buf.B, 0600)
     87 	if err != nil {
     88 		return nil, err
     89 	}
     90 
     91 	return data, nil
     92 }
     93 
     94 func generateCertificate(
     95 	filename string,
     96 	domain string,
     97 	key *rsa.PrivateKey,
     98 	parent *x509.Certificate,
     99 	rootKey *rsa.PrivateKey,
    100 ) ([]byte, error) {
    101 	serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
    102 	if err != nil {
    103 		return nil, err
    104 	}
    105 
    106 	return generateCertificateImpl(
    107 		filename,
    108 		&x509.Certificate{
    109 			DNSNames:              []string{domain},
    110 			Subject:               pkix.Name{CommonName: domain},
    111 			SerialNumber:          serial,
    112 			NotBefore:             time.Now(),
    113 			NotAfter:              time.Now().AddDate(2, 0, 30),
    114 			KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
    115 			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
    116 			BasicConstraintsValid: true,
    117 		},
    118 		key,
    119 		parent,
    120 		rootKey,
    121 	)
    122 }
    123 
    124 func generateRootCertificate(filename string, key *rsa.PrivateKey) (*x509.Certificate, error) {
    125 	serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
    126 	if err != nil {
    127 		return nil, err
    128 	}
    129 
    130 	template := &x509.Certificate{
    131 		Subject: pkix.Name{
    132 			CommonName: "Devroxy Internal CA ",
    133 		},
    134 		SerialNumber:          serial,
    135 		NotBefore:             time.Now(),
    136 		NotAfter:              time.Now().AddDate(2, 0, 30),
    137 		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
    138 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
    139 		BasicConstraintsValid: true,
    140 		IsCA:                  true,
    141 	}
    142 
    143 	data, err := generateCertificateImpl(
    144 		filename,
    145 		template,
    146 		key,
    147 		nil,
    148 		key,
    149 	)
    150 	if err != nil {
    151 		return nil, err
    152 	}
    153 
    154 	return x509.ParseCertificate(data)
    155 }
    156 
    157 func generateKey(filename string) (*rsa.PrivateKey, error) {
    158 	key, err := rsa.GenerateKey(rand.Reader, 2048)
    159 	if err != nil {
    160 		return nil, err
    161 	}
    162 	err = writeKey(filename, key)
    163 	if err != nil {
    164 		return nil, err
    165 	}
    166 	return key, nil
    167 }
    168 
    169 func writeKey(file string, key *rsa.PrivateKey) error {
    170 	data := x509.MarshalPKCS1PrivateKey(key)
    171 
    172 	buf := bytebufferpool.Get()
    173 	defer bytebufferpool.Put(buf)
    174 
    175 	err := pem.Encode(buf, &pem.Block{
    176 		Type:  "RSA PRIVATE KEY",
    177 		Bytes: data,
    178 	})
    179 	if err != nil {
    180 		return err
    181 	}
    182 
    183 	return os.WriteFile(file, buf.Bytes(), 0600)
    184 }
    185 
    186 func checkCertificate(cert *x509.Certificate, key *rsa.PrivateKey) error {
    187 	if cert.PublicKeyAlgorithm != x509.RSA {
    188 		return errors.New("only rsa certificate is supported")
    189 	}
    190 
    191 	dataA := x509.MarshalPKCS1PublicKey(cert.PublicKey.(*rsa.PublicKey))
    192 	dataB := x509.MarshalPKCS1PublicKey(&key.PublicKey)
    193 
    194 	if !bytes.Equal(dataA, dataB) {
    195 		return errors.New("public key not match")
    196 	}
    197 
    198 	return nil
    199 }