github.com/pachyderm/pachyderm@v1.13.4/src/server/pkg/cert/cert.go (about)

     1  // cert is a library for generating x509 certificates. Largely cribbed from the
     2  // go binary src/crypto/tls/generate_cert.go (with some simplifications for its
     3  // application in Pachyderm, and adapted to be a library)
     4  
     5  package cert
     6  
     7  import (
     8  	"crypto/rand"
     9  	"crypto/rsa"
    10  	"crypto/tls"
    11  	"crypto/x509"
    12  	"crypto/x509/pkix"
    13  	"encoding/pem"
    14  	"math/big"
    15  	"net"
    16  	"sync/atomic"
    17  	"time"
    18  
    19  	"github.com/pachyderm/pachyderm/src/client/pkg/errors"
    20  )
    21  
    22  const rsaKeySize = 2048               // Recommended by SO (below) and generate_cert.go
    23  const validDur = 365 * 24 * time.Hour // 1 year
    24  
    25  var serialNumber int64
    26  
    27  // PublicCertToPEM serializes the public x509 cert in 'cert' to a PEM-formatted
    28  // block
    29  func PublicCertToPEM(cert *tls.Certificate) []byte {
    30  	return pem.EncodeToMemory(&pem.Block{
    31  		Type:  "CERTIFICATE",
    32  		Bytes: cert.Certificate[0],
    33  	})
    34  }
    35  
    36  // KeyToPEM serializes the private key in 'cert' to a PEM-formatted block if
    37  // it's an RSA key, or nil otherwise (all certs returned by
    38  // GenerateSelfSignedCert use RSA keys)
    39  func KeyToPEM(cert *tls.Certificate) []byte {
    40  	switch k := cert.PrivateKey.(type) {
    41  	case *rsa.PrivateKey:
    42  		return pem.EncodeToMemory(&pem.Block{
    43  			Type:  "RSA PRIVATE KEY",
    44  			Bytes: x509.MarshalPKCS1PrivateKey(k),
    45  		})
    46  	default:
    47  		return nil
    48  	}
    49  }
    50  
    51  // GenerateSelfSignedCert generates a self-signed TLS cert for the domain name
    52  // 'address', with a private key. Other attributes of the subject can be set in
    53  // 'name' and ip addresses can be set in 'ipAddresses'
    54  func GenerateSelfSignedCert(address string, name *pkix.Name, ipAddresses ...string) (*tls.Certificate, error) {
    55  	// Generate Subject Distinguished Name
    56  	if name == nil {
    57  		name = &pkix.Name{}
    58  	}
    59  	switch {
    60  	case address == "" && name.CommonName == "":
    61  		return nil, errors.New("must set either \"address\" or \"name.CommonName\"")
    62  	case address != "" && name.CommonName == "":
    63  		name.CommonName = address
    64  	case address != "" && name.CommonName != "" && name.CommonName != address:
    65  		return nil, errors.Errorf("set address to \"%s\" but name.CommonName to \"%s\"", address, name.CommonName)
    66  	default:
    67  		// name.CommonName is already valid--nothing to do
    68  	}
    69  
    70  	// Parse IPs in ipAddresses
    71  	parsedIPs := []net.IP{}
    72  	for _, strIP := range ipAddresses {
    73  		nextParsedIP := net.ParseIP(strIP)
    74  		if nextParsedIP == nil {
    75  			return nil, errors.Errorf("invalid IP: %s", strIP)
    76  		}
    77  		parsedIPs = append(parsedIPs, nextParsedIP)
    78  	}
    79  	// Generate key pair. According to
    80  	// https://security.stackexchange.com/questions/5096/rsa-vs-dsa-for-ssh-authentication-keys
    81  	// RSA is likely to be faster and more secure in practice than DSA/ECDSA, so
    82  	// this only generates RSA keys
    83  	key, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
    84  	if err != nil {
    85  		return nil, errors.Wrapf(err, "could not generate RSA private key")
    86  	}
    87  
    88  	// Generate unsigned cert
    89  	cert := x509.Certificate{
    90  		// the x509 spec requires every x509 cert must have a serial number that is
    91  		// unique for the signing CA. All of the certs generated by this package
    92  		// are self-signed, so this just starts at 1 and counts up
    93  		SerialNumber: big.NewInt(atomic.AddInt64(&serialNumber, 1)),
    94  		Subject:      *name,
    95  		NotBefore:    time.Now().Add(-1 * time.Second),
    96  		NotAfter:     time.Now().Add(validDur),
    97  
    98  		KeyUsage: x509.KeyUsageCertSign | // can sign certs (need for self-signing)
    99  			x509.KeyUsageKeyEncipherment | // can encrypt other keys (need for TLS in symmetric mode)
   100  			x509.KeyUsageKeyAgreement, // can establish keys (need for TLS in Diffie-Hellman mode)
   101  		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, // can authenticate server (for TLS)
   102  
   103  		IsCA:                  true, // must be set b/c KeyUsageCertSign is set
   104  		BasicConstraintsValid: true, // mark "Basic Constraints" extn critical(?)
   105  		MaxPathLenZero:        true, // must directly sign all end entity certs
   106  		IPAddresses:           parsedIPs,
   107  		DNSNames:              []string{address},
   108  	}
   109  
   110  	// Sign 'cert' (cert is both 'template' and 'parent' b/c it's self-signed)
   111  	signedCertDER, err := x509.CreateCertificate(rand.Reader, &cert, &cert, &key.PublicKey, key)
   112  	if err != nil {
   113  		return nil, errors.Wrapf(err, "could not self-sign certificate")
   114  	}
   115  	signedCert, err := x509.ParseCertificate(signedCertDER)
   116  	if err != nil {
   117  		return nil, errors.Wrapf(err, "could not parse the just-generated signed certificate")
   118  	}
   119  	return &tls.Certificate{
   120  		Certificate: [][]byte{signedCertDER},
   121  		Leaf:        signedCert,
   122  		PrivateKey:  key,
   123  	}, nil
   124  }