OLD | NEW |
1 // Copyright 2012, 2013 Canonical Ltd. | 1 // Copyright 2012, 2013 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 package cert | 4 package cert |
5 | 5 |
6 import ( | 6 import ( |
7 "crypto/rand" | 7 "crypto/rand" |
8 "crypto/rsa" | 8 "crypto/rsa" |
9 "crypto/sha1" | 9 "crypto/sha1" |
10 "crypto/tls" | 10 "crypto/tls" |
11 "crypto/x509" | 11 "crypto/x509" |
12 "crypto/x509/pkix" | 12 "crypto/x509/pkix" |
13 "encoding/pem" | 13 "encoding/pem" |
14 "errors" | |
15 "fmt" | 14 "fmt" |
16 "math/big" | 15 "math/big" |
17 "net" | 16 "net" |
18 "time" | 17 "time" |
19 | 18 |
20 » "github.com/errgo/errgo" | 19 » "github.com/juju/errgo/errors" |
21 ) | 20 ) |
22 | 21 |
23 var KeyBits = 1024 | 22 var KeyBits = 1024 |
24 | 23 |
25 // ParseCert parses the given PEM-formatted X509 certificate. | 24 // ParseCert parses the given PEM-formatted X509 certificate. |
26 func ParseCert(certPEM []byte) (*x509.Certificate, error) { | 25 func ParseCert(certPEM []byte) (*x509.Certificate, error) { |
27 for len(certPEM) > 0 { | 26 for len(certPEM) > 0 { |
28 var certBlock *pem.Block | 27 var certBlock *pem.Block |
29 certBlock, certPEM = pem.Decode(certPEM) | 28 certBlock, certPEM = pem.Decode(certPEM) |
30 if certBlock == nil { | 29 if certBlock == nil { |
(...skipping 15 matching lines...) Expand all Loading... |
46 return nil, nil, err | 45 return nil, nil, err |
47 } | 46 } |
48 | 47 |
49 cert, err := x509.ParseCertificate(tlsCert.Certificate[0]) | 48 cert, err := x509.ParseCertificate(tlsCert.Certificate[0]) |
50 if err != nil { | 49 if err != nil { |
51 return nil, nil, err | 50 return nil, nil, err |
52 } | 51 } |
53 | 52 |
54 key, ok := tlsCert.PrivateKey.(*rsa.PrivateKey) | 53 key, ok := tlsCert.PrivateKey.(*rsa.PrivateKey) |
55 if !ok { | 54 if !ok { |
56 » » return nil, nil, fmt.Errorf("private key with unexpected type %T
", key) | 55 » » return nil, nil, errors.Newf("private key with unexpected type %
T", key) |
57 } | 56 } |
58 return cert, key, nil | 57 return cert, key, nil |
59 } | 58 } |
60 | 59 |
61 // Verify verifies that the given server certificate is valid with | 60 // Verify verifies that the given server certificate is valid with |
62 // respect to the given CA certificate at the given time. | 61 // respect to the given CA certificate at the given time. |
63 func Verify(srvCertPEM, caCertPEM []byte, when time.Time) error { | 62 func Verify(srvCertPEM, caCertPEM []byte, when time.Time) error { |
64 caCert, err := ParseCert(caCertPEM) | 63 caCert, err := ParseCert(caCertPEM) |
65 if err != nil { | 64 if err != nil { |
66 » » return errgo.Annotate(err, "cannot parse CA certificate") | 65 » » return errors.NoteMask(err, "cannot parse CA certificate") |
67 } | 66 } |
68 srvCert, err := ParseCert(srvCertPEM) | 67 srvCert, err := ParseCert(srvCertPEM) |
69 if err != nil { | 68 if err != nil { |
70 » » return errgo.Annotate(err, "cannot parse server certificate") | 69 » » return errors.NoteMask(err, "cannot parse server certificate") |
71 } | 70 } |
72 pool := x509.NewCertPool() | 71 pool := x509.NewCertPool() |
73 pool.AddCert(caCert) | 72 pool.AddCert(caCert) |
74 opts := x509.VerifyOptions{ | 73 opts := x509.VerifyOptions{ |
75 DNSName: "anyServer", | 74 DNSName: "anyServer", |
76 Roots: pool, | 75 Roots: pool, |
77 CurrentTime: when, | 76 CurrentTime: when, |
78 } | 77 } |
79 _, err = srvCert.Verify(opts) | 78 _, err = srvCert.Verify(opts) |
80 return err | 79 return err |
(...skipping 16 matching lines...) Expand all Loading... |
97 NotBefore: now.UTC().Add(-5 * time.Minute), | 96 NotBefore: now.UTC().Add(-5 * time.Minute), |
98 NotAfter: expiry.UTC(), | 97 NotAfter: expiry.UTC(), |
99 SubjectKeyId: bigIntHash(key.N), | 98 SubjectKeyId: bigIntHash(key.N), |
100 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUs
ageDigitalSignature | x509.KeyUsageCertSign, | 99 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUs
ageDigitalSignature | x509.KeyUsageCertSign, |
101 IsCA: true, | 100 IsCA: true, |
102 MaxPathLen: 0, // Disallow delegation for now. | 101 MaxPathLen: 0, // Disallow delegation for now. |
103 BasicConstraintsValid: true, | 102 BasicConstraintsValid: true, |
104 } | 103 } |
105 certDER, err := x509.CreateCertificate(rand.Reader, template, template,
&key.PublicKey, key) | 104 certDER, err := x509.CreateCertificate(rand.Reader, template, template,
&key.PublicKey, key) |
106 if err != nil { | 105 if err != nil { |
107 » » return nil, nil, fmt.Errorf("canot create certificate: %v", err) | 106 » » return nil, nil, errors.Notef(err, "canot create certificate") |
108 } | 107 } |
109 certPEM = pem.EncodeToMemory(&pem.Block{ | 108 certPEM = pem.EncodeToMemory(&pem.Block{ |
110 Type: "CERTIFICATE", | 109 Type: "CERTIFICATE", |
111 Bytes: certDER, | 110 Bytes: certDER, |
112 }) | 111 }) |
113 keyPEM = pem.EncodeToMemory(&pem.Block{ | 112 keyPEM = pem.EncodeToMemory(&pem.Block{ |
114 Type: "RSA PRIVATE KEY", | 113 Type: "RSA PRIVATE KEY", |
115 Bytes: x509.MarshalPKCS1PrivateKey(key), | 114 Bytes: x509.MarshalPKCS1PrivateKey(key), |
116 }) | 115 }) |
117 return certPEM, keyPEM, nil | 116 return certPEM, keyPEM, nil |
118 } | 117 } |
119 | 118 |
120 // NewServer generates a certificate/key pair suitable for use by a server. | 119 // NewServer generates a certificate/key pair suitable for use by a server. |
121 func NewServer(caCertPEM, caKeyPEM []byte, expiry time.Time, hostnames []string)
(certPEM, keyPEM []byte, err error) { | 120 func NewServer(caCertPEM, caKeyPEM []byte, expiry time.Time, hostnames []string)
(certPEM, keyPEM []byte, err error) { |
122 return newLeaf(caCertPEM, caKeyPEM, expiry, hostnames, []x509.ExtKeyUsag
e{x509.ExtKeyUsageServerAuth}) | 121 return newLeaf(caCertPEM, caKeyPEM, expiry, hostnames, []x509.ExtKeyUsag
e{x509.ExtKeyUsageServerAuth}) |
123 } | 122 } |
124 | 123 |
125 // NewClient generates a certificate/key pair suitable for client authentication
. | 124 // NewClient generates a certificate/key pair suitable for client authentication
. |
126 func NewClient(caCertPEM, caKeyPEM []byte, expiry time.Time) (certPEM, keyPEM []
byte, err error) { | 125 func NewClient(caCertPEM, caKeyPEM []byte, expiry time.Time) (certPEM, keyPEM []
byte, err error) { |
127 return newLeaf(caCertPEM, caKeyPEM, expiry, nil, []x509.ExtKeyUsage{x509
.ExtKeyUsageClientAuth}) | 126 return newLeaf(caCertPEM, caKeyPEM, expiry, nil, []x509.ExtKeyUsage{x509
.ExtKeyUsageClientAuth}) |
128 } | 127 } |
129 | 128 |
130 // newLeaf generates a certificate/key pair suitable for use by a leaf node. | 129 // newLeaf generates a certificate/key pair suitable for use by a leaf node. |
131 func newLeaf(caCertPEM, caKeyPEM []byte, expiry time.Time, hostnames []string, e
xtKeyUsage []x509.ExtKeyUsage) (certPEM, keyPEM []byte, err error) { | 130 func newLeaf(caCertPEM, caKeyPEM []byte, expiry time.Time, hostnames []string, e
xtKeyUsage []x509.ExtKeyUsage) (certPEM, keyPEM []byte, err error) { |
132 tlsCert, err := tls.X509KeyPair(caCertPEM, caKeyPEM) | 131 tlsCert, err := tls.X509KeyPair(caCertPEM, caKeyPEM) |
133 if err != nil { | 132 if err != nil { |
134 return nil, nil, err | 133 return nil, nil, err |
135 } | 134 } |
136 if len(tlsCert.Certificate) != 1 { | 135 if len(tlsCert.Certificate) != 1 { |
137 » » return nil, nil, fmt.Errorf("more than one certificate for CA") | 136 » » return nil, nil, errors.Newf("more than one certificate for CA") |
138 } | 137 } |
139 caCert, err := x509.ParseCertificate(tlsCert.Certificate[0]) | 138 caCert, err := x509.ParseCertificate(tlsCert.Certificate[0]) |
140 if err != nil { | 139 if err != nil { |
141 return nil, nil, err | 140 return nil, nil, err |
142 } | 141 } |
143 if !caCert.BasicConstraintsValid || !caCert.IsCA { | 142 if !caCert.BasicConstraintsValid || !caCert.IsCA { |
144 » » return nil, nil, fmt.Errorf("CA certificate is not a valid CA") | 143 » » return nil, nil, errors.Newf("CA certificate is not a valid CA") |
145 } | 144 } |
146 caKey, ok := tlsCert.PrivateKey.(*rsa.PrivateKey) | 145 caKey, ok := tlsCert.PrivateKey.(*rsa.PrivateKey) |
147 if !ok { | 146 if !ok { |
148 » » return nil, nil, fmt.Errorf("CA private key has unexpected type
%T", tlsCert.PrivateKey) | 147 » » return nil, nil, errors.Newf("CA private key has unexpected type
%T", tlsCert.PrivateKey) |
149 } | 148 } |
150 key, err := rsa.GenerateKey(rand.Reader, KeyBits) | 149 key, err := rsa.GenerateKey(rand.Reader, KeyBits) |
151 if err != nil { | 150 if err != nil { |
152 » » return nil, nil, fmt.Errorf("cannot generate key: %v", err) | 151 » » return nil, nil, errors.Notef(err, "cannot generate key") |
153 } | 152 } |
154 now := time.Now() | 153 now := time.Now() |
155 template := &x509.Certificate{ | 154 template := &x509.Certificate{ |
156 SerialNumber: new(big.Int), | 155 SerialNumber: new(big.Int), |
157 Subject: pkix.Name{ | 156 Subject: pkix.Name{ |
158 // This won't match host names with dots. The hostname | 157 // This won't match host names with dots. The hostname |
159 // is hardcoded when connecting to avoid the issue. | 158 // is hardcoded when connecting to avoid the issue. |
160 CommonName: "*", | 159 CommonName: "*", |
161 Organization: []string{"juju"}, | 160 Organization: []string{"juju"}, |
162 }, | 161 }, |
(...skipping 24 matching lines...) Expand all Loading... |
187 Bytes: x509.MarshalPKCS1PrivateKey(key), | 186 Bytes: x509.MarshalPKCS1PrivateKey(key), |
188 }) | 187 }) |
189 return certPEM, keyPEM, nil | 188 return certPEM, keyPEM, nil |
190 } | 189 } |
191 | 190 |
192 func bigIntHash(n *big.Int) []byte { | 191 func bigIntHash(n *big.Int) []byte { |
193 h := sha1.New() | 192 h := sha1.New() |
194 h.Write(n.Bytes()) | 193 h.Write(n.Bytes()) |
195 return h.Sum(nil) | 194 return h.Sum(nil) |
196 } | 195 } |
OLD | NEW |