1package ca 2 3import ( 4 "crypto/ecdsa" 5 "crypto/elliptic" 6 "crypto/rand" 7 "crypto/x509" 8 "crypto/x509/pkix" 9 "encoding/pem" 10 "math/big" 11 "os" 12 "time" 13 14 "github.com/grafana/dskit/runutil" 15) 16 17type CA struct { 18 key *ecdsa.PrivateKey 19 cert *x509.Certificate 20 serial *big.Int 21} 22 23func New(name string) *CA { 24 key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) 25 if err != nil { 26 panic(err) 27 } 28 29 return &CA{ 30 key: key, 31 cert: &x509.Certificate{ 32 SerialNumber: big.NewInt(1), 33 Subject: pkix.Name{ 34 Organization: []string{name}, 35 }, 36 NotBefore: time.Now(), 37 NotAfter: time.Now().Add(time.Hour * 24 * 180), 38 39 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 40 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, 41 BasicConstraintsValid: true, 42 IsCA: true, 43 }, 44 serial: big.NewInt(2), 45 } 46 47} 48 49func writeExclusivePEMFile(path, marker string, mode os.FileMode, data []byte) (err error) { 50 f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, mode) 51 if err != nil { 52 return err 53 } 54 defer runutil.CloseWithErrCapture(&err, f, "write pem file") 55 56 if err := pem.Encode(f, &pem.Block{Type: marker, Bytes: data}); err != nil { 57 return err 58 } 59 return nil 60} 61 62func (ca *CA) WriteCACertificate(path string) error { 63 derBytes, err := x509.CreateCertificate(rand.Reader, ca.cert, ca.cert, ca.key.Public(), ca.key) 64 if err != nil { 65 return err 66 } 67 68 return writeExclusivePEMFile(path, "CERTIFICATE", 0644, derBytes) 69} 70 71func (ca *CA) WriteCertificate(template *x509.Certificate, certPath string, keyPath string) error { 72 key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) 73 if err != nil { 74 return err 75 } 76 77 keyBytes, err := x509.MarshalECPrivateKey(key) 78 if err != nil { 79 return err 80 } 81 82 if err := writeExclusivePEMFile(keyPath, "PRIVATE KEY", 0600, keyBytes); err != nil { 83 return err 84 } 85 86 template.IsCA = false 87 template.NotBefore = time.Now() 88 if template.NotAfter.IsZero() { 89 template.NotAfter = time.Now().Add(time.Hour * 24 * 180) 90 } 91 template.SerialNumber = ca.serial.Add(ca.serial, big.NewInt(1)) 92 93 derBytes, err := x509.CreateCertificate(rand.Reader, template, ca.cert, key.Public(), ca.key) 94 if err != nil { 95 return err 96 } 97 98 return writeExclusivePEMFile(certPath, "CERTIFICATE", 0644, derBytes) 99} 100