mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	 7267d6ee56
			
		
	
	7267d6ee56
	
	
	
		
			
			Vault's new TLS devvault mode has two nits with certificate
construction:
 1. The CA doesn't need to include any SANs, as these aren't checked.
    Technically this means the CA could be reused as a leaf certificate
    for the one specified IP SAN, which is less desirable.
 2. Add hostname to SANs in addition to CNs. This is a best practice, as
    (when the CN is a hostname), it is preferable to have everything in
    SANs as well.
Neither of these are major changes.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
		
	
		
			
				
	
	
		
			174 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package server
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"crypto"
 | |
| 	"crypto/ecdsa"
 | |
| 	"crypto/elliptic"
 | |
| 	"crypto/rand"
 | |
| 	"crypto/x509"
 | |
| 	"crypto/x509/pkix"
 | |
| 	"encoding/pem"
 | |
| 	"fmt"
 | |
| 	"math/big"
 | |
| 	"net"
 | |
| 	"os"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/sdk/helper/certutil"
 | |
| )
 | |
| 
 | |
| type CaCert struct {
 | |
| 	PEM      string
 | |
| 	Template *x509.Certificate
 | |
| 	Signer   crypto.Signer
 | |
| }
 | |
| 
 | |
| // GenerateCert creates a new leaf cert from provided CA template and signer
 | |
| func GenerateCert(caCertTemplate *x509.Certificate, caSigner crypto.Signer) (string, string, error) {
 | |
| 	// Create the private key
 | |
| 	signer, keyPEM, err := privateKey()
 | |
| 	if err != nil {
 | |
| 		return "", "", fmt.Errorf("error generating private key for server certificate: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// The serial number for the cert
 | |
| 	sn, err := serialNumber()
 | |
| 	if err != nil {
 | |
| 		return "", "", fmt.Errorf("error generating serial number: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	signerKeyId, err := certutil.GetSubjKeyID(signer)
 | |
| 	if err != nil {
 | |
| 		return "", "", fmt.Errorf("error getting subject key id from key: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	hostname, err := os.Hostname()
 | |
| 	if err != nil {
 | |
| 		return "", "", fmt.Errorf("error getting hostname: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if hostname == "" {
 | |
| 		hostname = "localhost"
 | |
| 	}
 | |
| 
 | |
| 	// Create the leaf cert
 | |
| 	template := x509.Certificate{
 | |
| 		SerialNumber:   sn,
 | |
| 		Subject:        pkix.Name{CommonName: hostname},
 | |
| 		KeyUsage:       x509.KeyUsageDigitalSignature,
 | |
| 		ExtKeyUsage:    []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
 | |
| 		NotAfter:       time.Now().Add(365 * 24 * time.Hour),
 | |
| 		NotBefore:      time.Now().Add(-1 * time.Minute),
 | |
| 		IPAddresses:    []net.IP{net.ParseIP("127.0.0.1")},
 | |
| 		DNSNames:       []string{"localhost", "localhost4", "localhost6", "localhost.localdomain"},
 | |
| 		AuthorityKeyId: caCertTemplate.AuthorityKeyId,
 | |
| 		SubjectKeyId:   signerKeyId,
 | |
| 	}
 | |
| 
 | |
| 	// Only add our hostname to SANs if it isn't found.
 | |
| 	foundHostname := false
 | |
| 	for _, value := range template.DNSNames {
 | |
| 		if value == hostname {
 | |
| 			foundHostname = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if !foundHostname {
 | |
| 		template.DNSNames = append(template.DNSNames, hostname)
 | |
| 	}
 | |
| 
 | |
| 	bs, err := x509.CreateCertificate(
 | |
| 		rand.Reader, &template, caCertTemplate, signer.Public(), caSigner)
 | |
| 	if err != nil {
 | |
| 		return "", "", fmt.Errorf("error creating server certificate: %v", err)
 | |
| 	}
 | |
| 	var buf bytes.Buffer
 | |
| 	err = pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: bs})
 | |
| 	if err != nil {
 | |
| 		return "", "", fmt.Errorf("error encoding server certificate: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	return buf.String(), keyPEM, nil
 | |
| }
 | |
| 
 | |
| // GenerateCA generates a new self-signed CA cert and returns a
 | |
| // CaCert struct containing the PEM encoded cert,
 | |
| // X509 Certificate Template, and crypto.Signer
 | |
| func GenerateCA() (*CaCert, error) {
 | |
| 	// Create the private key we'll use for this CA cert.
 | |
| 	signer, _, err := privateKey()
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("error generating private key for CA: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	signerKeyId, err := certutil.GetSubjKeyID(signer)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("error getting subject key id from key: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// The serial number for the cert
 | |
| 	sn, err := serialNumber()
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("error generating serial number: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create the CA cert
 | |
| 	template := x509.Certificate{
 | |
| 		SerialNumber:          sn,
 | |
| 		Subject:               pkix.Name{CommonName: "Vault Dev CA"},
 | |
| 		BasicConstraintsValid: true,
 | |
| 		KeyUsage:              x509.KeyUsageCertSign,
 | |
| 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
 | |
| 		IsCA:                  true,
 | |
| 		NotAfter:              time.Now().Add(10 * 365 * 24 * time.Hour),
 | |
| 		NotBefore:             time.Now().Add(-1 * time.Minute),
 | |
| 		AuthorityKeyId:        signerKeyId,
 | |
| 		SubjectKeyId:          signerKeyId,
 | |
| 	}
 | |
| 
 | |
| 	bs, err := x509.CreateCertificate(
 | |
| 		rand.Reader, &template, &template, signer.Public(), signer)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("error creating CA certificate: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	var buf bytes.Buffer
 | |
| 	err = pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: bs})
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("error encoding CA certificate: %v", err)
 | |
| 	}
 | |
| 	return &CaCert{
 | |
| 		PEM:      buf.String(),
 | |
| 		Template: &template,
 | |
| 		Signer:   signer,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // privateKey returns a new ECDSA-based private key. Both a crypto.Signer
 | |
| // and the key in PEM format are returned.
 | |
| func privateKey() (crypto.Signer, string, error) {
 | |
| 	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | |
| 	if err != nil {
 | |
| 		return nil, "", err
 | |
| 	}
 | |
| 
 | |
| 	bs, err := x509.MarshalECPrivateKey(pk)
 | |
| 	if err != nil {
 | |
| 		return nil, "", err
 | |
| 	}
 | |
| 
 | |
| 	var buf bytes.Buffer
 | |
| 	err = pem.Encode(&buf, &pem.Block{Type: "EC PRIVATE KEY", Bytes: bs})
 | |
| 	if err != nil {
 | |
| 		return nil, "", err
 | |
| 	}
 | |
| 
 | |
| 	return pk, buf.String(), nil
 | |
| }
 | |
| 
 | |
| // serialNumber generates a new random serial number.
 | |
| func serialNumber() (*big.Int, error) {
 | |
| 	return rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
 | |
| }
 |