dev-tools/v2tool/server/ca.go (119 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
package server
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"log"
"math/big"
"time"
)
// CertificateAuthority is an abstraction for common certificate authority
// unique for process
type CertificateAuthority struct {
caCert *x509.Certificate
privateKey crypto.PrivateKey
caPEM []byte
}
// Pair is a x509 Key/Cert pair
type Pair struct {
Crt []byte
Key []byte
Certificate *tls.Certificate
}
// NewCA creates a new certificate authority capable of generating child certificates
func NewCA() (*CertificateAuthority, error) {
ca := &x509.Certificate{
DNSNames: []string{"localhost"},
SerialNumber: big.NewInt(1653),
Subject: pkix.Name{
Organization: []string{"elastic-fleet"},
CommonName: "localhost",
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
publicKey := &privateKey.PublicKey
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, publicKey, privateKey)
if err != nil {
log.Println("create ca failed", err)
return nil, fmt.Errorf("ca creation failed: %w", err)
}
var pubKeyBytes, privateKeyBytes []byte
certOut := bytes.NewBuffer(pubKeyBytes)
keyOut := bytes.NewBuffer(privateKeyBytes)
// Public key
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: caBytes})
if err != nil {
return nil, fmt.Errorf("signing ca certificate: %w", err)
}
// Private key
err = pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
if err != nil {
return nil, fmt.Errorf("generating ca private key: %w", err)
}
// prepare tls
caPEM := certOut.Bytes()
caTLS, err := tls.X509KeyPair(caPEM, keyOut.Bytes())
if err != nil {
return nil, fmt.Errorf("generating ca x509 pairP: %w", err)
}
caCert, err := x509.ParseCertificate(caTLS.Certificate[0])
if err != nil {
return nil, fmt.Errorf("generating ca private key: %w", err)
}
return &CertificateAuthority{
privateKey: caTLS.PrivateKey,
caCert: caCert,
caPEM: caPEM,
}, nil
}
// GeneratePair generates child certificate
func (c *CertificateAuthority) GeneratePair() (*Pair, error) {
return c.GeneratePairWithName("localhost")
}
// GeneratePairWithName generates child certificate with provided name as the common name.
func (c *CertificateAuthority) GeneratePairWithName(name string) (*Pair, error) {
// Prepare certificate
certTemplate := &x509.Certificate{
SerialNumber: big.NewInt(1658),
DNSNames: []string{name},
Subject: pkix.Name{
Organization: []string{"elastic-fleet"},
CommonName: name,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}
privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
publicKey := &privateKey.PublicKey
// Sign the certificate
certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, c.caCert, publicKey, c.privateKey)
if err != nil {
return nil, fmt.Errorf("signing certificate: %w", err)
}
var pubKeyBytes, privateKeyBytes []byte
certOut := bytes.NewBuffer(pubKeyBytes)
keyOut := bytes.NewBuffer(privateKeyBytes)
// Public key
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
if err != nil {
return nil, fmt.Errorf("generating public key: %w", err)
}
// Private key
err = pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
if err != nil {
return nil, fmt.Errorf("generating private key: %w", err)
}
// TLS Certificate
tlsCert, err := tls.X509KeyPair(certOut.Bytes(), keyOut.Bytes())
if err != nil {
return nil, fmt.Errorf("creating TLS certificate: %w", err)
}
return &Pair{
Crt: certOut.Bytes(),
Key: keyOut.Bytes(),
Certificate: &tlsCert,
}, nil
}
// Crt returns crt cert of certificate authority
func (c *CertificateAuthority) Crt() []byte {
return c.caPEM
}