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 }