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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
// Package certificate provides functions for creating and storing TLS certificates.
package certificate
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"os"
"time"
)
// CreateOptions configures the creation of a TLS certificate.
type CreateOptions struct {
// Subject Alternate Name values.
// Should contain the DNS names that this certificate is valid for.
// E.g. example.com, *.example.com
DNSNames []string
// Subject Alternate Name values.
// Should contain the IP addresses that the certificate is valid for.
IPAddresses []net.IP
// Subject specifies the certificate Subject.
//
// Subject.CommonName can contain the DNS name that this certificate
// is valid for. Server certificates should specify both a Subject
// and a Subject Alternate Name.
Subject pkix.Name
// Duration specifies the amount of time that the certificate is valid for.
Duration time.Duration
// Ed25519 specifies whether to generate an Ed25519 key pair.
// If false, an ECDSA key will be generated instead.
// Ed25519 is not as widely supported as ECDSA.
Ed25519 bool
}
// Create creates a new TLS certificate.
func Create(options CreateOptions) (tls.Certificate, error) {
crt, priv, err := newX509KeyPair(options)
if err != nil {
return tls.Certificate{}, err
}
var cert tls.Certificate
cert.Leaf = crt
cert.Certificate = append(cert.Certificate, crt.Raw)
cert.PrivateKey = priv
return cert, nil
}
// newX509KeyPair creates and returns a new certificate and private key.
func newX509KeyPair(options CreateOptions) (*x509.Certificate, crypto.PrivateKey, error) {
var pub crypto.PublicKey
var priv crypto.PrivateKey
if options.Ed25519 {
// Generate an Ed25519 private key
var err error
pub, priv, err = ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
} else {
// Generate an ECDSA private key
private, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}
priv = private
pub = &private.PublicKey
}
// ECDSA and Ed25519 keys should have the DigitalSignature KeyUsage bits
// set in the x509.Certificate template
keyUsage := x509.KeyUsageDigitalSignature
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, nil, err
}
notBefore := time.Now()
notAfter := notBefore.Add(options.Duration)
template := x509.Certificate{
SerialNumber: serialNumber,
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: keyUsage,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IPAddresses: options.IPAddresses,
DNSNames: options.DNSNames,
Subject: options.Subject,
}
crt, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, priv)
if err != nil {
return nil, nil, err
}
cert, err := x509.ParseCertificate(crt)
if err != nil {
return nil, nil, err
}
return cert, priv, nil
}
// Write writes the provided certificate and its private key
// to certPath and keyPath respectively.
func Write(cert tls.Certificate, certPath, keyPath string) error {
certOut, err := os.OpenFile(certPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer certOut.Close()
if err := pem.Encode(certOut, &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Leaf.Raw,
}); err != nil {
return err
}
keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer keyOut.Close()
privBytes, err := x509.MarshalPKCS8PrivateKey(cert.PrivateKey)
if err != nil {
return err
}
return pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes})
}
|