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 }