Skip to content

Commit 436a406

Browse files
authored
Merge pull request #152 from arangodb-helper/ssl-auto-keyfile-with-curves
Switch algorithme for auto-generated TLS certificates from RSA to ECDSA
2 parents affb35d + 67c8677 commit 436a406

File tree

3 files changed

+19
-119
lines changed

3 files changed

+19
-119
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changes from version 0.11.1 to master
22

3-
- Changed default ECDSA curve used by `arangodb create tls ...` from `P521` to `p256`.
3+
- Changed TLS algorithm for `-ssl.auto-key` from RSA (2048 bits) to ECDSA (`P256` curve).
4+
- Changed default ECDSA curve used by `arangodb create tls ...` from `P521` to `P256`.
45
- Log text showing the address the starter is listening on has been changed from "Listening on ..." to "ArangoDB Starter listening on ...".
56
- Solved problem where starter did not properly log a resilientsingle server ("Your resilient single server can now be accessed ...") when leadership challenge was still ongoing.
67

main.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,6 @@ func mustPrepareService(generateAutoKeyFile bool) (*service.Service, service.Boo
579579
}
580580
keyFile, err := service.CreateCertificate(service.CreateCertificateOptions{
581581
Hosts: hosts,
582-
RSABits: 2048,
583582
Organization: sslAutoOrganization,
584583
}, dataDir)
585584
if err != nil {

service/certificate.go

Lines changed: 17 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -23,121 +23,48 @@
2323
package service
2424

2525
import (
26-
"crypto"
27-
"crypto/ecdsa"
28-
"crypto/rand"
29-
"crypto/rsa"
3026
"crypto/tls"
31-
"crypto/x509"
3227
"crypto/x509/pkix"
33-
"encoding/pem"
34-
"errors"
35-
"fmt"
3628
"io/ioutil"
37-
"math/big"
38-
"net"
3929
"strings"
4030
"time"
31+
32+
certificates "github.com/arangodb-helper/go-certificates"
4133
)
4234

4335
// CreateCertificateOptions configures how to create a certificate.
4436
type CreateCertificateOptions struct {
4537
Hosts []string // Host names and/or IP addresses
4638
ValidFor time.Duration
47-
RSABits int
4839
Organization string
4940
}
5041

5142
const (
5243
defaultValidFor = time.Hour * 24 * 365 // 1year
44+
defaultCurve = "P256"
5345
)
5446

55-
func publicKey(priv interface{}) interface{} {
56-
switch k := priv.(type) {
57-
case *rsa.PrivateKey:
58-
return &k.PublicKey
59-
default:
60-
return nil
61-
}
62-
}
63-
64-
func pemBlockForKey(priv interface{}) *pem.Block {
65-
switch k := priv.(type) {
66-
case *rsa.PrivateKey:
67-
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
68-
default:
69-
return nil
70-
}
71-
}
72-
73-
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
74-
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
75-
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
76-
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
77-
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
78-
return key, nil
79-
}
80-
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
81-
switch key := key.(type) {
82-
case *rsa.PrivateKey, *ecdsa.PrivateKey:
83-
return key, nil
84-
default:
85-
return nil, maskAny(errors.New("tls: found unknown private key type in PKCS#8 wrapping"))
86-
}
87-
}
88-
if key, err := x509.ParseECPrivateKey(der); err == nil {
89-
return key, nil
90-
}
91-
92-
return nil, maskAny(errors.New("tls: failed to parse private key"))
93-
}
94-
9547
// CreateCertificate creates a self-signed certificate according to the given configuration.
9648
// The resulting certificate + private key will be written into a single file in the given folder.
9749
// The path of that single file is returned.
9850
func CreateCertificate(options CreateCertificateOptions, folder string) (string, error) {
99-
priv, err := rsa.GenerateKey(rand.Reader, options.RSABits)
100-
if err != nil {
101-
return "", maskAny(err)
102-
}
103-
104-
notBefore := time.Now()
10551
if options.ValidFor == 0 {
10652
options.ValidFor = defaultValidFor
10753
}
108-
notAfter := notBefore.Add(options.ValidFor)
109-
110-
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
111-
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
112-
if err != nil {
113-
return "", maskAny(fmt.Errorf("failed to generate serial number: %v", err))
114-
}
115-
116-
template := x509.Certificate{
117-
SerialNumber: serialNumber,
118-
Subject: pkix.Name{
54+
certOpts := certificates.CreateCertificateOptions{
55+
Hosts: options.Hosts,
56+
Subject: &pkix.Name{
11957
Organization: []string{options.Organization},
12058
},
121-
NotBefore: notBefore,
122-
NotAfter: notAfter,
123-
124-
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
125-
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
126-
BasicConstraintsValid: true,
127-
}
128-
129-
for _, h := range options.Hosts {
130-
if ip := net.ParseIP(h); ip != nil {
131-
template.IPAddresses = append(template.IPAddresses, ip)
132-
} else {
133-
template.DNSNames = append(template.DNSNames, h)
134-
}
59+
ValidFrom: time.Now(),
60+
ValidFor: options.ValidFor,
61+
ECDSACurve: defaultCurve,
13562
}
13663

137-
// Create the certificate
138-
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
64+
// Create self-signed certificate
65+
cert, priv, err := certificates.CreateCertificate(certOpts, nil)
13966
if err != nil {
140-
return "", maskAny(fmt.Errorf("Failed to create certificate: %v", err))
67+
return "", maskAny(err)
14168
}
14269

14370
// Write the certificate to disk
@@ -146,46 +73,19 @@ func CreateCertificate(options CreateCertificateOptions, folder string) (string,
14673
return "", maskAny(err)
14774
}
14875
defer f.Close()
149-
// Public key
150-
pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
151-
// Private key
152-
pem.Encode(f, pemBlockForKey(priv))
76+
content := strings.TrimSpace(cert) + "\n" + priv
77+
if _, err := f.WriteString(content); err != nil {
78+
return "", maskAny(err)
79+
}
15380

15481
return f.Name(), nil
15582
}
15683

15784
// LoadKeyFile loads a SSL keyfile formatted for the arangod server.
15885
func LoadKeyFile(keyFile string) (tls.Certificate, error) {
159-
raw, err := ioutil.ReadFile(keyFile)
86+
result, err := certificates.LoadKeyFile(keyFile)
16087
if err != nil {
16188
return tls.Certificate{}, maskAny(err)
16289
}
163-
164-
result := tls.Certificate{}
165-
for {
166-
var derBlock *pem.Block
167-
derBlock, raw = pem.Decode(raw)
168-
if derBlock == nil {
169-
break
170-
}
171-
if derBlock.Type == "CERTIFICATE" {
172-
result.Certificate = append(result.Certificate, derBlock.Bytes)
173-
} else if derBlock.Type == "PRIVATE KEY" || strings.HasSuffix(derBlock.Type, " PRIVATE KEY") {
174-
if result.PrivateKey == nil {
175-
result.PrivateKey, err = parsePrivateKey(derBlock.Bytes)
176-
if err != nil {
177-
return tls.Certificate{}, maskAny(err)
178-
}
179-
}
180-
}
181-
}
182-
183-
if len(result.Certificate) == 0 {
184-
return tls.Certificate{}, maskAny(fmt.Errorf("No certificates found in %s", keyFile))
185-
}
186-
if result.PrivateKey == nil {
187-
return tls.Certificate{}, maskAny(fmt.Errorf("No private key found in %s", keyFile))
188-
}
189-
19090
return result, nil
19191
}

0 commit comments

Comments
 (0)