-
Notifications
You must be signed in to change notification settings - Fork 0
/
tls.go
122 lines (108 loc) · 3.47 KB
/
tls.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
// Copyright (c) 2015-2019 The Hdfchain developers
// See LICENSE for details.
package main
import (
"crypto/elliptic"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/decred/dcrd/certgen"
credentials "google.golang.org/grpc/credentials"
)
// openRPCKeyPair creates or loads the RPC TLS keypair specified by the
// application config.
func openRPCKeyPair(cfg *config) (credentials.TransportCredentials, error) {
// Generate a new keypair when the key is missing.
_, e := os.Stat(cfg.KeyPath)
keyExists := !os.IsNotExist(e)
if !keyExists {
cert, err := generateRPCKeyPair(cfg.CertificatePath, cfg.KeyPath, cfg.AltDNSNames, certWriter{})
if err != nil {
return nil, fmt.Errorf("Unable to generate TLS Certificate: %v", err)
}
// The certificate generated by generateRPCKeyPair has a nil Leaf. The x509
// certificate must be parsed from the raw bytes to access DNSNames and
// IPAddresses.
if len(cert.Certificate) == 0 {
return nil, fmt.Errorf("No raw certificate data found")
}
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, fmt.Errorf("Could not parse x509 certificate: %v", err)
}
hosts := x509Cert.DNSNames
for _, ip := range x509Cert.IPAddresses {
hosts = append(hosts, ip.String())
}
if len(hosts) > 0 {
log.Infof("TLS certificate created for hosts %s", strings.Join(hosts, ", "))
}
return credentials.NewServerTLSFromCert(&cert), nil
}
cert, err := tls.LoadX509KeyPair(cfg.CertificatePath, cfg.KeyPath)
if err != nil {
return nil, err
}
return credentials.NewServerTLSFromCert(&cert), nil
}
// CertWriter is an interface that wraps either ioutil.WriteFile or a stub for
// testing.
type CertWriter interface {
WriteCertificate(string, []byte) error
}
type certWriter struct{}
func (w certWriter) WriteCertificate(certPath string, cert []byte) error {
return ioutil.WriteFile(certPath, cert, 0600)
}
// generateRPCKeyPair generates a new RPC TLS keypair and writes the cert and
// possibly also the key in PEM format to the paths specified by the config. If
// successful, the new keypair is returned.
// Respectfully pilfered from
// https://github.com/decred/dcrwallet/blob/master/rpcserver.go
func generateRPCKeyPair(certPath, keyPath string, altDnsNames []string, writer CertWriter) (tls.Certificate, error) {
log.Info("Generating TLS certificates...")
// Create directories for cert and key files if they do not yet exist.
certDir, _ := filepath.Split(certPath)
keyDir, _ := filepath.Split(keyPath)
err := os.MkdirAll(certDir, 0700)
if err != nil {
return tls.Certificate{}, err
}
err = os.MkdirAll(keyDir, 0700)
if err != nil {
return tls.Certificate{}, err
}
// Generate cert pair.
org := "hdfdata autogenerated cert"
validUntil := time.Now().Add(time.Hour * 24 * 365 * 10)
cert, key, err := certgen.NewTLSCertPair(elliptic.P521(), org,
validUntil, altDnsNames)
if err != nil {
return tls.Certificate{}, err
}
keyPair, err := tls.X509KeyPair(cert, key)
if err != nil {
return tls.Certificate{}, err
}
// Write cert and (potentially) the key files.
err = writer.WriteCertificate(certPath, cert)
if err != nil {
return tls.Certificate{}, err
}
err = writer.WriteCertificate(keyPath, key)
if err != nil {
rmErr := os.Remove(certPath)
if rmErr != nil {
log.Warnf("Cannot remove written certificates: %v",
rmErr)
}
return tls.Certificate{}, err
}
log.Info("Done generating TLS certificates")
return keyPair, nil
}