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