Skip to content

Commit fbc8ad9

Browse files
Fix #390: Use code to generate certificates for client & server (#400)
1 parent 556db3e commit fbc8ad9

File tree

10 files changed

+307
-148
lines changed

10 files changed

+307
-148
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
name: Test
1111
strategy:
1212
matrix:
13-
go-version: [1.13.x, 1.14.x, 1.15.x]
13+
go-version: [1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x]
1414
platform: [ubuntu-latest, macos-latest, windows-latest]
1515
runs-on: ${{ matrix.platform }}
1616
steps:

test/e2e/cert_utils_test.go

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
package e2e_test
2+
3+
import (
4+
"bytes"
5+
"crypto/ecdsa"
6+
"crypto/ed25519"
7+
"crypto/elliptic"
8+
"crypto/rand"
9+
"crypto/rsa"
10+
"crypto/x509"
11+
"crypto/x509/pkix"
12+
"encoding/pem"
13+
"fmt"
14+
"io/ioutil"
15+
"math/big"
16+
"net"
17+
"os"
18+
"path"
19+
"time"
20+
21+
chclient "github.com/jpillora/chisel/client"
22+
chserver "github.com/jpillora/chisel/server"
23+
)
24+
25+
type tlsConfig struct {
26+
serverTLS *chserver.TLSConfig
27+
clientTLS *chclient.TLSConfig
28+
tmpDir string
29+
}
30+
31+
func (t *tlsConfig) Close() {
32+
if t.tmpDir != "" {
33+
os.RemoveAll(t.tmpDir)
34+
}
35+
}
36+
37+
func newTestTLSConfig() (*tlsConfig, error) {
38+
tlsConfig := &tlsConfig{}
39+
_, serverCertPEM, serverKeyPEM, err := certGetCertificate(&certConfig{
40+
hosts: []string{
41+
"0.0.0.0",
42+
"localhost",
43+
},
44+
extKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
45+
})
46+
if err != nil {
47+
return nil, err
48+
}
49+
_, clientCertPEM, clientKeyPEM, err := certGetCertificate(&certConfig{
50+
extKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
51+
})
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
tlsConfig.tmpDir, err = ioutil.TempDir("", "")
57+
if err != nil {
58+
return nil, err
59+
}
60+
61+
dirServerCA := path.Join(tlsConfig.tmpDir, "server-ca")
62+
if err := os.Mkdir(dirServerCA, 0777); err != nil {
63+
return nil, err
64+
}
65+
pathServerCACrt := path.Join(dirServerCA, "client.crt")
66+
if err := ioutil.WriteFile(pathServerCACrt, clientCertPEM, 0666); err != nil {
67+
return nil, err
68+
}
69+
70+
dirClientCA := path.Join(tlsConfig.tmpDir, "client-ca")
71+
if err := os.Mkdir(dirClientCA, 0777); err != nil {
72+
return nil, err
73+
}
74+
pathClientCACrt := path.Join(dirClientCA, "server.crt")
75+
if err := ioutil.WriteFile(pathClientCACrt, serverCertPEM, 0666); err != nil {
76+
return nil, err
77+
}
78+
79+
dirServerCrt := path.Join(tlsConfig.tmpDir, "server-crt")
80+
if err := os.Mkdir(dirServerCrt, 0777); err != nil {
81+
return nil, err
82+
}
83+
pathServerCrtCrt := path.Join(dirServerCrt, "server.crt")
84+
if err := ioutil.WriteFile(pathServerCrtCrt, serverCertPEM, 0666); err != nil {
85+
return nil, err
86+
}
87+
pathServerCrtKey := path.Join(dirServerCrt, "server.key")
88+
if err := ioutil.WriteFile(pathServerCrtKey, serverKeyPEM, 0666); err != nil {
89+
return nil, err
90+
}
91+
92+
dirClientCrt := path.Join(tlsConfig.tmpDir, "client-crt")
93+
if err := os.Mkdir(dirClientCrt, 0777); err != nil {
94+
return nil, err
95+
}
96+
pathClientCrtCrt := path.Join(dirClientCrt, "client.crt")
97+
if err := ioutil.WriteFile(pathClientCrtCrt, clientCertPEM, 0666); err != nil {
98+
return nil, err
99+
}
100+
pathClientCrtKey := path.Join(dirClientCrt, "client.key")
101+
if err := ioutil.WriteFile(pathClientCrtKey, clientKeyPEM, 0666); err != nil {
102+
return nil, err
103+
}
104+
105+
// for self signed cert, it needs the server cert, for real cert, this need to be the trusted CA cert
106+
tlsConfig.serverTLS = &chserver.TLSConfig{
107+
CA: pathServerCACrt,
108+
Cert: pathServerCrtCrt,
109+
Key: pathServerCrtKey,
110+
}
111+
tlsConfig.clientTLS = &chclient.TLSConfig{
112+
CA: pathClientCACrt,
113+
Cert: pathClientCrtCrt,
114+
Key: pathClientCrtKey,
115+
}
116+
return tlsConfig, nil
117+
}
118+
119+
type certConfig struct {
120+
signCA *x509.Certificate
121+
isCA bool
122+
hosts []string
123+
validFrom *time.Time
124+
validFor *time.Time
125+
extKeyUsage []x509.ExtKeyUsage
126+
rsaBits int
127+
ecdsaCurve string
128+
ed25519Key bool
129+
}
130+
131+
func certGetCertificate(c *certConfig) (*x509.Certificate, []byte, []byte, error) {
132+
var err error
133+
var priv interface{}
134+
switch c.ecdsaCurve {
135+
case "":
136+
if c.ed25519Key {
137+
_, priv, err = ed25519.GenerateKey(rand.Reader)
138+
} else {
139+
rsaBits := c.rsaBits
140+
if rsaBits == 0 {
141+
rsaBits = 2048
142+
}
143+
priv, err = rsa.GenerateKey(rand.Reader, rsaBits)
144+
}
145+
case "P224":
146+
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
147+
case "P256":
148+
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
149+
case "P384":
150+
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
151+
case "P521":
152+
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
153+
default:
154+
return nil, nil, nil, fmt.Errorf("Unrecognized elliptic curve: %q", c.ecdsaCurve)
155+
}
156+
if err != nil {
157+
return nil, nil, nil, fmt.Errorf("Failed to generate private key: %v", err)
158+
}
159+
160+
// ECDSA, ED25519 and RSA subject keys should have the DigitalSignature
161+
// KeyUsage bits set in the x509.Certificate template
162+
keyUsage := x509.KeyUsageDigitalSignature
163+
// Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In
164+
// the context of TLS this KeyUsage is particular to RSA key exchange and
165+
// authentication.
166+
if _, isRSA := priv.(*rsa.PrivateKey); isRSA {
167+
keyUsage |= x509.KeyUsageKeyEncipherment
168+
}
169+
170+
notBefore := time.Now()
171+
if c.validFrom != nil {
172+
notBefore = *c.validFrom
173+
}
174+
175+
notAfter := time.Now().Add(24 * time.Hour)
176+
if c.validFor != nil {
177+
notAfter = *c.validFor
178+
}
179+
180+
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
181+
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
182+
if err != nil {
183+
return nil, nil, nil, fmt.Errorf("Failed to generate serial number: %v", err)
184+
}
185+
186+
cert := &x509.Certificate{
187+
SerialNumber: serialNumber,
188+
Subject: pkix.Name{
189+
OrganizationalUnit: []string{"test"},
190+
Organization: []string{"Chisel"},
191+
Country: []string{"us"},
192+
Province: []string{"ma"},
193+
Locality: []string{"Boston"},
194+
CommonName: "localhost",
195+
},
196+
NotBefore: notBefore,
197+
NotAfter: notAfter,
198+
199+
KeyUsage: keyUsage,
200+
ExtKeyUsage: c.extKeyUsage,
201+
BasicConstraintsValid: true,
202+
}
203+
204+
for _, h := range c.hosts {
205+
if ip := net.ParseIP(h); ip != nil {
206+
cert.IPAddresses = append(cert.IPAddresses, ip)
207+
} else {
208+
cert.DNSNames = append(cert.DNSNames, h)
209+
}
210+
}
211+
212+
if c.isCA {
213+
cert.IsCA = true
214+
cert.KeyUsage |= x509.KeyUsageCertSign
215+
}
216+
217+
ca := cert
218+
if c.signCA != nil {
219+
ca = c.signCA
220+
}
221+
222+
certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, certGetPublicKey(priv), priv)
223+
if err != nil {
224+
return nil, nil, nil, fmt.Errorf("Failed to create certificate: %v", err)
225+
}
226+
227+
certPEM := new(bytes.Buffer)
228+
pem.Encode(certPEM, &pem.Block{
229+
Type: "CERTIFICATE",
230+
Bytes: certBytes,
231+
})
232+
233+
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
234+
if err != nil {
235+
return nil, nil, nil, fmt.Errorf("Unable to marshal private key: %v", err)
236+
}
237+
certPrivKeyPEM := new(bytes.Buffer)
238+
pem.Encode(certPrivKeyPEM, &pem.Block{
239+
Type: "PRIVATE KEY",
240+
Bytes: privBytes,
241+
})
242+
243+
return cert, certPEM.Bytes(), certPrivKeyPEM.Bytes(), nil
244+
}
245+
246+
func certGetPublicKey(priv interface{}) interface{} {
247+
switch k := priv.(type) {
248+
case *rsa.PrivateKey:
249+
return &k.PublicKey
250+
case *ecdsa.PrivateKey:
251+
return &k.PublicKey
252+
case ed25519.PrivateKey:
253+
return k.Public().(ed25519.PublicKey)
254+
default:
255+
return nil
256+
}
257+
}

test/e2e/setup_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616

1717
const debug = true
1818

19-
//test layout configuration
19+
// test layout configuration
2020
type testLayout struct {
2121
server *chserver.Config
2222
client *chclient.Config

test/e2e/tls/client-ca/server.crt

Lines changed: 0 additions & 16 deletions
This file was deleted.

test/e2e/tls/client-crt/client.crt

Lines changed: 0 additions & 16 deletions
This file was deleted.

test/e2e/tls/client-crt/client.key

Lines changed: 0 additions & 15 deletions
This file was deleted.

test/e2e/tls/server-ca/client.crt

Lines changed: 0 additions & 16 deletions
This file was deleted.

test/e2e/tls/server-crt/server.crt

Lines changed: 0 additions & 16 deletions
This file was deleted.

test/e2e/tls/server-crt/server.key

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)