github.com/lmb/consul@v1.4.1/command/tls/generate.go (about)

     1  package tls
     2  
     3  import (
     4  	"bytes"
     5  	"crypto"
     6  	"crypto/ecdsa"
     7  	"crypto/elliptic"
     8  	"crypto/rand"
     9  	"crypto/sha256"
    10  	"crypto/x509"
    11  	"crypto/x509/pkix"
    12  	"encoding/pem"
    13  	"fmt"
    14  	"math/big"
    15  	"net"
    16  	"strings"
    17  	"time"
    18  )
    19  
    20  // GenerateSerialNumber returns random bigint generated with crypto/rand
    21  func GenerateSerialNumber() (*big.Int, error) {
    22  	l := new(big.Int).Lsh(big.NewInt(1), 128)
    23  	s, err := rand.Int(rand.Reader, l)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	return s, nil
    28  }
    29  
    30  // GeneratePrivateKey generates a new ecdsa private key
    31  func GeneratePrivateKey() (crypto.Signer, string, error) {
    32  	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    33  	if err != nil {
    34  		return nil, "", fmt.Errorf("error generating private key: %s", err)
    35  	}
    36  
    37  	bs, err := x509.MarshalECPrivateKey(pk)
    38  	if err != nil {
    39  		return nil, "", fmt.Errorf("error generating private key: %s", err)
    40  	}
    41  
    42  	var buf bytes.Buffer
    43  	err = pem.Encode(&buf, &pem.Block{Type: "EC PRIVATE KEY", Bytes: bs})
    44  	if err != nil {
    45  		return nil, "", fmt.Errorf("error encoding private key: %s", err)
    46  	}
    47  
    48  	return pk, buf.String(), nil
    49  }
    50  
    51  // GenerateCA generates a new CA for agent TLS (not to be confused with Connect TLS)
    52  func GenerateCA(signer crypto.Signer, sn *big.Int, days int, constraints []string) (string, error) {
    53  	id, err := keyID(signer.Public())
    54  	if err != nil {
    55  		return "", err
    56  	}
    57  
    58  	name := fmt.Sprintf("Consul Agent CA %d", sn)
    59  
    60  	// Create the CA cert
    61  	template := x509.Certificate{
    62  		SerialNumber: sn,
    63  		Subject: pkix.Name{
    64  			Country:       []string{"US"},
    65  			PostalCode:    []string{"94105"},
    66  			Province:      []string{"CA"},
    67  			Locality:      []string{"San Francisco"},
    68  			StreetAddress: []string{"101 Second Street"},
    69  			Organization:  []string{"HashiCorp Inc."},
    70  			CommonName:    name,
    71  		},
    72  		BasicConstraintsValid: true,
    73  		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign | x509.KeyUsageDigitalSignature,
    74  		IsCA:                  true,
    75  		NotAfter:              time.Now().AddDate(0, 0, days),
    76  		NotBefore:             time.Now(),
    77  		AuthorityKeyId:        id,
    78  		SubjectKeyId:          id,
    79  	}
    80  
    81  	if len(constraints) > 0 {
    82  		template.PermittedDNSDomainsCritical = true
    83  		template.PermittedDNSDomains = constraints
    84  	}
    85  	bs, err := x509.CreateCertificate(
    86  		rand.Reader, &template, &template, signer.Public(), signer)
    87  	if err != nil {
    88  		return "", fmt.Errorf("error generating CA certificate: %s", err)
    89  	}
    90  
    91  	var buf bytes.Buffer
    92  	err = pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: bs})
    93  	if err != nil {
    94  		return "", fmt.Errorf("error encoding private key: %s", err)
    95  	}
    96  
    97  	return buf.String(), nil
    98  }
    99  
   100  // GenerateCert generates a new certificate for agent TLS (not to be confused with Connect TLS)
   101  func GenerateCert(signer crypto.Signer, ca string, sn *big.Int, name string, days int, DNSNames []string, IPAddresses []net.IP, extKeyUsage []x509.ExtKeyUsage) (string, string, error) {
   102  	parent, err := parseCert(ca)
   103  	if err != nil {
   104  		return "", "", err
   105  	}
   106  
   107  	signee, pk, err := GeneratePrivateKey()
   108  	if err != nil {
   109  		return "", "", err
   110  	}
   111  
   112  	id, err := keyID(signee.Public())
   113  	if err != nil {
   114  		return "", "", err
   115  	}
   116  
   117  	template := x509.Certificate{
   118  		SerialNumber:          sn,
   119  		Subject:               pkix.Name{CommonName: name},
   120  		BasicConstraintsValid: true,
   121  		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   122  		ExtKeyUsage:           extKeyUsage,
   123  		IsCA:                  false,
   124  		NotAfter:              time.Now().AddDate(0, 0, days),
   125  		NotBefore:             time.Now(),
   126  		SubjectKeyId:          id,
   127  		DNSNames:              DNSNames,
   128  		IPAddresses:           IPAddresses,
   129  	}
   130  
   131  	bs, err := x509.CreateCertificate(rand.Reader, &template, parent, signee.Public(), signer)
   132  	if err != nil {
   133  		return "", "", err
   134  	}
   135  
   136  	var buf bytes.Buffer
   137  	err = pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: bs})
   138  	if err != nil {
   139  		return "", "", fmt.Errorf("error encoding private key: %s", err)
   140  	}
   141  
   142  	return buf.String(), pk, nil
   143  }
   144  
   145  // KeyId returns a x509 KeyId from the given signing key.
   146  func keyID(raw interface{}) ([]byte, error) {
   147  	switch raw.(type) {
   148  	case *ecdsa.PublicKey:
   149  	default:
   150  		return nil, fmt.Errorf("invalid key type: %T", raw)
   151  	}
   152  
   153  	// This is not standard; RFC allows any unique identifier as long as they
   154  	// match in subject/authority chains but suggests specific hashing of DER
   155  	// bytes of public key including DER tags.
   156  	bs, err := x509.MarshalPKIXPublicKey(raw)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	// String formatted
   162  	kID := sha256.Sum256(bs)
   163  	return []byte(strings.Replace(fmt.Sprintf("% x", kID), " ", ":", -1)), nil
   164  }
   165  
   166  func parseCert(pemValue string) (*x509.Certificate, error) {
   167  	// The _ result below is not an error but the remaining PEM bytes.
   168  	block, _ := pem.Decode([]byte(pemValue))
   169  	if block == nil {
   170  		return nil, fmt.Errorf("no PEM-encoded data found")
   171  	}
   172  
   173  	if block.Type != "CERTIFICATE" {
   174  		return nil, fmt.Errorf("first PEM-block should be CERTIFICATE type")
   175  	}
   176  
   177  	return x509.ParseCertificate(block.Bytes)
   178  }
   179  
   180  // ParseSigner parses a crypto.Signer from a PEM-encoded key. The private key
   181  // is expected to be the first block in the PEM value.
   182  func ParseSigner(pemValue string) (crypto.Signer, error) {
   183  	// The _ result below is not an error but the remaining PEM bytes.
   184  	block, _ := pem.Decode([]byte(pemValue))
   185  	if block == nil {
   186  		return nil, fmt.Errorf("no PEM-encoded data found")
   187  	}
   188  
   189  	switch block.Type {
   190  	case "EC PRIVATE KEY":
   191  		return x509.ParseECPrivateKey(block.Bytes)
   192  	default:
   193  		return nil, fmt.Errorf("unknown PEM block type for signing key: %s", block.Type)
   194  	}
   195  }
   196  
   197  func Verify(caString, certString, dns string) error {
   198  	roots := x509.NewCertPool()
   199  	ok := roots.AppendCertsFromPEM([]byte(caString))
   200  	if !ok {
   201  		return fmt.Errorf("failed to parse root certificate")
   202  	}
   203  
   204  	cert, err := parseCert(certString)
   205  	if err != nil {
   206  		return fmt.Errorf("failed to parse certificate")
   207  	}
   208  
   209  	opts := x509.VerifyOptions{
   210  		DNSName: fmt.Sprintf(dns),
   211  		Roots:   roots,
   212  	}
   213  
   214  	_, err = cert.Verify(opts)
   215  	return err
   216  }