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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package cert
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"math/big"
"time"
)
var KeyBits = 1024
// ParseCert parses the given PEM-formatted X509 certificate.
func ParseCert(certPEM []byte) (*x509.Certificate, error) {
for len(certPEM) > 0 {
var certBlock *pem.Block
certBlock, certPEM = pem.Decode(certPEM)
if certBlock == nil {
break
}
if certBlock.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(certBlock.Bytes)
return cert, err
}
}
return nil, errors.New("no certificates found")
}
// ParseCert parses the given PEM-formatted X509 certificate
// and RSA private key.
func ParseCertAndKey(certPEM, keyPEM []byte) (*x509.Certificate, *rsa.PrivateKey, error) {
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return nil, nil, err
}
cert, err := x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
return nil, nil, err
}
key, ok := tlsCert.PrivateKey.(*rsa.PrivateKey)
if !ok {
return nil, nil, fmt.Errorf("private key with unexpected type %T", key)
}
return cert, key, nil
}
// Verify verifies that the given server certificate is valid with
// respect to the given CA certificate at the given time.
func Verify(srvCertPEM, caCertPEM []byte, when time.Time) error {
caCert, err := ParseCert(caCertPEM)
if err != nil {
return fmt.Errorf("cannot parse CA certificate: %v", err)
}
srvCert, err := ParseCert(srvCertPEM)
if err != nil {
return fmt.Errorf("cannot parse server certificate: %v", err)
}
pool := x509.NewCertPool()
pool.AddCert(caCert)
opts := x509.VerifyOptions{
DNSName: "anyServer",
Roots: pool,
CurrentTime: when,
}
_, err = srvCert.Verify(opts)
return err
}
// NewCA generates a CA certificate/key pair suitable for signing server
// keys for an environment with the given name.
func NewCA(envName string, expiry time.Time) (certPEM, keyPEM []byte, err error) {
key, err := rsa.GenerateKey(rand.Reader, KeyBits)
if err != nil {
return nil, nil, err
}
now := time.Now()
template := &x509.Certificate{
SerialNumber: new(big.Int),
Subject: pkix.Name{
// TODO quote the environment name when we start using
// Go version 1.1. See Go issue 3791.
CommonName: fmt.Sprintf("juju-generated CA for environment %s", envName),
Organization: []string{"juju"},
},
NotBefore: now.UTC().Add(-5 * time.Minute),
NotAfter: expiry.UTC(),
SubjectKeyId: bigIntHash(key.N),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
IsCA: true,
MaxPathLen: 0, // Disallow delegation for now.
BasicConstraintsValid: true,
}
certDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if err != nil {
return nil, nil, fmt.Errorf("canot create certificate: %v", err)
}
certPEM = pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certDER,
})
keyPEM = pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
})
return certPEM, keyPEM, nil
}
// NewServer generates a certificate/key pair suitable for use by a
// server for an environment with the given name.
func NewServer(envName string, caCertPEM, caKeyPEM []byte, expiry time.Time) (certPEM, keyPEM []byte, err error) {
tlsCert, err := tls.X509KeyPair(caCertPEM, caKeyPEM)
if err != nil {
return nil, nil, err
}
if len(tlsCert.Certificate) != 1 {
return nil, nil, fmt.Errorf("more than one certificate for CA")
}
caCert, err := x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
return nil, nil, err
}
if !caCert.BasicConstraintsValid || !caCert.IsCA {
return nil, nil, fmt.Errorf("CA certificate is not a valid CA")
}
caKey, ok := tlsCert.PrivateKey.(*rsa.PrivateKey)
if !ok {
return nil, nil, fmt.Errorf("CA private key has unexpected type %T", tlsCert.PrivateKey)
}
key, err := rsa.GenerateKey(rand.Reader, KeyBits)
if err != nil {
return nil, nil, fmt.Errorf("cannot generate key: %v", err)
}
now := time.Now()
template := &x509.Certificate{
SerialNumber: new(big.Int),
Subject: pkix.Name{
// This won't match host names with dots. The hostname
// is hardcoded when connecting to avoid the issue.
CommonName: "*",
Organization: []string{"juju"},
},
NotBefore: now.UTC().Add(-5 * time.Minute),
NotAfter: expiry.UTC(),
SubjectKeyId: bigIntHash(key.N),
KeyUsage: x509.KeyUsageDataEncipherment,
}
certDER, err := x509.CreateCertificate(rand.Reader, template, caCert, &key.PublicKey, caKey)
if err != nil {
return nil, nil, err
}
certPEM = pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certDER,
})
keyPEM = pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
})
return certPEM, keyPEM, nil
}
func bigIntHash(n *big.Int) []byte {
h := sha1.New()
h.Write(n.Bytes())
return h.Sum(nil)
}
|